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:
parent
d4294342fd
commit
7f8a85698f
2 changed files with 201 additions and 3 deletions
|
@ -2030,6 +2030,43 @@
|
||||||
#define PINEVIEW_CURSOR_DFT_WM 0
|
#define PINEVIEW_CURSOR_DFT_WM 0
|
||||||
#define PINEVIEW_CURSOR_GUARD_WM 5
|
#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
|
* The two pipe frame counter registers are not synchronized, so
|
||||||
* reading a stable value is somewhat tricky. The following code
|
* reading a stable value is somewhat tricky. The following code
|
||||||
|
@ -2310,8 +2347,15 @@
|
||||||
#define GTIIR 0x44018
|
#define GTIIR 0x44018
|
||||||
#define GTIER 0x4401c
|
#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_ARB_CTL 0x45000
|
||||||
#define DISP_TILE_SURFACE_SWIZZLING (1<<13)
|
#define DISP_TILE_SURFACE_SWIZZLING (1<<13)
|
||||||
|
#define DISP_FBC_WM_DIS (1<<15)
|
||||||
|
|
||||||
/* PCH */
|
/* PCH */
|
||||||
|
|
||||||
|
|
|
@ -2527,6 +2527,30 @@ static struct intel_watermark_params i830_wm_info = {
|
||||||
I830_FIFO_LINE_SIZE
|
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
|
* intel_calculate_wm - calculate watermark level
|
||||||
* @clock_in_khz: pixel clock
|
* @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);
|
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
|
* 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);
|
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;
|
return;
|
||||||
} else if (IS_G4X(dev)) {
|
} else if (IS_G4X(dev)) {
|
||||||
uint32_t dspclk_gate;
|
uint32_t dspclk_gate;
|
||||||
|
@ -5088,9 +5233,18 @@ static void intel_init_display(struct drm_device *dev)
|
||||||
i830_get_display_clock_speed;
|
i830_get_display_clock_speed;
|
||||||
|
|
||||||
/* For FIFO watermark updates */
|
/* For FIFO watermark updates */
|
||||||
if (HAS_PCH_SPLIT(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;
|
dev_priv->display.update_wm = NULL;
|
||||||
else if (IS_PINEVIEW(dev)) {
|
}
|
||||||
|
} else
|
||||||
|
dev_priv->display.update_wm = NULL;
|
||||||
|
} else if (IS_PINEVIEW(dev)) {
|
||||||
if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
|
if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
|
||||||
dev_priv->fsb_freq,
|
dev_priv->fsb_freq,
|
||||||
dev_priv->mem_freq)) {
|
dev_priv->mem_freq)) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue