drm: Make the vblank disable timer per-crtc
Currently there's one per-device vblank disable timer, and it gets reset wheneven the vblank refcount for any crtc drops to zero. That means that one crtc could accidentally be keeping the vblank interrupts for other crtcs enabled even if there are no users for them. Make the disable timer per-crtc to avoid this issue. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
parent
97cbc883d0
commit
e69595c250
|
@ -140,33 +140,34 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
|
||||||
|
|
||||||
static void vblank_disable_fn(unsigned long arg)
|
static void vblank_disable_fn(unsigned long arg)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = (struct drm_device *)arg;
|
struct drm_vblank_crtc *vblank = (void *)arg;
|
||||||
|
struct drm_device *dev = vblank->dev;
|
||||||
unsigned long irqflags;
|
unsigned long irqflags;
|
||||||
int i;
|
int crtc = vblank->crtc;
|
||||||
|
|
||||||
if (!dev->vblank_disable_allowed)
|
if (!dev->vblank_disable_allowed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < dev->num_crtcs; i++) {
|
|
||||||
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
||||||
if (atomic_read(&dev->vblank[i].refcount) == 0 &&
|
if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
|
||||||
dev->vblank[i].enabled) {
|
DRM_DEBUG("disabling vblank on crtc %d\n", crtc);
|
||||||
DRM_DEBUG("disabling vblank on crtc %d\n", i);
|
vblank_disable_and_save(dev, crtc);
|
||||||
vblank_disable_and_save(dev, i);
|
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drm_vblank_cleanup(struct drm_device *dev)
|
void drm_vblank_cleanup(struct drm_device *dev)
|
||||||
{
|
{
|
||||||
|
int crtc;
|
||||||
|
|
||||||
/* Bail if the driver didn't call drm_vblank_init() */
|
/* Bail if the driver didn't call drm_vblank_init() */
|
||||||
if (dev->num_crtcs == 0)
|
if (dev->num_crtcs == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
del_timer_sync(&dev->vblank_disable_timer);
|
for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
|
||||||
|
del_timer_sync(&dev->vblank[crtc].disable_timer);
|
||||||
vblank_disable_fn((unsigned long)dev);
|
vblank_disable_fn((unsigned long)&dev->vblank[crtc]);
|
||||||
|
}
|
||||||
|
|
||||||
kfree(dev->vblank);
|
kfree(dev->vblank);
|
||||||
|
|
||||||
|
@ -178,8 +179,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
|
||||||
{
|
{
|
||||||
int i, ret = -ENOMEM;
|
int i, ret = -ENOMEM;
|
||||||
|
|
||||||
setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
|
|
||||||
(unsigned long)dev);
|
|
||||||
spin_lock_init(&dev->vbl_lock);
|
spin_lock_init(&dev->vbl_lock);
|
||||||
spin_lock_init(&dev->vblank_time_lock);
|
spin_lock_init(&dev->vblank_time_lock);
|
||||||
|
|
||||||
|
@ -189,8 +188,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
|
||||||
if (!dev->vblank)
|
if (!dev->vblank)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
for (i = 0; i < num_crtcs; i++)
|
for (i = 0; i < num_crtcs; i++) {
|
||||||
|
dev->vblank[i].dev = dev;
|
||||||
|
dev->vblank[i].crtc = i;
|
||||||
init_waitqueue_head(&dev->vblank[i].queue);
|
init_waitqueue_head(&dev->vblank[i].queue);
|
||||||
|
setup_timer(&dev->vblank[i].disable_timer, vblank_disable_fn,
|
||||||
|
(unsigned long)&dev->vblank[i]);
|
||||||
|
}
|
||||||
|
|
||||||
DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
|
DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
|
||||||
|
|
||||||
|
@ -900,7 +904,7 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
|
||||||
/* Last user schedules interrupt disable */
|
/* Last user schedules interrupt disable */
|
||||||
if (atomic_dec_and_test(&dev->vblank[crtc].refcount) &&
|
if (atomic_dec_and_test(&dev->vblank[crtc].refcount) &&
|
||||||
(drm_vblank_offdelay > 0))
|
(drm_vblank_offdelay > 0))
|
||||||
mod_timer(&dev->vblank_disable_timer,
|
mod_timer(&dev->vblank[crtc].disable_timer,
|
||||||
jiffies + ((drm_vblank_offdelay * HZ)/1000));
|
jiffies + ((drm_vblank_offdelay * HZ)/1000));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(drm_vblank_put);
|
EXPORT_SYMBOL(drm_vblank_put);
|
||||||
|
@ -909,8 +913,6 @@ EXPORT_SYMBOL(drm_vblank_put);
|
||||||
* drm_vblank_off - disable vblank events on a CRTC
|
* drm_vblank_off - disable vblank events on a CRTC
|
||||||
* @dev: DRM device
|
* @dev: DRM device
|
||||||
* @crtc: CRTC in question
|
* @crtc: CRTC in question
|
||||||
*
|
|
||||||
* Caller must hold event lock.
|
|
||||||
*/
|
*/
|
||||||
void drm_vblank_off(struct drm_device *dev, int crtc)
|
void drm_vblank_off(struct drm_device *dev, int crtc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1024,14 +1024,17 @@ struct drm_pending_vblank_event {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct drm_vblank_crtc {
|
struct drm_vblank_crtc {
|
||||||
|
struct drm_device *dev; /* pointer to the drm_device */
|
||||||
wait_queue_head_t queue; /**< VBLANK wait queue */
|
wait_queue_head_t queue; /**< VBLANK wait queue */
|
||||||
struct timeval time[DRM_VBLANKTIME_RBSIZE]; /**< timestamp of current count */
|
struct timeval time[DRM_VBLANKTIME_RBSIZE]; /**< timestamp of current count */
|
||||||
|
struct timer_list disable_timer; /* delayed disable timer */
|
||||||
atomic_t count; /**< number of VBLANK interrupts */
|
atomic_t count; /**< number of VBLANK interrupts */
|
||||||
atomic_t refcount; /* number of users of vblank interruptsper crtc */
|
atomic_t refcount; /* number of users of vblank interruptsper crtc */
|
||||||
u32 last; /* protected by dev->vbl_lock, used */
|
u32 last; /* protected by dev->vbl_lock, used */
|
||||||
/* for wraparound handling */
|
/* for wraparound handling */
|
||||||
u32 last_wait; /* Last vblank seqno waited per CRTC */
|
u32 last_wait; /* Last vblank seqno waited per CRTC */
|
||||||
unsigned int inmodeset; /* Display driver is setting mode */
|
unsigned int inmodeset; /* Display driver is setting mode */
|
||||||
|
int crtc; /* crtc index */
|
||||||
bool enabled; /* so we don't call enable more than
|
bool enabled; /* so we don't call enable more than
|
||||||
once per disable */
|
once per disable */
|
||||||
};
|
};
|
||||||
|
@ -1119,7 +1122,6 @@ struct drm_device {
|
||||||
|
|
||||||
spinlock_t vblank_time_lock; /**< Protects vblank count and time updates during vblank enable/disable */
|
spinlock_t vblank_time_lock; /**< Protects vblank count and time updates during vblank enable/disable */
|
||||||
spinlock_t vbl_lock;
|
spinlock_t vbl_lock;
|
||||||
struct timer_list vblank_disable_timer;
|
|
||||||
|
|
||||||
u32 max_vblank_count; /**< size of vblank counter register */
|
u32 max_vblank_count; /**< size of vblank counter register */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue