Commit f2752282 authored by Ville Syrjälä's avatar Ville Syrjälä Committed by Daniel Vetter

drm: Add drm_vblank_on()

drm_vblank_off() will turn off vblank interrupts, but as long as the
refcount is elevated drm_vblank_get() will not re-enable them. This
is a problem is someone is holding a vblank reference while a modeset is
happening, and the driver requires vblank interrupt to work during that
time.

Add drm_vblank_on() as a counterpart to drm_vblank_off() which will
re-enabled vblank interrupts if the refcount is already elevated. This
will allow drivers to choose the specific places in the modeset sequence
at which vblank interrupts get disabled and enabled.

Testcase: igt/kms_flip/*-vs-suspend
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
[danvet: Add Testcase tag for the igt I've written.]
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 3212a22f
...@@ -840,25 +840,18 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) ...@@ -840,25 +840,18 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
} }
/** /**
* drm_vblank_get - get a reference count on vblank events * drm_vblank_enable - enable the vblank interrupt on a CRTC
* @dev: DRM device * @dev: DRM device
* @crtc: which CRTC to own * @crtc: CRTC in question
*
* Acquire a reference count on vblank events to avoid having them disabled
* while in use.
*
* RETURNS
* Zero on success, nonzero on failure.
*/ */
int drm_vblank_get(struct drm_device *dev, int crtc) static int drm_vblank_enable(struct drm_device *dev, int crtc)
{ {
unsigned long irqflags;
int ret = 0; int ret = 0;
spin_lock_irqsave(&dev->vbl_lock, irqflags); assert_spin_locked(&dev->vbl_lock);
/* Going from 0->1 means we have to enable interrupts again */
if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) {
spin_lock(&dev->vblank_time_lock); spin_lock(&dev->vblank_time_lock);
if (!dev->vblank[crtc].enabled) { if (!dev->vblank[crtc].enabled) {
/* Enable vblank irqs under vblank_time_lock protection. /* Enable vblank irqs under vblank_time_lock protection.
* All vblank count & timestamp updates are held off * All vblank count & timestamp updates are held off
...@@ -867,8 +860,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc) ...@@ -867,8 +860,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
* prevent double-accounting of same vblank interval. * prevent double-accounting of same vblank interval.
*/ */
ret = dev->driver->enable_vblank(dev, crtc); ret = dev->driver->enable_vblank(dev, crtc);
DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
crtc, ret);
if (ret) if (ret)
atomic_dec(&dev->vblank[crtc].refcount); atomic_dec(&dev->vblank[crtc].refcount);
else { else {
...@@ -876,7 +868,32 @@ int drm_vblank_get(struct drm_device *dev, int crtc) ...@@ -876,7 +868,32 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
drm_update_vblank_count(dev, crtc); drm_update_vblank_count(dev, crtc);
} }
} }
spin_unlock(&dev->vblank_time_lock); spin_unlock(&dev->vblank_time_lock);
return ret;
}
/**
* drm_vblank_get - get a reference count on vblank events
* @dev: DRM device
* @crtc: which CRTC to own
*
* Acquire a reference count on vblank events to avoid having them disabled
* while in use.
*
* RETURNS
* Zero on success, nonzero on failure.
*/
int drm_vblank_get(struct drm_device *dev, int crtc)
{
unsigned long irqflags;
int ret = 0;
spin_lock_irqsave(&dev->vbl_lock, irqflags);
/* Going from 0->1 means we have to enable interrupts again */
if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) {
ret = drm_vblank_enable(dev, crtc);
} else { } else {
if (!dev->vblank[crtc].enabled) { if (!dev->vblank[crtc].enabled) {
atomic_dec(&dev->vblank[crtc].refcount); atomic_dec(&dev->vblank[crtc].refcount);
...@@ -945,6 +962,23 @@ void drm_vblank_off(struct drm_device *dev, int crtc) ...@@ -945,6 +962,23 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
} }
EXPORT_SYMBOL(drm_vblank_off); EXPORT_SYMBOL(drm_vblank_off);
/**
* drm_vblank_on - enable vblank events on a CRTC
* @dev: DRM device
* @crtc: CRTC in question
*/
void drm_vblank_on(struct drm_device *dev, int crtc)
{
unsigned long irqflags;
spin_lock_irqsave(&dev->vbl_lock, irqflags);
/* re-enable interrupts if there's are users left */
if (atomic_read(&dev->vblank[crtc].refcount) != 0)
WARN_ON(drm_vblank_enable(dev, crtc));
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
EXPORT_SYMBOL(drm_vblank_on);
/** /**
* drm_vblank_pre_modeset - account for vblanks across mode sets * drm_vblank_pre_modeset - account for vblanks across mode sets
* @dev: DRM device * @dev: DRM device
......
...@@ -3737,6 +3737,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) ...@@ -3737,6 +3737,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
* happening. * happening.
*/ */
intel_wait_for_vblank(dev, intel_crtc->pipe); intel_wait_for_vblank(dev, intel_crtc->pipe);
drm_vblank_on(dev, pipe);
} }
/* IPS only exists on ULT machines and is tied to pipe A. */ /* IPS only exists on ULT machines and is tied to pipe A. */
...@@ -3828,6 +3830,8 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) ...@@ -3828,6 +3830,8 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
* to change the workaround. */ * to change the workaround. */
haswell_mode_set_planes_workaround(intel_crtc); haswell_mode_set_planes_workaround(intel_crtc);
ilk_crtc_enable_planes(crtc); ilk_crtc_enable_planes(crtc);
drm_vblank_on(dev, pipe);
} }
static void ironlake_pfit_disable(struct intel_crtc *crtc) static void ironlake_pfit_disable(struct intel_crtc *crtc)
...@@ -4351,6 +4355,8 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) ...@@ -4351,6 +4355,8 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
for_each_encoder_on_crtc(dev, crtc, encoder) for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->enable(encoder); encoder->enable(encoder);
drm_vblank_on(dev, pipe);
} }
static void i9xx_crtc_enable(struct drm_crtc *crtc) static void i9xx_crtc_enable(struct drm_crtc *crtc)
...@@ -4398,6 +4404,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) ...@@ -4398,6 +4404,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
for_each_encoder_on_crtc(dev, crtc, encoder) for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->enable(encoder); encoder->enable(encoder);
drm_vblank_on(dev, pipe);
} }
static void i9xx_pfit_disable(struct intel_crtc *crtc) static void i9xx_pfit_disable(struct intel_crtc *crtc)
......
...@@ -1360,6 +1360,7 @@ extern bool drm_handle_vblank(struct drm_device *dev, int crtc); ...@@ -1360,6 +1360,7 @@ extern bool drm_handle_vblank(struct drm_device *dev, int crtc);
extern int drm_vblank_get(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc);
extern void drm_vblank_put(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc);
extern void drm_vblank_off(struct drm_device *dev, int crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc);
extern void drm_vblank_on(struct drm_device *dev, int crtc);
extern void drm_vblank_cleanup(struct drm_device *dev); extern void drm_vblank_cleanup(struct drm_device *dev);
extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
struct timeval *tvblank, unsigned flags); struct timeval *tvblank, unsigned flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment