]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/i915/intel_display.c
Merge branch 'drm-intel-fixes' into drm-intel-next
[karo-tx-linux.git] / drivers / gpu / drm / i915 / intel_display.c
index 09236115395737e89c54cad6d5b16e13a6ec4740..97d28013db795559ae730ef2ee26209cd6d410d5 100644 (file)
@@ -1414,36 +1414,25 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
        struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int cfb_pitch;
        int plane, i;
        u32 fbc_ctl, fbc_ctl2;
 
-       if (fb->pitch == dev_priv->cfb_pitch &&
-           obj->fence_reg == dev_priv->cfb_fence &&
-           intel_crtc->plane == dev_priv->cfb_plane &&
-           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;
+       cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
+       if (fb->pitch < cfb_pitch)
+               cfb_pitch = fb->pitch;
 
        /* FBC_CTL wants 64B units */
-       dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
-       dev_priv->cfb_fence = obj->fence_reg;
-       dev_priv->cfb_plane = intel_crtc->plane;
-       plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
+       cfb_pitch = (cfb_pitch / 64) - 1;
+       plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
 
        /* Clear old tags */
        for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
                I915_WRITE(FBC_TAG + (i * 4), 0);
 
        /* Set it up... */
-       fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane;
-       if (obj->tiling_mode != I915_TILING_NONE)
-               fbc_ctl2 |= FBC_CTL_CPU_FENCE;
+       fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE;
+       fbc_ctl2 |= plane;
        I915_WRITE(FBC_CONTROL2, fbc_ctl2);
        I915_WRITE(FBC_FENCE_OFF, crtc->y);
 
@@ -1451,14 +1440,13 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
        if (IS_I945GM(dev))
                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;
-       if (obj->tiling_mode != I915_TILING_NONE)
-               fbc_ctl |= dev_priv->cfb_fence;
+       fbc_ctl |= obj->fence_reg;
        I915_WRITE(FBC_CONTROL, fbc_ctl);
 
-       DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
-                     dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
+       DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ",
+                     cfb_pitch, crtc->y, intel_crtc->plane);
 }
 
 static bool i8xx_fbc_enabled(struct drm_device *dev)
@@ -1480,30 +1468,9 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        unsigned long stall_watermark = 200;
        u32 dpfc_ctl;
 
-       dpfc_ctl = I915_READ(DPFC_CONTROL);
-       if (dpfc_ctl & DPFC_CTL_EN) {
-               if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
-                   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_pitch = (dev_priv->cfb_pitch / 64) - 1;
-       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;
-       if (obj->tiling_mode != I915_TILING_NONE) {
-               dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence;
-               I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
-       } else {
-               I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY);
-       }
+       dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg;
+       I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
 
        I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
                   (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
@@ -1571,32 +1538,12 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        u32 dpfc_ctl;
 
        dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
-       if (dpfc_ctl & DPFC_CTL_EN) {
-               if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
-                   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_pitch = (dev_priv->cfb_pitch / 64) - 1;
-       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 |= (plane | DPFC_CTL_LIMIT_1X);
-       if (obj->tiling_mode != I915_TILING_NONE) {
-               dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence);
-               I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
-       } else {
-               I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY);
-       }
+       /* Set persistent mode for front-buffer rendering, ala X. */
+       dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE;
+       dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg);
+       I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
 
        I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
                   (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
@@ -1608,7 +1555,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 
        if (IS_GEN6(dev)) {
                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);
                sandybridge_blit_fbc_update(dev);
        }
@@ -1648,24 +1595,109 @@ bool intel_fbc_enabled(struct drm_device *dev)
        return dev_priv->display.fbc_enabled(dev);
 }
 
+static void intel_fbc_work_fn(struct work_struct *__work)
+{
+       struct intel_fbc_work *work =
+               container_of(to_delayed_work(__work),
+                            struct intel_fbc_work, work);
+       struct drm_device *dev = work->crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       mutex_lock(&dev->struct_mutex);
+       if (work == dev_priv->fbc_work) {
+               /* Double check that we haven't switched fb without cancelling
+                * the prior work.
+                */
+               if (work->crtc->fb == work->fb) {
+                       dev_priv->display.enable_fbc(work->crtc,
+                                                    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;
+       }
+       mutex_unlock(&dev->struct_mutex);
+
+       kfree(work);
+}
+
+static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv)
+{
+       if (dev_priv->fbc_work == NULL)
+               return;
+
+       DRM_DEBUG_KMS("cancelling pending FBC enable\n");
+
+       /* Synchronisation is provided by struct_mutex and checking of
+        * dev_priv->fbc_work, so we can perform the cancellation
+        * entirely asynchronously.
+        */
+       if (cancel_delayed_work(&dev_priv->fbc_work->work))
+               /* tasklet was killed before being run, clean up */
+               kfree(dev_priv->fbc_work);
+
+       /* Mark the work as no longer wanted so that if it does
+        * wake-up (because the work was already running and waiting
+        * for our mutex), it will discover that is no longer
+        * necessary to run.
+        */
+       dev_priv->fbc_work = NULL;
+}
+
 static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 {
-       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct intel_fbc_work *work;
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (!dev_priv->display.enable_fbc)
                return;
 
-       dev_priv->display.enable_fbc(crtc, interval);
+       intel_cancel_fbc_work(dev_priv);
+
+       work = kzalloc(sizeof *work, GFP_KERNEL);
+       if (work == NULL) {
+               dev_priv->display.enable_fbc(crtc, interval);
+               return;
+       }
+
+       work->crtc = crtc;
+       work->fb = crtc->fb;
+       work->interval = interval;
+       INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
+
+       dev_priv->fbc_work = work;
+
+       DRM_DEBUG_KMS("scheduling delayed FBC enable\n");
+
+       /* Delay the actual enabling to let pageflipping cease and the
+        * 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
+        * following the termination of the page-flipping sequence
+        * and indeed performing the enable as a co-routine and not
+        * waiting synchronously upon the vblank.
+        */
+       schedule_delayed_work(&work->work, msecs_to_jiffies(50));
 }
 
 void intel_disable_fbc(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       intel_cancel_fbc_work(dev_priv);
+
        if (!dev_priv->display.disable_fbc)
                return;
 
        dev_priv->display.disable_fbc(dev);
+       dev_priv->cfb_plane = -1;
 }
 
 /**
@@ -1764,8 +1796,13 @@ static void intel_update_fbc(struct drm_device *dev)
                dev_priv->no_fbc_reason = FBC_BAD_PLANE;
                goto out_disable;
        }
-       if (obj->tiling_mode != I915_TILING_X) {
-               DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
+
+       /* The use of a CPU fence is mandatory in order to detect writes
+        * by the CPU to the scanout and trigger updates to the FBC.
+        */
+       if (obj->tiling_mode != I915_TILING_X ||
+           obj->fence_reg == I915_FENCE_REG_NONE) {
+               DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n");
                dev_priv->no_fbc_reason = FBC_NOT_TILED;
                goto out_disable;
        }
@@ -1774,6 +1811,44 @@ static void intel_update_fbc(struct drm_device *dev)
        if (in_dbg_master())
                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);
        return;
 
@@ -2021,7 +2096,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 
        /* no fb bound */
        if (!crtc->fb) {
-               DRM_DEBUG_KMS("No FB bound\n");
+               DRM_ERROR("No FB bound\n");
                return 0;
        }
 
@@ -2030,6 +2105,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        case 1:
                break;
        default:
+               DRM_ERROR("no plane for crtc\n");
                return -EINVAL;
        }
 
@@ -2039,6 +2115,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                                         NULL);
        if (ret != 0) {
                mutex_unlock(&dev->struct_mutex);
+               DRM_ERROR("pin & fence failed\n");
                return ret;
        }
 
@@ -2067,6 +2144,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        if (ret) {
                i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
                mutex_unlock(&dev->struct_mutex);
+               DRM_ERROR("failed to update base address\n");
                return ret;
        }
 
@@ -2787,14 +2865,18 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
                I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size);
        }
 
+       /*
+        * On ILK+ LUT must be loaded before the pipe is running but with
+        * clocks enabled
+        */
+       intel_crtc_load_lut(crtc);
+
        intel_enable_pipe(dev_priv, pipe, is_pch_port);
        intel_enable_plane(dev_priv, plane, pipe);
 
        if (is_pch_port)
                ironlake_pch_enable(crtc);
 
-       intel_crtc_load_lut(crtc);
-
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
        mutex_unlock(&dev->struct_mutex);
@@ -2820,9 +2902,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
 
        intel_disable_plane(dev_priv, plane, pipe);
 
-       if (dev_priv->cfb_plane == plane &&
-           dev_priv->display.disable_fbc)
-               dev_priv->display.disable_fbc(dev);
+       if (dev_priv->cfb_plane == plane)
+               intel_disable_fbc(dev);
 
        intel_disable_pipe(dev_priv, pipe);
 
@@ -2986,9 +3067,8 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        intel_crtc_dpms_overlay(intel_crtc, false);
        intel_crtc_update_cursor(crtc, false);
 
-       if (dev_priv->cfb_plane == plane &&
-           dev_priv->display.disable_fbc)
-               dev_priv->display.disable_fbc(dev);
+       if (dev_priv->cfb_plane == plane)
+               intel_disable_fbc(dev);
 
        intel_disable_plane(dev_priv, plane, pipe);
        intel_disable_pipe(dev_priv, pipe);
@@ -4393,7 +4473,8 @@ static void intel_update_watermarks(struct drm_device *dev)
 
 static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
 {
-       return dev_priv->lvds_use_ssc && i915_panel_use_ssc;
+       return dev_priv->lvds_use_ssc && i915_panel_use_ssc
+               && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
 }
 
 /**
@@ -6342,6 +6423,7 @@ static void intel_unpin_work_fn(struct work_struct *__work)
        drm_gem_object_unreference(&work->pending_flip_obj->base);
        drm_gem_object_unreference(&work->old_fb_obj->base);
 
+       intel_update_fbc(work->dev);
        mutex_unlock(&work->dev->struct_mutex);
        kfree(work);
 }
@@ -6706,6 +6788,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        if (ret)
                goto cleanup_pending;
 
+       intel_disable_fbc(dev);
        mutex_unlock(&dev->struct_mutex);
 
        trace_i915_flip_request(intel_crtc->plane, obj);
@@ -8062,6 +8145,15 @@ static void quirk_pipea_force (struct drm_device *dev)
        DRM_DEBUG_DRIVER("applying pipe a force quirk\n");
 }
 
+/*
+ * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason
+ */
+static void quirk_ssc_force_disable(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       dev_priv->quirks |= QUIRK_LVDS_SSC_DISABLE;
+}
+
 struct intel_quirk {
        int device;
        int subsystem_vendor;
@@ -8090,6 +8182,9 @@ struct intel_quirk intel_quirks[] = {
        /* 855 & before need to leave pipe A & dpll A up */
        { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
        { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+
+       /* Lenovo U160 cannot use SSC on LVDS */
+       { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
 };
 
 static void intel_init_quirks(struct drm_device *dev)
@@ -8217,8 +8312,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
                intel_increase_pllclock(crtc);
        }
 
-       if (dev_priv->display.disable_fbc)
-               dev_priv->display.disable_fbc(dev);
+       intel_disable_fbc(dev);
 
        if (IS_IRONLAKE_M(dev))
                ironlake_disable_drps(dev);
@@ -8235,6 +8329,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
        drm_irq_uninstall(dev);
        cancel_work_sync(&dev_priv->hotplug_work);
 
+       /* flush any delayed tasks or pending work */
+       flush_scheduled_work();
+
        /* Shut off idle work before the crtcs get freed. */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                intel_crtc = to_intel_crtc(crtc);