[Intel-gfx] [PATCH]DRM i915: IGD big FIFO support
Shaohua Li
shaohua.li at intel.com
Mon May 18 04:44:52 CEST 2009
Big FIFO is a feature to put memory into self-refresh mode when CPU
enters C3+ state. Gfx has a FIFO to buffer memory access, when the
watermark of the FIFO is under threshold, Gfx doesn't need access
memory, so at that time memory can be put into self-refresh mode.
The watermark calculation is based on CPU C3 state exit latency. If
watermark is wrong, when CPU enters C3+, display will be broken or
flicker.
I had a power measurement about the feature:
environment: 1920x1400 display, Atom CPU with C4 enabled, system FSB
is 667 and memory is DDR2 667. Launch X, and gives system several
minutes to settle down, then test the power of the whole system in idle time:
without big fifo, idle power is 19.8w
with it, idle power is 18.6w
The patch doesn't enable HPLL off for CxSR. Last time I heard it's broken
in current IGD chip, if it works, then I'll add it later.
Signed-off-by: Shaohua Li <shaohua.li at intel.com>
---
drivers/gpu/drm/i915/i915_drv.h | 2
drivers/gpu/drm/i915/i915_reg.h | 4
drivers/gpu/drm/i915/intel_display.c | 245 ++++++++++++++++++++++++++++++++++-
3 files changed, 250 insertions(+), 1 deletion(-)
Index: linux/drivers/gpu/drm/i915/i915_drv.h
===================================================================
--- linux.orig/drivers/gpu/drm/i915/i915_drv.h 2009-05-14 09:13:17.000000000 +0800
+++ linux/drivers/gpu/drm/i915/i915_drv.h 2009-05-18 10:12:43.000000000 +0800
@@ -194,6 +194,8 @@ typedef struct drm_i915_private {
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
+ bool cxsr_initialized;
+
/* Register state */
u8 saveLBB;
u32 saveDSPACNTR;
Index: linux/drivers/gpu/drm/i915/intel_display.c
===================================================================
--- linux.orig/drivers/gpu/drm/i915/intel_display.c 2009-05-15 11:35:45.000000000 +0800
+++ linux/drivers/gpu/drm/i915/intel_display.c 2009-05-18 10:29:16.000000000 +0800
@@ -786,7 +786,7 @@ intel_pipe_set_base(struct drm_crtc *crt
}
-
+static void igd_set_cxsr(struct drm_device *dev, bool on);
/**
* Sets the power management mode of the pipe and plane.
*
@@ -848,8 +848,12 @@ static void intel_crtc_dpms(struct drm_c
/* Give the overlay scaler a chance to enable if it's on this pipe */
//intel_crtc_dpms_video(crtc, true); TODO
+ if (crtc->enabled)
+ igd_set_cxsr(dev, true);
break;
case DRM_MODE_DPMS_OFF:
+ if (crtc->enabled)
+ igd_set_cxsr(dev, false);
/* Give the overlay scaler a chance to disable if it's on this pipe */
//intel_crtc_dpms_video(crtc, FALSE); TODO
@@ -1030,6 +1034,243 @@ static int intel_panel_fitter_pipe (stru
return 1;
}
+struct cxsr_latency {
+ int is_desktop;
+ unsigned long fsb;
+ unsigned long mem_freq;
+ unsigned long display_sr;
+ unsigned long display_hpll_disable;
+ unsigned long cursor_sr;
+ unsigned long cursor_hpll_disable;
+};
+
+static struct cxsr_latency igd_cxsr_latency[] = {
+ {1, 800, 400, 3382, 33382, 3983, 33983}, // DDR2-400 SC
+ {1, 800, 667, 3354, 33354, 3807, 33807}, // DDR2-667 SC
+ {1, 800, 800, 3347, 33347, 3763, 33763}, // DDR2-800 SC
+
+ {1, 667, 400, 3400, 33400, 4021, 34021}, // DDR2-400 SC
+ {1, 667, 667, 3372, 33372, 3845, 33845}, // DDR2-667 SC
+ {1, 667, 800, 3386, 33386, 3822, 33822}, // DDR2-800 SC
+
+ {1, 400, 400, 3472, 33472, 4173, 34173}, // DDR2-400 SC
+ {1, 400, 667, 3443, 33443, 3996, 33996}, // DDR2-667 SC
+ {1, 400, 800, 3430, 33430, 3946, 33946}, // DDR2-800 SC
+
+ {0, 800, 400, 3438, 33438, 4065, 34065}, // DDR2-400 SC
+ {0, 800, 667, 3410, 33410, 3889, 33889}, // DDR2-667 SC
+ {0, 800, 800, 3403, 33403, 3845, 33845}, // DDR2-800 SC
+
+ {0, 667, 400, 3456, 33456, 4103, 34106}, // DDR2-400 SC
+ {0, 667, 667, 3428, 33428, 3927, 33927}, // DDR2-667 SC
+ {0, 667, 800, 3443, 33443, 3905, 33905}, // DDR2-800 SC
+
+ {0, 400, 400, 3528, 33528, 4255, 34255}, // DDR2-400 SC
+ {0, 400, 667, 3500, 33500, 4079, 34079}, // DDR2-667 SC
+ {0, 400, 800, 3487, 33487, 4029, 34029}, // DDR2-800 SC
+};
+
+struct watermark_igd_parameter {
+ unsigned long fifo_size;
+ unsigned long max_wm;
+ unsigned long default_wm;
+ unsigned long guard_size;
+};
+
+#define IGD_DISPLAY_FIFO 512 /* in 64byte unit */
+#define IGD_MAX_WM 0x1ff
+#define IGD_DFT_WM 0x3f
+#define IGD_DFT_HPLLOFF_WM 0
+#define IGD_GUARD_WM 10
+#define IGD_CURSOR_FIFO 64
+#define IGD_CURSOR_MAX_WM 0x3f
+#define IGD_CURSOR_DFT_WM 0
+#define IGD_CURSOR_GUARD_WM 5
+static struct watermark_igd_parameter display_wm =
+{IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_WM, IGD_GUARD_WM};
+static struct watermark_igd_parameter display_hplloff_wm =
+{IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_HPLLOFF_WM, IGD_GUARD_WM};
+static struct watermark_igd_parameter cursor_wm =
+{IGD_CURSOR_FIFO, IGD_CURSOR_MAX_WM, IGD_CURSOR_DFT_WM, IGD_CURSOR_GUARD_WM};
+static struct watermark_igd_parameter cursor_hplloff_wm =
+{IGD_CURSOR_FIFO, IGD_CURSOR_MAX_WM, IGD_CURSOR_DFT_WM, IGD_CURSOR_GUARD_WM};
+
+static unsigned long cxsr_igd_calculate_wm(unsigned long clock_in_khz,
+ struct watermark_igd_parameter *wm, unsigned long latency)
+{
+ unsigned long bytes_required, wm_size;
+
+ /* always use 32bpp */
+ bytes_required = clock_in_khz * 4 * latency/1000000;
+ bytes_required /= 64;
+ wm_size = wm->fifo_size - bytes_required - wm->guard_size;
+
+ if (wm_size > wm->max_wm)
+ wm_size = wm->max_wm;
+ if (wm_size == 0)
+ wm_size = wm->default_wm;
+ return wm_size;
+}
+
+static void igd_set_cxsr(struct drm_device *dev, bool on)
+{
+ u32 reg;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!IS_IGD(dev) || !dev_priv->cxsr_initialized)
+ return;
+ if (on) {
+ /* activate cxsr */
+ reg = I915_READ(DSPFW3);
+ reg |= 1 << 30;
+ I915_WRITE(DSPFW3, reg);
+ } else {
+ /* deactivate cxsr */
+ reg = I915_READ(DSPFW3);
+ reg &= ~(1 << 30);
+ I915_WRITE(DSPFW3, reg);
+ }
+}
+
+#define HOSTBAR_SIZE (1024 * 16)
+static int igd_get_freq(struct drm_device *dev, unsigned long *fsb,
+ unsigned long *mem)
+{
+ struct pci_dev *host;
+ u64 mch_bar;
+ u32 tmp;
+ void *addr;
+
+ host = pci_get_device(PCI_VENDOR_ID_INTEL, 0xa000, NULL);
+ if (!host)
+ host = pci_get_device(PCI_VENDOR_ID_INTEL, 0xa010, NULL);
+ if (!host)
+ return -ENODEV;
+ pci_read_config_dword(host, 0x48, &tmp);
+ mch_bar = tmp;
+ pci_read_config_dword(host, 0x4c, &tmp);
+ mch_bar |= ((u64)tmp) << 32;
+
+ mch_bar &= ~((u64)(HOSTBAR_SIZE -1));
+ pci_dev_put(host);
+
+ addr = ioremap(mch_bar, HOSTBAR_SIZE);
+ if (!addr)
+ return -ENOMEM;
+ tmp = *(u32 *)(addr + 0xc00);
+ iounmap(addr);
+
+ switch (tmp & 0x7) {
+ case 1:
+ *fsb = 533; /* 133*4 */
+ break;
+ case 2:
+ *fsb = 800; /* 200*4 */
+ break;
+ case 3:
+ *fsb = 667; /* 167*4 */
+ break;
+ case 5:
+ *fsb = 400; /* 100*4 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch ((tmp >> 4) & 0x7) {
+ case 1:
+ *mem = 533;
+ break;
+ case 2:
+ *mem = 667;
+ break;
+ case 3:
+ *mem = 800;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void igd_setup_cxsr(struct drm_device *dev, unsigned long clock)
+{
+ u32 reg;
+ unsigned long wm;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct cxsr_latency *latency;
+ unsigned long fsb, mem;
+ int i;
+ int pipes_enabled = 0;
+ struct drm_crtc *crtc;
+
+ if (!IS_IGD(dev))
+ return;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->enabled)
+ pipes_enabled++;
+ }
+ if (pipes_enabled != 1) {
+ DRM_DEBUG("Two pipes are enabled, disable CxSR\n");
+ goto disable_cxsr;
+ }
+
+ if (igd_get_freq(dev, &fsb, &mem))
+ goto disable_cxsr;
+
+ for (i = 0; i < ARRAY_SIZE(igd_cxsr_latency); i++) {
+ latency = &igd_cxsr_latency[i];
+ if ((!!IS_IGDG(dev)) == latency->is_desktop &&
+ fsb == latency->fsb && mem == latency->mem_freq)
+ break;
+ }
+ if (i >= ARRAY_SIZE(igd_cxsr_latency)) {
+ DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+ goto disable_cxsr;
+ }
+
+ /* Display SR */
+ wm = cxsr_igd_calculate_wm(clock, &display_wm, latency->display_sr);
+ reg = I915_READ(DSPFW1);
+ reg &= 0x7fffff;
+ reg |= wm << 23;
+ I915_WRITE(DSPFW1, reg);
+ DRM_DEBUG("DSPFW1 register is %x\n", reg);
+
+ /* cursor SR */
+ wm = cxsr_igd_calculate_wm(clock, &cursor_wm, latency->cursor_sr);
+ reg = I915_READ(DSPFW3);
+ reg &= ~(0x3f << 24);
+ reg |= (wm & 0x3f) << 24;
+ I915_WRITE(DSPFW3, reg);
+
+ /* Display HPLL off SR */
+ wm = cxsr_igd_calculate_wm(clock, &display_hplloff_wm,
+ latency->display_hpll_disable);
+ reg = I915_READ(DSPFW3);
+ reg &= 0xfffffe00;
+ reg |= wm & 0x1ff;
+ I915_WRITE(DSPFW3, reg);
+
+ /* cursor HPLL off SR */
+ wm = cxsr_igd_calculate_wm(clock, &cursor_hplloff_wm,
+ latency->cursor_hpll_disable);
+ reg = I915_READ(DSPFW3);
+ reg &= ~(0x3f << 16);
+ reg |= (wm & 0x3f) << 16;
+ I915_WRITE(DSPFW3, reg);
+ DRM_DEBUG("DSPFW3 register is %x\n", reg);
+
+ dev_priv->cxsr_initialized = true;
+
+ DRM_INFO("Big FIFO is enabled\n");
+ return;
+disable_cxsr:
+ dev_priv->cxsr_initialized = false;
+ DRM_INFO("Big FIFO is disabled\n");
+}
+
static int intel_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
@@ -1318,6 +1559,8 @@ static int intel_crtc_mode_set(struct dr
if (ret != 0)
return ret;
+ igd_setup_cxsr(dev, clock.dot);
+
drm_vblank_post_modeset(dev, pipe);
return 0;
Index: linux/drivers/gpu/drm/i915/i915_reg.h
===================================================================
--- linux.orig/drivers/gpu/drm/i915/i915_reg.h 2009-05-18 10:12:40.000000000 +0800
+++ linux/drivers/gpu/drm/i915/i915_reg.h 2009-05-18 10:12:43.000000000 +0800
@@ -1381,6 +1381,10 @@
#define DSPARB_CSTART_SHIFT 7
#define DSPARB_BSTART_MASK (0x7f)
#define DSPARB_BSTART_SHIFT 0
+
+#define DSPFW1 0x70034
+#define DSPFW2 0x70038
+#define DSPFW3 0x7003c
/*
* The two pipe frame counter registers are not synchronized, so
* reading a stable value is somewhat tricky. The following code
More information about the Intel-gfx
mailing list