drm/i915: Add frame buffer compression support on Ironlake mobile

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>
This commit is contained in:
Zhao Yakui 2010-06-12 14:32:27 +08:00 committed by Eric Anholt
parent c936f44d1b
commit b52eb4dcab
6 changed files with 125 additions and 8 deletions

View File

@ -1257,7 +1257,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
drm_mm_put_block(compressed_fb); 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, compressed_llb = drm_mm_search_free(&dev_priv->vram, 4096,
4096, 0); 4096, 0);
if (!compressed_llb) { if (!compressed_llb) {
@ -1283,8 +1283,9 @@ static void i915_setup_compression(struct drm_device *dev, int size)
intel_disable_fbc(dev); intel_disable_fbc(dev);
dev_priv->compressed_fb = compressed_fb; dev_priv->compressed_fb = compressed_fb;
if (IS_IRONLAKE_M(dev))
if (IS_GM45(dev)) { I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
else if (IS_GM45(dev)) {
I915_WRITE(DPFC_CB_BASE, compressed_fb->start); I915_WRITE(DPFC_CB_BASE, compressed_fb->start);
} else { } else {
I915_WRITE(FBC_CFB_BASE, cfb_base); 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; 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); ll_base, size >> 20);
} }

View 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 = { static const struct intel_device_info intel_ironlake_m_info = {
.is_ironlake = 1, .is_mobile = 1, .is_i965g = 1, .is_i9xx = 1, .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, .has_hotplug = 1,
}; };

View 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 int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
extern void i8xx_disable_fbc(struct drm_device *dev); extern void i8xx_disable_fbc(struct drm_device *dev);
extern void g4x_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_disable_fbc(struct drm_device *dev);
extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval); extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
extern bool intel_fbc_enabled(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev);

View File

@ -530,6 +530,21 @@
#define DPFC_CHICKEN 0x3224 #define DPFC_CHICKEN 0x3224
#define DPFC_HT_MODIFY (1<<31) #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 * GPIO regs
*/ */
@ -2491,6 +2506,10 @@
#define ILK_VSDPFD_FULL (1<<21) #define ILK_VSDPFD_FULL (1<<21)
#define ILK_DSPCLK_GATE 0x42020 #define ILK_DSPCLK_GATE 0x42020
#define ILK_DPARB_CLK_GATE (1<<5) #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_ARB_CTL 0x45000
#define DISP_TILE_SURFACE_SWIZZLING (1<<13) #define DISP_TILE_SURFACE_SWIZZLING (1<<13)

View File

@ -602,7 +602,9 @@ void i915_save_display(struct drm_device *dev)
/* Only save FBC state on the platform that supports FBC */ /* Only save FBC state on the platform that supports FBC */
if (I915_HAS_FBC(dev)) { 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); dev_priv->saveDPFC_CB_BASE = I915_READ(DPFC_CB_BASE);
} else { } else {
dev_priv->saveFBC_CFB_BASE = I915_READ(FBC_CFB_BASE); 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*/ /* only restore FBC info on the platform that supports FBC*/
if (I915_HAS_FBC(dev)) { 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); g4x_disable_fbc(dev);
I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE); I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
} else { } else {

View File

@ -1123,6 +1123,67 @@ static bool g4x_fbc_enabled(struct drm_device *dev)
return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN; 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) bool intel_fbc_enabled(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; 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_crtc_load_lut(crtc);
intel_update_fbc(crtc, &crtc->mode);
break; break;
case DRM_MODE_DPMS_OFF: case DRM_MODE_DPMS_OFF:
DRM_DEBUG_KMS("crtc %d dpms off\n", pipe); 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); 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); i915_disable_vga(dev);
/* disable cpu pipe, disable after all planes disabled */ /* 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) | (I915_READ(DISP_ARB_CTL) |
DISP_FBC_WM_DIS)); 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; return;
} else if (IS_G4X(dev)) { } else if (IS_G4X(dev)) {
uint32_t dspclk_gate; uint32_t dspclk_gate;
@ -5530,7 +5617,11 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.dpms = i9xx_crtc_dpms; dev_priv->display.dpms = i9xx_crtc_dpms;
if (I915_HAS_FBC(dev)) { 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.fbc_enabled = g4x_fbc_enabled;
dev_priv->display.enable_fbc = g4x_enable_fbc; dev_priv->display.enable_fbc = g4x_enable_fbc;
dev_priv->display.disable_fbc = g4x_disable_fbc; dev_priv->display.disable_fbc = g4x_disable_fbc;