drm/i915: Handle changing enable_fbc parameter at runtime better.
[linux-2.6-microblaze.git] / drivers / gpu / drm / i915 / intel_fbc.c
index f88c1b5..707d49c 100644 (file)
@@ -46,16 +46,6 @@ static inline bool fbc_supported(struct drm_i915_private *dev_priv)
        return HAS_FBC(dev_priv);
 }
 
-static inline bool fbc_on_pipe_a_only(struct drm_i915_private *dev_priv)
-{
-       return IS_HASWELL(dev_priv) || INTEL_GEN(dev_priv) >= 8;
-}
-
-static inline bool fbc_on_plane_a_only(struct drm_i915_private *dev_priv)
-{
-       return INTEL_GEN(dev_priv) < 4;
-}
-
 static inline bool no_fbc_on_multiple_pipes(struct drm_i915_private *dev_priv)
 {
        return INTEL_GEN(dev_priv) <= 3;
@@ -183,7 +173,7 @@ static void g4x_fbc_activate(struct drm_i915_private *dev_priv)
        else
                dpfc_ctl |= DPFC_CTL_LIMIT_1X;
 
-       if (params->vma->fence) {
+       if (params->flags & PLANE_HAS_FENCE) {
                dpfc_ctl |= DPFC_CTL_FENCE_EN | params->vma->fence->id;
                I915_WRITE(DPFC_FENCE_YOFF, params->crtc.fence_y_offset);
        } else {
@@ -241,7 +231,7 @@ static void ilk_fbc_activate(struct drm_i915_private *dev_priv)
                break;
        }
 
-       if (params->vma->fence) {
+       if (params->flags & PLANE_HAS_FENCE) {
                dpfc_ctl |= DPFC_CTL_FENCE_EN;
                if (IS_GEN5(dev_priv))
                        dpfc_ctl |= params->vma->fence->id;
@@ -324,7 +314,7 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv)
                break;
        }
 
-       if (params->vma->fence) {
+       if (params->flags & PLANE_HAS_FENCE) {
                dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN;
                I915_WRITE(SNB_DPFC_CTL_SA,
                           SNB_CPU_FENCE_ENABLE |
@@ -492,7 +482,8 @@ static void intel_fbc_schedule_activation(struct intel_crtc *crtc)
        schedule_work(&work->work);
 }
 
-static void intel_fbc_deactivate(struct drm_i915_private *dev_priv)
+static void intel_fbc_deactivate(struct drm_i915_private *dev_priv,
+                                const char *reason)
 {
        struct intel_fbc *fbc = &dev_priv->fbc;
 
@@ -505,6 +496,8 @@ static void intel_fbc_deactivate(struct drm_i915_private *dev_priv)
 
        if (fbc->active)
                intel_fbc_hw_deactivate(dev_priv);
+
+       fbc->no_fbc_reason = reason;
 }
 
 static bool multiple_pipes_ok(struct intel_crtc *crtc,
@@ -668,11 +661,13 @@ void intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv)
 static bool stride_is_valid(struct drm_i915_private *dev_priv,
                            unsigned int stride)
 {
-       /* These should have been caught earlier. */
-       WARN_ON(stride < 512);
-       WARN_ON((stride & (64 - 1)) != 0);
+       /* This should have been caught earlier. */
+       if (WARN_ON_ONCE((stride & (64 - 1)) != 0))
+               return false;
 
        /* Below are the additional FBC restrictions. */
+       if (stride < 512)
+               return false;
 
        if (IS_GEN2(dev_priv) || IS_GEN3(dev_priv))
                return stride == 4096 || stride == 8192;
@@ -748,6 +743,7 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
        struct drm_framebuffer *fb = plane_state->base.fb;
 
        cache->vma = NULL;
+       cache->flags = 0;
 
        cache->crtc.mode_flags = crtc_state->base.adjusted_mode.flags;
        if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
@@ -773,6 +769,9 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
        cache->fb.stride = fb->pitches[0];
 
        cache->vma = plane_state->vma;
+       cache->flags = plane_state->flags;
+       if (WARN_ON(cache->flags & PLANE_HAS_FENCE && !cache->vma->fence))
+               cache->flags &= ~PLANE_HAS_FENCE;
 }
 
 static bool intel_fbc_can_activate(struct intel_crtc *crtc)
@@ -794,8 +793,7 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc)
                return false;
        }
 
-       if ((cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) ||
-           (cache->crtc.mode_flags & DRM_MODE_FLAG_DBLSCAN)) {
+       if (cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) {
                fbc->no_fbc_reason = "incompatible mode";
                return false;
        }
@@ -811,8 +809,14 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc)
         * Note that is possible for a tiled surface to be unmappable (and
         * so have no fence associated with it) due to aperture constaints
         * at the time of pinning.
+        *
+        * FIXME with 90/270 degree rotation we should use the fence on
+        * the normal GTT view (the rotated view doesn't even have a
+        * fence). Would need changes to the FBC fence Y offset as well.
+        * For now this will effecively disable FBC with 90/270 degree
+        * rotation.
         */
-       if (!cache->vma->fence) {
+       if (!(cache->flags & PLANE_HAS_FENCE)) {
                fbc->no_fbc_reason = "framebuffer not tiled or fenced";
                return false;
        }
@@ -855,6 +859,17 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc)
                return false;
        }
 
+       /*
+        * Work around a problem on GEN9+ HW, where enabling FBC on a plane
+        * having a Y offset that isn't divisible by 4 causes FIFO underrun
+        * and screen flicker.
+        */
+       if (IS_GEN(dev_priv, 9, 10) &&
+           (fbc->state_cache.plane.adjusted_y & 3)) {
+               fbc->no_fbc_reason = "plane Y offset is misaligned";
+               return false;
+       }
+
        return true;
 }
 
@@ -893,6 +908,7 @@ static void intel_fbc_get_reg_params(struct intel_crtc *crtc,
        memset(params, 0, sizeof(*params));
 
        params->vma = cache->vma;
+       params->flags = cache->flags;
 
        params->crtc.pipe = crtc->pipe;
        params->crtc.i9xx_plane = to_intel_plane(crtc->base.primary)->i9xx_plane;
@@ -921,6 +937,7 @@ void intel_fbc_pre_update(struct intel_crtc *crtc,
 {
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
        struct intel_fbc *fbc = &dev_priv->fbc;
+       const char *reason = "update pending";
 
        if (!fbc_supported(dev_priv))
                return;
@@ -928,7 +945,7 @@ void intel_fbc_pre_update(struct intel_crtc *crtc,
        mutex_lock(&fbc->lock);
 
        if (!multiple_pipes_ok(crtc, plane_state)) {
-               fbc->no_fbc_reason = "more than one pipe active";
+               reason = "more than one pipe active";
                goto deactivate;
        }
 
@@ -938,11 +955,35 @@ void intel_fbc_pre_update(struct intel_crtc *crtc,
        intel_fbc_update_state_cache(crtc, crtc_state, plane_state);
 
 deactivate:
-       intel_fbc_deactivate(dev_priv);
+       intel_fbc_deactivate(dev_priv, reason);
 unlock:
        mutex_unlock(&fbc->lock);
 }
 
+/**
+ * __intel_fbc_disable - disable FBC
+ * @dev_priv: i915 device instance
+ *
+ * This is the low level function that actually disables FBC. Callers should
+ * grab the FBC lock.
+ */
+static void __intel_fbc_disable(struct drm_i915_private *dev_priv)
+{
+       struct intel_fbc *fbc = &dev_priv->fbc;
+       struct intel_crtc *crtc = fbc->crtc;
+
+       WARN_ON(!mutex_is_locked(&fbc->lock));
+       WARN_ON(!fbc->enabled);
+       WARN_ON(fbc->active);
+
+       DRM_DEBUG_KMS("Disabling FBC on pipe %c\n", pipe_name(crtc->pipe));
+
+       __intel_fbc_cleanup_cfb(dev_priv);
+
+       fbc->enabled = false;
+       fbc->crtc = NULL;
+}
+
 static void __intel_fbc_post_update(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
@@ -954,6 +995,13 @@ static void __intel_fbc_post_update(struct intel_crtc *crtc)
        if (!fbc->enabled || fbc->crtc != crtc)
                return;
 
+       if (!i915_modparams.enable_fbc) {
+               intel_fbc_deactivate(dev_priv, "disabled at runtime per module param");
+               __intel_fbc_disable(dev_priv);
+
+               return;
+       }
+
        if (!intel_fbc_can_activate(crtc)) {
                WARN_ON(fbc->active);
                return;
@@ -971,9 +1019,8 @@ static void __intel_fbc_post_update(struct intel_crtc *crtc)
            intel_fbc_reg_params_equal(&old_params, &fbc->params))
                return;
 
-       intel_fbc_deactivate(dev_priv);
+       intel_fbc_deactivate(dev_priv, "FBC enabled (active or scheduled)");
        intel_fbc_schedule_activation(crtc);
-       fbc->no_fbc_reason = "FBC enabled (active or scheduled)";
 }
 
 void intel_fbc_post_update(struct intel_crtc *crtc)
@@ -1014,7 +1061,7 @@ void intel_fbc_invalidate(struct drm_i915_private *dev_priv,
        fbc->busy_bits |= intel_fbc_get_frontbuffer_bit(fbc) & frontbuffer_bits;
 
        if (fbc->enabled && fbc->busy_bits)
-               intel_fbc_deactivate(dev_priv);
+               intel_fbc_deactivate(dev_priv, "frontbuffer write");
 
        mutex_unlock(&fbc->lock);
 }
@@ -1085,13 +1132,10 @@ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv,
                struct intel_crtc_state *crtc_state;
                struct intel_crtc *crtc = to_intel_crtc(plane_state->base.crtc);
 
-               if (!plane_state->base.visible)
+               if (!plane->has_fbc)
                        continue;
 
-               if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe != PIPE_A)
-                       continue;
-
-               if (fbc_on_plane_a_only(dev_priv) && plane->i9xx_plane != PLANE_A)
+               if (!plane_state->base.visible)
                        continue;
 
                crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
@@ -1161,31 +1205,6 @@ out:
        mutex_unlock(&fbc->lock);
 }
 
-/**
- * __intel_fbc_disable - disable FBC
- * @dev_priv: i915 device instance
- *
- * This is the low level function that actually disables FBC. Callers should
- * grab the FBC lock.
- */
-static void __intel_fbc_disable(struct drm_i915_private *dev_priv)
-{
-       struct intel_fbc *fbc = &dev_priv->fbc;
-       struct intel_crtc *crtc = fbc->crtc;
-
-       WARN_ON(!mutex_is_locked(&fbc->lock));
-       WARN_ON(!fbc->enabled);
-       WARN_ON(fbc->active);
-       WARN_ON(crtc->active);
-
-       DRM_DEBUG_KMS("Disabling FBC on pipe %c\n", pipe_name(crtc->pipe));
-
-       __intel_fbc_cleanup_cfb(dev_priv);
-
-       fbc->enabled = false;
-       fbc->crtc = NULL;
-}
-
 /**
  * intel_fbc_disable - disable FBC if it's associated with crtc
  * @crtc: the CRTC
@@ -1200,6 +1219,8 @@ void intel_fbc_disable(struct intel_crtc *crtc)
        if (!fbc_supported(dev_priv))
                return;
 
+       WARN_ON(crtc->active);
+
        mutex_lock(&fbc->lock);
        if (fbc->crtc == crtc)
                __intel_fbc_disable(dev_priv);
@@ -1222,8 +1243,10 @@ void intel_fbc_global_disable(struct drm_i915_private *dev_priv)
                return;
 
        mutex_lock(&fbc->lock);
-       if (fbc->enabled)
+       if (fbc->enabled) {
+               WARN_ON(fbc->crtc->active);
                __intel_fbc_disable(dev_priv);
+       }
        mutex_unlock(&fbc->lock);
 
        cancel_work_sync(&fbc->work.work);
@@ -1244,7 +1267,7 @@ static void intel_fbc_underrun_work_fn(struct work_struct *work)
        DRM_DEBUG_KMS("Disabling FBC due to FIFO underrun.\n");
        fbc->underrun_detected = true;
 
-       intel_fbc_deactivate(dev_priv);
+       intel_fbc_deactivate(dev_priv, "FIFO underrun");
 out:
        mutex_unlock(&fbc->lock);
 }
@@ -1348,7 +1371,6 @@ static bool need_fbc_vtd_wa(struct drm_i915_private *dev_priv)
 void intel_fbc_init(struct drm_i915_private *dev_priv)
 {
        struct intel_fbc *fbc = &dev_priv->fbc;
-       enum pipe pipe;
 
        INIT_WORK(&fbc->work.work, intel_fbc_work_fn);
        INIT_WORK(&fbc->underrun_work, intel_fbc_underrun_work_fn);
@@ -1369,14 +1391,6 @@ void intel_fbc_init(struct drm_i915_private *dev_priv)
                return;
        }
 
-       for_each_pipe(dev_priv, pipe) {
-               fbc->possible_framebuffer_bits |=
-                               INTEL_FRONTBUFFER_PRIMARY(pipe);
-
-               if (fbc_on_pipe_a_only(dev_priv))
-                       break;
-       }
-
        /* This value was pulled out of someone's hat */
        if (INTEL_GEN(dev_priv) <= 4 && !IS_GM45(dev_priv))
                I915_WRITE(FBC_CONTROL, 500 << FBC_CTL_INTERVAL_SHIFT);