mirror of
https://github.com/torvalds/linux.git
synced 2024-12-18 00:53:40 +00:00
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
@ -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 */
|
||||
|
||||
|
@ -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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user