drm/i915: Add frame buffer compression support on Ironlake mobile
authorZhao Yakui <yakui.zhao@intel.com>
Sat, 12 Jun 2010 06:32:27 +0000 (14:32 +0800)
committerEric Anholt <eric@anholt.net>
Mon, 2 Aug 2010 02:03:44 +0000 (19:03 -0700)
About 0.2W power can be saved on one HP laptop.

Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_display.c

index 92898035845d606e134865b6d9348d187909b979..9ddb7b5ac057ff1207aa90a73f97f7182ebaca47 100644 (file)
@@ -1257,7 +1257,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
                drm_mm_put_block(compressed_fb);
        }
 
-       if (!IS_GM45(dev)) {
+       if (!(IS_GM45(dev) || IS_IRONLAKE_M(dev))) {
                compressed_llb = drm_mm_search_free(&dev_priv->vram, 4096,
                                                    4096, 0);
                if (!compressed_llb) {
@@ -1283,8 +1283,9 @@ static void i915_setup_compression(struct drm_device *dev, int size)
 
        intel_disable_fbc(dev);
        dev_priv->compressed_fb = compressed_fb;
-
-       if (IS_GM45(dev)) {
+       if (IS_IRONLAKE_M(dev))
+               I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
+       else if (IS_GM45(dev)) {
                I915_WRITE(DPFC_CB_BASE, compressed_fb->start);
        } else {
                I915_WRITE(FBC_CFB_BASE, cfb_base);
@@ -1292,7 +1293,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
                dev_priv->compressed_llb = compressed_llb;
        }
 
-       DRM_DEBUG("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", cfb_base,
+       DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", cfb_base,
                  ll_base, size >> 20);
 }
 
index 65d3f3e8475b2e5a81bc0f3f14e20ad28b86dc6b..b5694d24b5419f669c859a68b46f634e0d6f029f 100644 (file)
@@ -134,7 +134,7 @@ static const struct intel_device_info intel_ironlake_d_info = {
 
 static const struct intel_device_info intel_ironlake_m_info = {
        .is_ironlake = 1, .is_mobile = 1, .is_i965g = 1, .is_i9xx = 1,
-       .need_gfx_hws = 1, .has_rc6 = 1,
+       .need_gfx_hws = 1, .has_fbc = 1, .has_rc6 = 1,
        .has_hotplug = 1,
 };
 
index 7fba852455f47ed2ac8dc6303179d00e4c7c65bb..f5636d8da96d9467f0d0c01343f8fe16f0e5158f 100644 (file)
@@ -1042,6 +1042,7 @@ extern void intel_modeset_cleanup(struct drm_device *dev);
 extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
 extern void i8xx_disable_fbc(struct drm_device *dev);
 extern void g4x_disable_fbc(struct drm_device *dev);
+extern void ironlake_disable_fbc(struct drm_device *dev);
 extern void intel_disable_fbc(struct drm_device *dev);
 extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
 extern bool intel_fbc_enabled(struct drm_device *dev);
index d0ccfa0c72def8a0177fcd70c39d6a776cf8ef89..e2a6f687d8b021e94c60589ad3a5991eeeed0209 100644 (file)
 #define DPFC_CHICKEN           0x3224
 #define   DPFC_HT_MODIFY       (1<<31)
 
+/* Framebuffer compression for Ironlake */
+#define ILK_DPFC_CB_BASE       0x43200
+#define ILK_DPFC_CONTROL       0x43208
+/* The bit 28-8 is reserved */
+#define   DPFC_RESERVED                (0x1FFFFF00)
+#define ILK_DPFC_RECOMP_CTL    0x4320c
+#define ILK_DPFC_STATUS                0x43210
+#define ILK_DPFC_FENCE_YOFF    0x43218
+#define ILK_DPFC_CHICKEN       0x43224
+#define ILK_FBC_RT_BASE                0x2128
+#define   ILK_FBC_RT_VALID     (1<<0)
+
+#define ILK_DISPLAY_CHICKEN1   0x42000
+#define   ILK_FBCQ_DIS         (1<<22)
+
 /*
  * GPIO regs
  */
 #define  ILK_VSDPFD_FULL       (1<<21)
 #define ILK_DSPCLK_GATE                0x42020
 #define  ILK_DPARB_CLK_GATE    (1<<5)
+/* According to spec this bit 7/8/9 of 0x42020 should be set to enable FBC */
+#define   ILK_CLK_FBC          (1<<7)
+#define   ILK_DPFC_DIS1                (1<<8)
+#define   ILK_DPFC_DIS2                (1<<9)
 
 #define DISP_ARB_CTL   0x45000
 #define  DISP_TILE_SURFACE_SWIZZLING   (1<<13)
index 60a5800fba6e33c6638dc7fc455a33b041a3df8d..6e2025274db500e9d11c5ace422000196e457dd6 100644 (file)
@@ -602,7 +602,9 @@ void i915_save_display(struct drm_device *dev)
 
        /* Only save FBC state on the platform that supports FBC */
        if (I915_HAS_FBC(dev)) {
-               if (IS_GM45(dev)) {
+               if (IS_IRONLAKE_M(dev)) {
+                       dev_priv->saveDPFC_CB_BASE = I915_READ(ILK_DPFC_CB_BASE);
+               } else if (IS_GM45(dev)) {
                        dev_priv->saveDPFC_CB_BASE = I915_READ(DPFC_CB_BASE);
                } else {
                        dev_priv->saveFBC_CFB_BASE = I915_READ(FBC_CFB_BASE);
@@ -706,7 +708,10 @@ void i915_restore_display(struct drm_device *dev)
 
        /* only restore FBC info on the platform that supports FBC*/
        if (I915_HAS_FBC(dev)) {
-               if (IS_GM45(dev)) {
+               if (IS_IRONLAKE_M(dev)) {
+                       ironlake_disable_fbc(dev);
+                       I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
+               } else if (IS_GM45(dev)) {
                        g4x_disable_fbc(dev);
                        I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
                } else {
index cca6ce80d464c0403de04adb393bcdcbc6f35e55..f2f812e15e61c80c13f2b7834fbab6881c018e67 100644 (file)
@@ -1123,6 +1123,67 @@ static bool g4x_fbc_enabled(struct drm_device *dev)
        return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN;
 }
 
+static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_framebuffer *fb = crtc->fb;
+       struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+       struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int plane = (intel_crtc->plane == 0) ? DPFC_CTL_PLANEA :
+                                              DPFC_CTL_PLANEB;
+       unsigned long stall_watermark = 200;
+       u32 dpfc_ctl;
+
+       dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
+       dev_priv->cfb_fence = obj_priv->fence_reg;
+       dev_priv->cfb_plane = intel_crtc->plane;
+
+       dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
+       dpfc_ctl &= DPFC_RESERVED;
+       dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
+       if (obj_priv->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);
+       }
+
+       I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
+       I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
+                  (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
+                  (interval << DPFC_RECOMP_TIMER_COUNT_SHIFT));
+       I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y);
+       I915_WRITE(ILK_FBC_RT_BASE, obj_priv->gtt_offset | ILK_FBC_RT_VALID);
+       /* enable it... */
+       I915_WRITE(ILK_DPFC_CONTROL, I915_READ(ILK_DPFC_CONTROL) |
+                  DPFC_CTL_EN);
+
+       DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
+}
+
+void ironlake_disable_fbc(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 dpfc_ctl;
+
+       /* Disable compression */
+       dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
+       dpfc_ctl &= ~DPFC_CTL_EN;
+       I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
+       intel_wait_for_vblank(dev);
+
+       DRM_DEBUG_KMS("disabled FBC\n");
+}
+
+static bool ironlake_fbc_enabled(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN;
+}
+
 bool intel_fbc_enabled(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1966,6 +2027,8 @@ static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                intel_crtc_load_lut(crtc);
 
+               intel_update_fbc(crtc, &crtc->mode);
+
        break;
        case DRM_MODE_DPMS_OFF:
                DRM_DEBUG_KMS("crtc %d dpms off\n", pipe);
@@ -1980,6 +2043,10 @@ static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
                        I915_READ(dspbase_reg);
                }
 
+               if (dev_priv->cfb_plane == plane &&
+                   dev_priv->display.disable_fbc)
+                       dev_priv->display.disable_fbc(dev);
+
                i915_disable_vga(dev);
 
                /* disable cpu pipe, disable after all planes disabled */
@@ -5452,6 +5519,26 @@ void intel_init_clock_gating(struct drm_device *dev)
                                        (I915_READ(DISP_ARB_CTL) |
                                                DISP_FBC_WM_DIS));
                }
+               /*
+                * Based on the document from hardware guys the following bits
+                * should be set unconditionally in order to enable FBC.
+                * The bit 22 of 0x42000
+                * The bit 22 of 0x42004
+                * The bit 7,8,9 of 0x42020.
+                */
+               if (IS_IRONLAKE_M(dev)) {
+                       I915_WRITE(ILK_DISPLAY_CHICKEN1,
+                                  I915_READ(ILK_DISPLAY_CHICKEN1) |
+                                  ILK_FBCQ_DIS);
+                       I915_WRITE(ILK_DISPLAY_CHICKEN2,
+                                  I915_READ(ILK_DISPLAY_CHICKEN2) |
+                                  ILK_DPARB_GATE);
+                       I915_WRITE(ILK_DSPCLK_GATE,
+                                  I915_READ(ILK_DSPCLK_GATE) |
+                                  ILK_DPFC_DIS1 |
+                                  ILK_DPFC_DIS2 |
+                                  ILK_CLK_FBC);
+               }
                return;
        } else if (IS_G4X(dev)) {
                uint32_t dspclk_gate;
@@ -5530,7 +5617,11 @@ static void intel_init_display(struct drm_device *dev)
                dev_priv->display.dpms = i9xx_crtc_dpms;
 
        if (I915_HAS_FBC(dev)) {
-               if (IS_GM45(dev)) {
+               if (IS_IRONLAKE_M(dev)) {
+                       dev_priv->display.fbc_enabled = ironlake_fbc_enabled;
+                       dev_priv->display.enable_fbc = ironlake_enable_fbc;
+                       dev_priv->display.disable_fbc = ironlake_disable_fbc;
+               } else if (IS_GM45(dev)) {
                        dev_priv->display.fbc_enabled = g4x_fbc_enabled;
                        dev_priv->display.enable_fbc = g4x_enable_fbc;
                        dev_priv->display.disable_fbc = g4x_disable_fbc;