Commit 016b9b61 authored by Chris Wilson's avatar Chris Wilson Committed by Keith Packard

drm/i915: Share the common work of disabling active FBC before updating

Upon review, all path share the same dependencies for updating the
registers and so we can benefit from sharing the code and checking
early.

This removes the unsightly intel_wait_for_vblank() from the lowlevel
functions and upon further analysis the only path that will require a
wait is if we are performing an instantaneous transition between two
valid FBC configurations. The page-flip path itself will have disabled
FBC registers and will have waited for at least one vblank before
finishing the flip and attempting to re-enable FBC. This wait can be
accomplished simply by delaying the enable until after we are sure that
a vblank will have passed, which we are already doing to make sure that
the display is settled before enabling FBC.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarKeith Packard <keithp@keithp.com>
Signed-off-by: default avatarKeith Packard <keithp@keithp.com>
parent 1630fe75
...@@ -331,10 +331,8 @@ typedef struct drm_i915_private { ...@@ -331,10 +331,8 @@ typedef struct drm_i915_private {
uint32_t last_instdone1; uint32_t last_instdone1;
unsigned long cfb_size; unsigned long cfb_size;
unsigned long cfb_pitch; unsigned int cfb_fb;
unsigned long cfb_offset; enum plane cfb_plane;
int cfb_fence;
int cfb_plane;
int cfb_y; int cfb_y;
struct intel_fbc_work *fbc_work; struct intel_fbc_work *fbc_work;
......
...@@ -1414,27 +1414,17 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) ...@@ -1414,27 +1414,17 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb->obj; struct drm_i915_gem_object *obj = intel_fb->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int cfb_pitch;
int plane, i; int plane, i;
u32 fbc_ctl, fbc_ctl2; u32 fbc_ctl, fbc_ctl2;
if (fb->pitch == dev_priv->cfb_pitch && cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
obj->fence_reg == dev_priv->cfb_fence && if (fb->pitch < cfb_pitch)
intel_crtc->plane == dev_priv->cfb_plane && cfb_pitch = fb->pitch;
I915_READ(FBC_CONTROL) & FBC_CTL_EN)
return;
i8xx_disable_fbc(dev);
dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
if (fb->pitch < dev_priv->cfb_pitch)
dev_priv->cfb_pitch = fb->pitch;
/* FBC_CTL wants 64B units */ /* FBC_CTL wants 64B units */
dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; cfb_pitch = (cfb_pitch / 64) - 1;
dev_priv->cfb_fence = obj->fence_reg; plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
dev_priv->cfb_plane = intel_crtc->plane;
plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
/* Clear old tags */ /* Clear old tags */
for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++) for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
...@@ -1450,13 +1440,13 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) ...@@ -1450,13 +1440,13 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC; fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
if (IS_I945GM(dev)) if (IS_I945GM(dev))
fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */ fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT; fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
fbc_ctl |= dev_priv->cfb_fence; fbc_ctl |= obj->fence_reg;
I915_WRITE(FBC_CONTROL, fbc_ctl); I915_WRITE(FBC_CONTROL, fbc_ctl);
DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ", DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ",
dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); cfb_pitch, crtc->y, intel_crtc->plane);
} }
static bool i8xx_fbc_enabled(struct drm_device *dev) static bool i8xx_fbc_enabled(struct drm_device *dev)
...@@ -1478,23 +1468,8 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) ...@@ -1478,23 +1468,8 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
unsigned long stall_watermark = 200; unsigned long stall_watermark = 200;
u32 dpfc_ctl; u32 dpfc_ctl;
dpfc_ctl = I915_READ(DPFC_CONTROL);
if (dpfc_ctl & DPFC_CTL_EN) {
if (dev_priv->cfb_fence == obj->fence_reg &&
dev_priv->cfb_plane == intel_crtc->plane &&
dev_priv->cfb_y == crtc->y)
return;
I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
intel_wait_for_vblank(dev, intel_crtc->pipe);
}
dev_priv->cfb_fence = obj->fence_reg;
dev_priv->cfb_plane = intel_crtc->plane;
dev_priv->cfb_y = crtc->y;
dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence; dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg;
I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
...@@ -1563,27 +1538,11 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) ...@@ -1563,27 +1538,11 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
u32 dpfc_ctl; u32 dpfc_ctl;
dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
if (dpfc_ctl & DPFC_CTL_EN) {
if (dev_priv->cfb_fence == obj->fence_reg &&
dev_priv->cfb_plane == intel_crtc->plane &&
dev_priv->cfb_offset == obj->gtt_offset &&
dev_priv->cfb_y == crtc->y)
return;
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
intel_wait_for_vblank(dev, intel_crtc->pipe);
}
dev_priv->cfb_fence = obj->fence_reg;
dev_priv->cfb_plane = intel_crtc->plane;
dev_priv->cfb_offset = obj->gtt_offset;
dev_priv->cfb_y = crtc->y;
dpfc_ctl &= DPFC_RESERVED; dpfc_ctl &= DPFC_RESERVED;
dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X); dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
/* Set persistent mode for front-buffer rendering, ala X. */ /* Set persistent mode for front-buffer rendering, ala X. */
dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE; dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE;
dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence); dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg);
I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
...@@ -1596,7 +1555,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) ...@@ -1596,7 +1555,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
if (IS_GEN6(dev)) { if (IS_GEN6(dev)) {
I915_WRITE(SNB_DPFC_CTL_SA, I915_WRITE(SNB_DPFC_CTL_SA,
SNB_CPU_FENCE_ENABLE | dev_priv->cfb_fence); SNB_CPU_FENCE_ENABLE | obj->fence_reg);
I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y);
sandybridge_blit_fbc_update(dev); sandybridge_blit_fbc_update(dev);
} }
...@@ -1649,10 +1608,15 @@ static void intel_fbc_work_fn(struct work_struct *__work) ...@@ -1649,10 +1608,15 @@ static void intel_fbc_work_fn(struct work_struct *__work)
/* Double check that we haven't switched fb without cancelling /* Double check that we haven't switched fb without cancelling
* the prior work. * the prior work.
*/ */
if (work->crtc->fb == work->fb) if (work->crtc->fb == work->fb) {
dev_priv->display.enable_fbc(work->crtc, dev_priv->display.enable_fbc(work->crtc,
work->interval); work->interval);
dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane;
dev_priv->cfb_fb = work->crtc->fb->base.id;
dev_priv->cfb_y = work->crtc->y;
}
dev_priv->fbc_work = NULL; dev_priv->fbc_work = NULL;
} }
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
...@@ -1710,7 +1674,10 @@ static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) ...@@ -1710,7 +1674,10 @@ static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
DRM_DEBUG_KMS("scheduling delayed FBC enable\n"); DRM_DEBUG_KMS("scheduling delayed FBC enable\n");
/* Delay the actual enabling to let pageflipping cease and the /* Delay the actual enabling to let pageflipping cease and the
* display to settle before starting the compression. * display to settle before starting the compression. Note that
* this delay also serves a second purpose: it allows for a
* vblank to pass after disabling the FBC before we attempt
* to modify the control registers.
* *
* A more complicated solution would involve tracking vblanks * A more complicated solution would involve tracking vblanks
* following the termination of the page-flipping sequence * following the termination of the page-flipping sequence
...@@ -1730,6 +1697,7 @@ void intel_disable_fbc(struct drm_device *dev) ...@@ -1730,6 +1697,7 @@ void intel_disable_fbc(struct drm_device *dev)
return; return;
dev_priv->display.disable_fbc(dev); dev_priv->display.disable_fbc(dev);
dev_priv->cfb_plane = -1;
} }
/** /**
...@@ -1843,6 +1811,44 @@ static void intel_update_fbc(struct drm_device *dev) ...@@ -1843,6 +1811,44 @@ static void intel_update_fbc(struct drm_device *dev)
if (in_dbg_master()) if (in_dbg_master())
goto out_disable; goto out_disable;
/* If the scanout has not changed, don't modify the FBC settings.
* Note that we make the fundamental assumption that the fb->obj
* cannot be unpinned (and have its GTT offset and fence revoked)
* without first being decoupled from the scanout and FBC disabled.
*/
if (dev_priv->cfb_plane == intel_crtc->plane &&
dev_priv->cfb_fb == fb->base.id &&
dev_priv->cfb_y == crtc->y)
return;
if (intel_fbc_enabled(dev)) {
/* We update FBC along two paths, after changing fb/crtc
* configuration (modeswitching) and after page-flipping
* finishes. For the latter, we know that not only did
* we disable the FBC at the start of the page-flip
* sequence, but also more than one vblank has passed.
*
* For the former case of modeswitching, it is possible
* to switch between two FBC valid configurations
* instantaneously so we do need to disable the FBC
* before we can modify its control registers. We also
* have to wait for the next vblank for that to take
* effect. However, since we delay enabling FBC we can
* assume that a vblank has passed since disabling and
* that we can safely alter the registers in the deferred
* callback.
*
* In the scenario that we go from a valid to invalid
* and then back to valid FBC configuration we have
* no strict enforcement that a vblank occurred since
* disabling the FBC. However, along all current pipe
* disabling paths we do need to wait for a vblank at
* some point. And we wait before enabling FBC anyway.
*/
DRM_DEBUG_KMS("disabling active FBC for update\n");
intel_disable_fbc(dev);
}
intel_enable_fbc(crtc, 500); intel_enable_fbc(crtc, 500);
return; return;
......
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