238,6 → 238,64 |
diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0; |
} |
|
/* |
* Within a drm_vblank_pre_modeset - drm_vblank_post_modeset |
* interval? If so then vblank irqs keep running and it will likely |
* happen that the hardware vblank counter is not trustworthy as it |
* might reset at some point in that interval and vblank timestamps |
* are not trustworthy either in that interval. Iow. this can result |
* in a bogus diff >> 1 which must be avoided as it would cause |
* random large forward jumps of the software vblank counter. |
*/ |
if (diff > 1 && (vblank->inmodeset & 0x2)) { |
DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u" |
" due to pre-modeset.\n", pipe, diff); |
diff = 1; |
} |
|
/* |
* FIMXE: Need to replace this hack with proper seqlocks. |
* |
* Restrict the bump of the software vblank counter to a safe maximum |
* value of +1 whenever there is the possibility that concurrent readers |
* of vblank timestamps could be active at the moment, as the current |
* implementation of the timestamp caching and updating is not safe |
* against concurrent readers for calls to store_vblank() with a bump |
* of anything but +1. A bump != 1 would very likely return corrupted |
* timestamps to userspace, because the same slot in the cache could |
* be concurrently written by store_vblank() and read by one of those |
* readers without the read-retry logic detecting the collision. |
* |
* Concurrent readers can exist when we are called from the |
* drm_vblank_off() or drm_vblank_on() functions and other non-vblank- |
* irq callers. However, all those calls to us are happening with the |
* vbl_lock locked to prevent drm_vblank_get(), so the vblank refcount |
* can't increase while we are executing. Therefore a zero refcount at |
* this point is safe for arbitrary counter bumps if we are called |
* outside vblank irq, a non-zero count is not 100% safe. Unfortunately |
* we must also accept a refcount of 1, as whenever we are called from |
* drm_vblank_get() -> drm_vblank_enable() the refcount will be 1 and |
* we must let that one pass through in order to not lose vblank counts |
* during vblank irq off - which would completely defeat the whole |
* point of this routine. |
* |
* Whenever we are called from vblank irq, we have to assume concurrent |
* readers exist or can show up any time during our execution, even if |
* the refcount is currently zero, as vblank irqs are usually only |
* enabled due to the presence of readers, and because when we are called |
* from vblank irq we can't hold the vbl_lock to protect us from sudden |
* bumps in vblank refcount. Therefore also restrict bumps to +1 when |
* called from vblank irq. |
*/ |
if ((diff > 1) && (atomic_read(&vblank->refcount) > 1 || |
(flags & DRM_CALLED_FROM_VBLIRQ))) { |
DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u " |
"refcount %u, vblirq %u\n", pipe, diff, |
atomic_read(&vblank->refcount), |
(flags & DRM_CALLED_FROM_VBLIRQ) != 0); |
diff = 1; |
} |
|
DRM_DEBUG_VBL("updating vblank count on crtc %u:" |
" current=%u, diff=%u, hw=%u hw_last=%u\n", |
pipe, vblank->count, diff, cur_vblank, vblank->last); |
1178,6 → 1236,8 |
spin_lock_irqsave(&dev->event_lock, irqflags); |
|
spin_lock(&dev->vbl_lock); |
DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", |
pipe, vblank->enabled, vblank->inmodeset); |
vblank_disable_and_save(dev, pipe); |
wake_up(&vblank->queue); |
|
1280,6 → 1340,9 |
return; |
|
spin_lock_irqsave(&dev->vbl_lock, irqflags); |
DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", |
pipe, vblank->enabled, vblank->inmodeset); |
|
/* Drop our private "prevent drm_vblank_get" refcount */ |
if (vblank->inmodeset) { |
atomic_dec(&vblank->refcount); |
1292,8 → 1355,7 |
* re-enable interrupts if there are users left, or the |
* user wishes vblank interrupts to be enabled all the time. |
*/ |
if (atomic_read(&vblank->refcount) != 0 || |
(!dev->vblank_disable_immediate && drm_vblank_offdelay == 0)) |
if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) |
WARN_ON(drm_vblank_enable(dev, pipe)); |
spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
} |
1388,6 → 1450,7 |
if (vblank->inmodeset) { |
spin_lock_irqsave(&dev->vbl_lock, irqflags); |
dev->vblank_disable_allowed = true; |
drm_reset_vblank_timestamp(dev, pipe); |
spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
|
if (vblank->inmodeset & 0x2) |
1507,19 → 1570,3 |
return 0; |
} |
EXPORT_SYMBOL(drm_vblank_no_hw_counter); |
|
u64 div64_u64(u64 dividend, u64 divisor) |
{ |
u32 high, d; |
|
high = divisor >> 32; |
if (high) { |
unsigned int shift = fls(high); |
|
d = divisor >> shift; |
dividend >>= shift; |
} else |
d = divisor; |
|
return div_u64(dividend, d); |
} |