drm/i915: Add the support of memory self-refresh on Ironlake

Update the self-refresh watermark for display plane/cursor and enable
the memory self-refresh on Ironlake. The watermark is also updated for
the active display plane.

More than 1W idle power is saved on one Ironlake laptop after enabling
memory self-refresh.

Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
This commit is contained in:
Zhenyu Wang 2010-04-01 13:07:53 +08:00 committed by Eric Anholt
parent d4294342fd
commit 7f8a85698f
2 changed files with 201 additions and 3 deletions

View File

@ -2030,6 +2030,43 @@
#define PINEVIEW_CURSOR_DFT_WM 0
#define PINEVIEW_CURSOR_GUARD_WM 5
/* define the Watermark register on Ironlake */
#define WM0_PIPEA_ILK 0x45100
#define WM0_PIPE_PLANE_MASK (0x7f<<16)
#define WM0_PIPE_PLANE_SHIFT 16
#define WM0_PIPE_SPRITE_MASK (0x3f<<8)
#define WM0_PIPE_SPRITE_SHIFT 8
#define WM0_PIPE_CURSOR_MASK (0x1f)
#define WM0_PIPEB_ILK 0x45104
#define WM1_LP_ILK 0x45108
#define WM1_LP_SR_EN (1<<31)
#define WM1_LP_LATENCY_SHIFT 24
#define WM1_LP_LATENCY_MASK (0x7f<<24)
#define WM1_LP_SR_MASK (0x1ff<<8)
#define WM1_LP_SR_SHIFT 8
#define WM1_LP_CURSOR_MASK (0x3f)
/* Memory latency timer register */
#define MLTR_ILK 0x11222
/* the unit of memory self-refresh latency time is 0.5us */
#define ILK_SRLT_MASK 0x3f
/* define the fifo size on Ironlake */
#define ILK_DISPLAY_FIFO 128
#define ILK_DISPLAY_MAXWM 64
#define ILK_DISPLAY_DFTWM 8
#define ILK_DISPLAY_SR_FIFO 512
#define ILK_DISPLAY_MAX_SRWM 0x1ff
#define ILK_DISPLAY_DFT_SRWM 0x3f
#define ILK_CURSOR_SR_FIFO 64
#define ILK_CURSOR_MAX_SRWM 0x3f
#define ILK_CURSOR_DFT_SRWM 8
#define ILK_FIFO_LINE_SIZE 64
/*
* The two pipe frame counter registers are not synchronized, so
* reading a stable value is somewhat tricky. The following code
@ -2310,8 +2347,15 @@
#define GTIIR 0x44018
#define GTIER 0x4401c
#define ILK_DISPLAY_CHICKEN2 0x42004
#define ILK_DPARB_GATE (1<<22)
#define ILK_VSDPFD_FULL (1<<21)
#define ILK_DSPCLK_GATE 0x42020
#define ILK_DPARB_CLK_GATE (1<<5)
#define DISP_ARB_CTL 0x45000
#define DISP_TILE_SURFACE_SWIZZLING (1<<13)
#define DISP_FBC_WM_DIS (1<<15)
/* PCH */

View File

@ -2527,6 +2527,30 @@ static struct intel_watermark_params i830_wm_info = {
I830_FIFO_LINE_SIZE
};
static struct intel_watermark_params ironlake_display_wm_info = {
ILK_DISPLAY_FIFO,
ILK_DISPLAY_MAXWM,
ILK_DISPLAY_DFTWM,
2,
ILK_FIFO_LINE_SIZE
};
static struct intel_watermark_params ironlake_display_srwm_info = {
ILK_DISPLAY_SR_FIFO,
ILK_DISPLAY_MAX_SRWM,
ILK_DISPLAY_DFT_SRWM,
2,
ILK_FIFO_LINE_SIZE
};
static struct intel_watermark_params ironlake_cursor_srwm_info = {
ILK_CURSOR_SR_FIFO,
ILK_CURSOR_MAX_SRWM,
ILK_CURSOR_DFT_SRWM,
2,
ILK_FIFO_LINE_SIZE
};
/**
* intel_calculate_wm - calculate watermark level
* @clock_in_khz: pixel clock
@ -3014,6 +3038,108 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused,
I915_WRITE(FW_BLC, fwater_lo);
}
#define ILK_LP0_PLANE_LATENCY 700
static void ironlake_update_wm(struct drm_device *dev, int planea_clock,
int planeb_clock, int sr_hdisplay, int pixel_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
int sr_wm, cursor_wm;
unsigned long line_time_us;
int sr_clock, entries_required;
u32 reg_value;
/* Calculate and update the watermark for plane A */
if (planea_clock) {
entries_required = ((planea_clock / 1000) * pixel_size *
ILK_LP0_PLANE_LATENCY) / 1000;
entries_required = DIV_ROUND_UP(entries_required,
ironlake_display_wm_info.cacheline_size);
planea_wm = entries_required +
ironlake_display_wm_info.guard_size;
if (planea_wm > (int)ironlake_display_wm_info.max_wm)
planea_wm = ironlake_display_wm_info.max_wm;
cursora_wm = 16;
reg_value = I915_READ(WM0_PIPEA_ILK);
reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
reg_value |= (planea_wm << WM0_PIPE_PLANE_SHIFT) |
(cursora_wm & WM0_PIPE_CURSOR_MASK);
I915_WRITE(WM0_PIPEA_ILK, reg_value);
DRM_DEBUG_KMS("FIFO watermarks For pipe A - plane %d, "
"cursor: %d\n", planea_wm, cursora_wm);
}
/* Calculate and update the watermark for plane B */
if (planeb_clock) {
entries_required = ((planeb_clock / 1000) * pixel_size *
ILK_LP0_PLANE_LATENCY) / 1000;
entries_required = DIV_ROUND_UP(entries_required,
ironlake_display_wm_info.cacheline_size);
planeb_wm = entries_required +
ironlake_display_wm_info.guard_size;
if (planeb_wm > (int)ironlake_display_wm_info.max_wm)
planeb_wm = ironlake_display_wm_info.max_wm;
cursorb_wm = 16;
reg_value = I915_READ(WM0_PIPEB_ILK);
reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
reg_value |= (planeb_wm << WM0_PIPE_PLANE_SHIFT) |
(cursorb_wm & WM0_PIPE_CURSOR_MASK);
I915_WRITE(WM0_PIPEB_ILK, reg_value);
DRM_DEBUG_KMS("FIFO watermarks For pipe B - plane %d, "
"cursor: %d\n", planeb_wm, cursorb_wm);
}
/*
* Calculate and update the self-refresh watermark only when one
* display plane is used.
*/
if (!planea_clock || !planeb_clock) {
int line_count;
/* Read the self-refresh latency. The unit is 0.5us */
int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK;
sr_clock = planea_clock ? planea_clock : planeb_clock;
line_time_us = ((sr_hdisplay * 1000) / sr_clock);
/* Use ns/us then divide to preserve precision */
line_count = ((ilk_sr_latency * 500) / line_time_us + 1000)
/ 1000;
/* calculate the self-refresh watermark for display plane */
entries_required = line_count * sr_hdisplay * pixel_size;
entries_required = DIV_ROUND_UP(entries_required,
ironlake_display_srwm_info.cacheline_size);
sr_wm = entries_required +
ironlake_display_srwm_info.guard_size;
/* calculate the self-refresh watermark for display cursor */
entries_required = line_count * pixel_size * 64;
entries_required = DIV_ROUND_UP(entries_required,
ironlake_cursor_srwm_info.cacheline_size);
cursor_wm = entries_required +
ironlake_cursor_srwm_info.guard_size;
/* configure watermark and enable self-refresh */
reg_value = I915_READ(WM1_LP_ILK);
reg_value &= ~(WM1_LP_LATENCY_MASK | WM1_LP_SR_MASK |
WM1_LP_CURSOR_MASK);
reg_value |= WM1_LP_SR_EN |
(ilk_sr_latency << WM1_LP_LATENCY_SHIFT) |
(sr_wm << WM1_LP_SR_SHIFT) | cursor_wm;
I915_WRITE(WM1_LP_ILK, reg_value);
DRM_DEBUG_KMS("self-refresh watermark: display plane %d "
"cursor %d\n", sr_wm, cursor_wm);
} else {
/* Turn off self refresh if both pipes are enabled */
I915_WRITE(WM1_LP_ILK, I915_READ(WM1_LP_ILK) & ~WM1_LP_SR_EN);
}
}
/**
* intel_update_watermarks - update FIFO watermark values based on current modes
*
@ -4973,6 +5099,25 @@ void intel_init_clock_gating(struct drm_device *dev)
}
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
/*
* According to the spec the following bits should be set in
* order to enable memory self-refresh
* The bit 22/21 of 0x42004
* The bit 5 of 0x42020
* The bit 15 of 0x45000
*/
if (IS_IRONLAKE(dev)) {
I915_WRITE(ILK_DISPLAY_CHICKEN2,
(I915_READ(ILK_DISPLAY_CHICKEN2) |
ILK_DPARB_GATE | ILK_VSDPFD_FULL));
I915_WRITE(ILK_DSPCLK_GATE,
(I915_READ(ILK_DSPCLK_GATE) |
ILK_DPARB_CLK_GATE));
I915_WRITE(DISP_ARB_CTL,
(I915_READ(DISP_ARB_CTL) |
DISP_FBC_WM_DIS));
}
return;
} else if (IS_G4X(dev)) {
uint32_t dspclk_gate;
@ -5088,9 +5233,18 @@ static void intel_init_display(struct drm_device *dev)
i830_get_display_clock_speed;
/* For FIFO watermark updates */
if (HAS_PCH_SPLIT(dev))
dev_priv->display.update_wm = NULL;
else if (IS_PINEVIEW(dev)) {
if (HAS_PCH_SPLIT(dev)) {
if (IS_IRONLAKE(dev)) {
if (I915_READ(MLTR_ILK) & ILK_SRLT_MASK)
dev_priv->display.update_wm = ironlake_update_wm;
else {
DRM_DEBUG_KMS("Failed to get proper latency. "
"Disable CxSR\n");
dev_priv->display.update_wm = NULL;
}
} else
dev_priv->display.update_wm = NULL;
} else if (IS_PINEVIEW(dev)) {
if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
dev_priv->fsb_freq,
dev_priv->mem_freq)) {