drm/i915: implement IPS feature
authorPaulo Zanoni <paulo.r.zanoni@intel.com>
Fri, 31 May 2013 19:33:22 +0000 (16:33 -0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Fri, 31 May 2013 19:39:55 +0000 (21:39 +0200)
Intermediate Pixel Storage is a feature that should reduce the number
of times the display engine wakes up memory to read pixels, so it
should allow deeper PC states. IPS can only be enabled on ULT pipe A
with 8:8:8 pipe pixel formats.

With eDP 1920x1080 and correct watermarks but without FBC this moves
my PC7 residency from 2.5% to around 38%.

v2: - It's tied to pipe A, not port A
    - Add pipe_config support (Chris)
    - Add some assertions (Chris)
    - Rebase against latest dinq
v3: - Don't ever set ips_enabled to false (Daniel)
    - Only check for ips_enabled at hsw_disable_ips (Daniel)
v4: - Add hsw_compute_ips_config (Daniel)
    - Use the new dump_pipe_config (Daniel)

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h

index 89fac19d3249eee8df570b4df919e4823e8b199d..5a593d20036cd204d2e71839410862c88e6fdaaf 100644 (file)
 /* Framebuffer compression for Ivybridge */
 #define IVB_FBC_RT_BASE                        0x7020
 
+#define IPS_CTL                0x43408
+#define   IPS_ENABLE   (1 << 31)
 
 #define _HSW_PIPE_SLICE_CHICKEN_1_A    0x420B0
 #define _HSW_PIPE_SLICE_CHICKEN_1_B    0x420B4
 #define _LGC_PALETTE_B           0x4a800
 #define LGC_PALETTE(pipe) _PIPE(pipe, _LGC_PALETTE_A, _LGC_PALETTE_B)
 
+#define _GAMMA_MODE_A          0x4a480
+#define _GAMMA_MODE_B          0x4ac80
+#define GAMMA_MODE(pipe) _PIPE(pipe, _GAMMA_MODE_A, _GAMMA_MODE_B)
+#define GAMMA_MODE_MODE_MASK   (3 << 0)
+#define GAMMA_MODE_MODE_8bit   (0 << 0)
+#define GAMMA_MODE_MODE_10bit  (1 << 0)
+#define GAMMA_MODE_MODE_12bit  (2 << 0)
+#define GAMMA_MODE_MODE_SPLIT  (3 << 0)
+
 /* interrupts */
 #define DE_MASTER_IRQ_CONTROL   (1 << 31)
 #define DE_SPRITEB_FLIP_DONE    (1 << 29)
index 2e2c6a370f52b95b5213d4c65a3d991c9aa81a4d..acfc6a164a595b4699150205f176c09dd22d1f4c 100644 (file)
@@ -3242,6 +3242,42 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        intel_wait_for_vblank(dev, intel_crtc->pipe);
 }
 
+/* IPS only exists on ULT machines and is tied to pipe A. */
+static bool hsw_crtc_supports_ips(struct intel_crtc *crtc)
+{
+       return IS_ULT(crtc->base.dev) && crtc->pipe == PIPE_A;
+}
+
+static void hsw_enable_ips(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
+       if (!crtc->config.ips_enabled)
+               return;
+
+       /* We can only enable IPS after we enable a plane and wait for a vblank.
+        * We guarantee that the plane is enabled by calling intel_enable_ips
+        * only after intel_enable_plane. And intel_enable_plane already waits
+        * for a vblank, so all we need to do here is to enable the IPS bit. */
+       assert_plane_enabled(dev_priv, crtc->plane);
+       I915_WRITE(IPS_CTL, IPS_ENABLE);
+}
+
+static void hsw_disable_ips(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!crtc->config.ips_enabled)
+               return;
+
+       assert_plane_enabled(dev_priv, crtc->plane);
+       I915_WRITE(IPS_CTL, 0);
+
+       /* We need to wait for a vblank before we can disable the plane. */
+       intel_wait_for_vblank(dev, crtc->pipe);
+}
+
 static void haswell_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -3289,6 +3325,8 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
                          intel_crtc->config.has_pch_encoder);
        intel_enable_plane(dev_priv, plane, pipe);
 
+       hsw_enable_ips(intel_crtc);
+
        if (intel_crtc->config.has_pch_encoder)
                lpt_pch_enable(crtc);
 
@@ -3431,6 +3469,8 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        if (dev_priv->cfb_plane == plane)
                intel_disable_fbc(dev);
 
+       hsw_disable_ips(intel_crtc);
+
        intel_disable_plane(dev_priv, plane, pipe);
 
        if (intel_crtc->config.has_pch_encoder)
@@ -3991,11 +4031,19 @@ retry:
        return setup_ok ? 0 : -EINVAL;
 }
 
+static void hsw_compute_ips_config(struct intel_crtc *crtc,
+                                  struct intel_crtc_config *pipe_config)
+{
+       pipe_config->ips_enabled = hsw_crtc_supports_ips(crtc) &&
+                                  pipe_config->pipe_bpp == 24;
+}
+
 static int intel_crtc_compute_config(struct drm_crtc *crtc,
                                     struct intel_crtc_config *pipe_config)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
        if (HAS_PCH_SPLIT(dev)) {
                /* FDI link clock is fixed at 2.7G */
@@ -4025,8 +4073,11 @@ static int intel_crtc_compute_config(struct drm_crtc *crtc,
                pipe_config->pipe_bpp = 8*3;
        }
 
+       if (IS_HASWELL(dev))
+               hsw_compute_ips_config(intel_crtc, pipe_config);
+
        if (pipe_config->has_pch_encoder)
-               return ironlake_fdi_compute_config(to_intel_crtc(crtc), pipe_config);
+               return ironlake_fdi_compute_config(intel_crtc, pipe_config);
 
        return 0;
 }
@@ -5932,6 +5983,9 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
        if (intel_display_power_enabled(dev, pfit_domain))
                ironlake_get_pfit_config(crtc, pipe_config);
 
+       pipe_config->ips_enabled = hsw_crtc_supports_ips(crtc) &&
+                                  (I915_READ(IPS_CTL) & IPS_ENABLE);
+
        return true;
 }
 
@@ -6236,8 +6290,10 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       int palreg = PALETTE(intel_crtc->pipe);
+       enum pipe pipe = intel_crtc->pipe;
+       int palreg = PALETTE(pipe);
        int i;
+       bool reenable_ips = false;
 
        /* The clocks have to be on to load the palette. */
        if (!crtc->enabled || !intel_crtc->active)
@@ -6245,7 +6301,17 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
 
        /* use legacy palette for Ironlake */
        if (HAS_PCH_SPLIT(dev))
-               palreg = LGC_PALETTE(intel_crtc->pipe);
+               palreg = LGC_PALETTE(pipe);
+
+       /* Workaround : Do not read or write the pipe palette/gamma data while
+        * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled.
+        */
+       if (intel_crtc->config.ips_enabled &&
+           ((I915_READ(GAMMA_MODE(pipe)) & GAMMA_MODE_MODE_MASK) ==
+            GAMMA_MODE_MODE_SPLIT)) {
+               hsw_disable_ips(intel_crtc);
+               reenable_ips = true;
+       }
 
        for (i = 0; i < 256; i++) {
                I915_WRITE(palreg + 4 * i,
@@ -6253,6 +6319,9 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
                           (intel_crtc->lut_g[i] << 8) |
                           intel_crtc->lut_b[i]);
        }
+
+       if (reenable_ips)
+               hsw_enable_ips(intel_crtc);
 }
 
 static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -7680,6 +7749,7 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
        DRM_DEBUG_KMS("pch pfit: pos: 0x%08x, size: 0x%08x\n",
                      pipe_config->pch_pfit.pos,
                      pipe_config->pch_pfit.size);
+       DRM_DEBUG_KMS("ips: %i\n", pipe_config->ips_enabled);
 }
 
 static struct intel_crtc_config *
@@ -7996,6 +8066,8 @@ intel_pipe_config_compare(struct drm_device *dev,
        PIPE_CONF_CHECK_I(pch_pfit.pos);
        PIPE_CONF_CHECK_I(pch_pfit.size);
 
+       PIPE_CONF_CHECK_I(ips_enabled);
+
 #undef PIPE_CONF_CHECK_I
 #undef PIPE_CONF_CHECK_FLAGS
 
index bb4c50b570850a72f186a45f4449af080c8cbf41..fdf6303be0a96951057d6fa6eb1e9f89289d58d4 100644 (file)
@@ -268,6 +268,8 @@ struct intel_crtc_config {
        /* FDI configuration, only valid if has_pch_encoder is set. */
        int fdi_lanes;
        struct intel_link_m_n fdi_m_n;
+
+       bool ips_enabled;
 };
 
 struct intel_crtc {