From patchwork Wed Dec 15 07:42:31 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuanhan Liu X-Patchwork-Id: 412641 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oBF7jNIX001352 for ; Wed, 15 Dec 2010 07:45:43 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6BCAE9ECD9 for ; Tue, 14 Dec 2010 23:45:23 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by gabe.freedesktop.org (Postfix) with ESMTP id 7417B9EAF2 for ; Tue, 14 Dec 2010 23:43:08 -0800 (PST) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga101.jf.intel.com with ESMTP; 14 Dec 2010 23:43:07 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.59,347,1288594800"; d="scan'208";a="583873793" Received: from yliu-dev.sh.intel.com ([10.239.36.30]) by orsmga002.jf.intel.com with ESMTP; 14 Dec 2010 23:42:49 -0800 From: Yuanhan Liu To: intel-gfx@lists.freedesktop.org Date: Wed, 15 Dec 2010 15:42:31 +0800 Message-Id: <1292398952-11378-2-git-send-email-yuanhan.liu@linux.intel.com> X-Mailer: git-send-email 1.7.3.2 In-Reply-To: <1292398952-11378-1-git-send-email-yuanhan.liu@linux.intel.com> References: <1292398952-11378-1-git-send-email-yuanhan.liu@linux.intel.com> Subject: [Intel-gfx] [PATCH 1/2] drm/i915: Add self-refresh support on Sandybridge X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org Errors-To: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Wed, 15 Dec 2010 07:45:44 +0000 (UTC) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 2282199..864e75d 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -933,7 +933,7 @@ static int i915_sr_status(struct seq_file *m, void *unused) drm_i915_private_t *dev_priv = dev->dev_private; bool sr_enabled = false; - if (IS_GEN5(dev)) + if (HAS_PCH_SPLIT(dev)) sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN; else if (IS_CRESTLINE(dev) || IS_I945G(dev) || IS_I945GM(dev)) sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b284c13..5bba63a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -596,6 +596,8 @@ #define ILK_DISPLAY_CHICKEN1 0x42000 #define ILK_FBCQ_DIS (1<<22) +#define ILK_PABSTRETCH_DIS (1<<21) + /* * GPIO regs @@ -955,6 +957,8 @@ */ #define MCHBAR_MIRROR_BASE 0x10000 +#define MCHBAR_MIRROR_BASE_SNB 0x140000 + /** 915-945 and GM965 MCH register controlling DRAM channel access */ #define DCC 0x10200 #define DCC_ADDRESSING_MODE_SINGLE_CHANNEL (0 << 0) @@ -2346,6 +2350,46 @@ #define ILK_FIFO_LINE_SIZE 64 + +/* define the WM info on Sandybridge */ +#define SNB_DISPLAY_FIFO 128 +#define SNB_DISPLAY_MAXWM 0x7f /* bit 16:22 */ +#define SNB_DISPLAY_DFTWM 8 +#define SNB_CURSOR_FIFO 32 +#define SNB_CURSOR_MAXWM 0x1f /* bit 4:0 */ +#define SNB_CURSOR_DFTWM 8 + +#define SNB_DISPLAY_SR_FIFO 512 +#define SNB_DISPLAY_MAX_SRWM 0x1ff /* bit 16:8 */ +#define SNB_DISPLAY_DFT_SRWM 0x3f +#define SNB_CURSOR_SR_FIFO 64 +#define SNB_CURSOR_MAX_SRWM 0x3f /* bit 5:0 */ +#define SNB_CURSOR_DFT_SRWM 8 + +#define SNB_FBC_MAX_SRWM 0xf /* bit 23:20 */ + +#define SNB_FIFO_LINE_SIZE 64 + + +/* the address where we get all kinds of latency value */ +#define SSKPD 0x5d10 +#define SSKPD_WM0_MASK 0x3f +#define SSKPD_WM0_SHIFT 0 +#define SSKPD_WM1_MASK 0x3f00 +#define SSKPD_WM1_SHIFT 8 +#define SSKPD_WM2_MASK 0x3f0000 +#define SSKPD_WM2_SHIFT 16 +#define SSKPD_WM3_MASK 0x3f000000 +#define SSKPD_WM3_SHIFT 24 + +#define SNB_LATENCY(mask,shift) ((I915_READ(MCHBAR_MIRROR_BASE_SNB + SSKPD) & \ + mask) >> shift) +#define SNB_WM0_LATENCY (SNB_LATENCY(SSKPD_WM0_MASK, SSKPD_WM0_SHIFT)) +#define SNB_WM1_LATENCY (SNB_LATENCY(SSKPD_WM1_MASK, SSKPD_WM1_SHIFT)) +#define SNB_WM2_LATENCY (SNB_LATENCY(SSKPD_WM2_MASK, SSKPD_WM2_SHIFT)) +#define SNB_WM3_LATENCY (SNB_LATENCY(SSKPD_WM3_MASK, SSKPD_WM3_SHIFT)) + + /* * The two pipe frame counter registers are not synchronized, so * reading a stable value is somewhat tricky. The following code @@ -2651,6 +2695,8 @@ #define ILK_VSDPFD_FULL (1<<21) #define ILK_DSPCLK_GATE 0x42020 #define ILK_DPARB_CLK_GATE (1<<5) +#define ILK_DPFD_CLK_GATE (1<<7) + /* 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) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 17c213f..59a6d89 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2845,6 +2845,39 @@ static struct intel_watermark_params ironlake_cursor_srwm_info = { ILK_FIFO_LINE_SIZE }; +static struct intel_watermark_params sandybridge_display_wm_info = { + SNB_DISPLAY_FIFO, + SNB_DISPLAY_MAXWM, + SNB_DISPLAY_DFTWM, + 2, + SNB_FIFO_LINE_SIZE +}; + +static struct intel_watermark_params sandybridge_cursor_wm_info = { + SNB_CURSOR_FIFO, + SNB_CURSOR_MAXWM, + SNB_CURSOR_DFTWM, + 2, + SNB_FIFO_LINE_SIZE +}; + +static struct intel_watermark_params sandybridge_display_srwm_info = { + SNB_DISPLAY_SR_FIFO, + SNB_DISPLAY_MAX_SRWM, + SNB_DISPLAY_DFT_SRWM, + 2, + SNB_FIFO_LINE_SIZE +}; + +static struct intel_watermark_params sandybridge_cursor_srwm_info = { + SNB_CURSOR_SR_FIFO, + SNB_CURSOR_MAX_SRWM, + SNB_CURSOR_DFT_SRWM, + 2, + SNB_FIFO_LINE_SIZE +}; + + /** * intel_calculate_wm - calculate watermark level * @clock_in_khz: pixel clock @@ -3500,6 +3533,243 @@ static void ironlake_update_wm(struct drm_device *dev, /* XXX setup WM2 and WM3 */ } +static bool sandybridge_compute_wm0(struct drm_device *dev, + int pipe, + int *plane_wm, + int *cursor_wm) +{ + struct drm_crtc *crtc; + struct drm_i915_private *dev_priv = dev->dev_private; + int htotal, hdisplay, clock, pixel_size = 0; + int line_time_us, line_count, entries; + + crtc = intel_get_crtc_for_pipe(dev, pipe); + if (crtc->fb == NULL || !crtc->enabled) + return false; + + htotal = crtc->mode.htotal; + hdisplay = crtc->mode.hdisplay; + clock = crtc->mode.clock; + pixel_size = crtc->fb->bits_per_pixel / 8; + + /* Use the small buffer method to calculate plane watermark */ + entries = ((clock * pixel_size / 1000) * SNB_WM0_LATENCY * 100) / 1000; + entries = DIV_ROUND_UP(entries, + sandybridge_display_wm_info.cacheline_size); + *plane_wm = entries + sandybridge_display_wm_info.guard_size; + if (*plane_wm > (int)sandybridge_display_wm_info.max_wm) + *plane_wm = sandybridge_display_wm_info.max_wm; + + /* Use the large buffer method to calculate cursor watermark */ + line_time_us = ((htotal * 1000) / clock); + line_count = (SNB_WM0_LATENCY * 100 / line_time_us + 1000) / 1000; + entries = line_count * 64 * pixel_size; + entries = DIV_ROUND_UP(entries, + sandybridge_cursor_wm_info.cacheline_size); + *cursor_wm = entries + sandybridge_cursor_wm_info.guard_size; + if (*cursor_wm > sandybridge_cursor_wm_info.max_wm) + *cursor_wm = sandybridge_cursor_wm_info.max_wm; + + return true; +} + +/* + * compute watermark values of WM[1-3], SNB support 3 levels of watermark + */ +static void sandybridge_compute_srwm(int hdisplay, int htotal, int pixel_size, + int clock, int latency_ns, int *fbc_wm, + int *display_wm, int *cursor_wm) +{ + + unsigned long line_time_us; + int small, large; + int entries; + int line_count, line_size; + + if (!latency_ns) { + *fbc_wm = *display_wm = *cursor_wm = 0; + return; + } + + line_time_us = (htotal * 1000) / clock; + line_count = (latency_ns / line_time_us + 1000) / 1000; + line_size = hdisplay * pixel_size; + + /* Use the minimum of the small and large buffer method for primary */ + small = ((clock * pixel_size / 1000) * latency_ns) / 1000; + large = line_count * line_size; + + entries = DIV_ROUND_UP(min(small, large), + sandybridge_display_srwm_info.cacheline_size); + *display_wm = entries + sandybridge_display_srwm_info.guard_size; + + /* + * Spec said: + * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2 + */ + *fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2; + + /* calculate the self-refresh watermark for display cursor */ + entries = line_count * pixel_size * 64; + entries = DIV_ROUND_UP(entries, + sandybridge_cursor_srwm_info.cacheline_size); + *cursor_wm = entries + sandybridge_cursor_srwm_info.guard_size; +} + +/* + * Check the wm result. + * + * If any calculated watermark values is larger than the maximum value that + * can be programmed into the associated watermark register, that watermark + * must be disabled. + * + * Also return true if all of those watermark values is 0, which is set by + * sandybridge_compute_srwm, to indicate the latency is ZERO. + */ +static bool sandybridge_check_srwm(struct drm_device *dev, int fbc_wm, + int display_wm, int cursor_wm, int level) +{ + int err = false; + struct drm_i915_private *dev_priv = dev->dev_private; + + DRM_DEBUG_KMS("watermark %d: display plane %d, fbc lines %d," + " cursor %d\n", level, display_wm, fbc_wm, cursor_wm); + + if (fbc_wm > SNB_FBC_MAX_SRWM) { + err = true; + DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d)\n", + fbc_wm, SNB_FBC_MAX_SRWM); + + /* fbc has it's way to disable FBC WM */ + I915_WRITE(DISP_ARB_CTL, (I915_READ(DISP_ARB_CTL) | + DISP_FBC_WM_DIS)); + + } + if (display_wm > SNB_DISPLAY_MAX_SRWM) { + err = true; + DRM_DEBUG_KMS("display watermark(%d) is too large(%d)\n", + display_wm, SNB_DISPLAY_MAX_SRWM); + } + if (cursor_wm > SNB_CURSOR_MAX_SRWM) { + err = true; + DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d)\n", + cursor_wm, SNB_CURSOR_MAX_SRWM); + } + + if (!(fbc_wm || display_wm || cursor_wm)) { + err = true; + DRM_DEBUG_KMS("latency %d is 0\n", level); + } + + if (err) + DRM_DEBUG_KMS("disable watermark %d\n", level); + + return err; +} + +static void sandybridge_update_wm(struct drm_device *dev, + int planea_clock, int planeb_clock, + int hdisplay, int htotal, + int pixel_size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int fbc_wm, plane_wm, cursor_wm, enabled; + int clock; + int tmp; + + enabled = 0; + if (sandybridge_compute_wm0(dev, 0, &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEA_ILK, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe A -" + " plane %d, " "cursor: %d\n", + plane_wm, cursor_wm); + enabled++; + } + + if (sandybridge_compute_wm0(dev, 1, &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEB_ILK, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe B -" + " plane %d, cursor: %d\n", + plane_wm, cursor_wm); + enabled++; + } + + /* + * Calculate and update the self-refresh watermark only when one + * display plane is used. + * + * WM1/WM2/WM2 watermarks have to be enabled in the ascending order, + * and disabled in the descending order + * + */ + if (enabled != 1) + goto disable_wm1; + + clock = planea_clock ? planea_clock : planeb_clock; + + /* WM1 */ + sandybridge_compute_srwm(hdisplay, htotal, pixel_size, + clock, SNB_WM1_LATENCY * 500, + &fbc_wm, &plane_wm, &cursor_wm); + if (sandybridge_check_srwm(dev, fbc_wm, plane_wm, cursor_wm, 1)) + goto disable_wm1; + tmp = (WM1_LP_SR_EN | + (SNB_WM1_LATENCY << WM1_LP_LATENCY_SHIFT) | + (fbc_wm << WM1_LP_FBC_SHIFT) | + (plane_wm << WM1_LP_SR_SHIFT) | + cursor_wm); + I915_WRITE(WM1_LP_ILK, tmp); + + /* WM2 */ + sandybridge_compute_srwm(hdisplay, htotal, pixel_size, + clock, SNB_WM2_LATENCY * 500, + &fbc_wm, &plane_wm, &cursor_wm); + /* + * if watermark check failed, do not enable the rest + * level watermarks + */ + if (sandybridge_check_srwm(dev, fbc_wm, plane_wm, cursor_wm, 2)) + goto disable_wm2; + tmp = (WM2_LP_EN | + (SNB_WM2_LATENCY << WM1_LP_LATENCY_SHIFT) | + (fbc_wm << WM1_LP_FBC_SHIFT) | + (plane_wm << WM1_LP_SR_SHIFT) | + cursor_wm); + I915_WRITE(WM2_LP_ILK, tmp); + + /* WM3 */ + sandybridge_compute_srwm(hdisplay, htotal, pixel_size, + clock, SNB_WM3_LATENCY * 500, + &fbc_wm, &plane_wm, &cursor_wm); + if (sandybridge_check_srwm(dev, fbc_wm, plane_wm, cursor_wm, 3)) + goto disable_wm3; + tmp = (WM3_LP_EN | + (SNB_WM3_LATENCY << WM1_LP_LATENCY_SHIFT) | + (fbc_wm << WM1_LP_FBC_SHIFT) | + (plane_wm << WM1_LP_SR_SHIFT) | + cursor_wm); + I915_WRITE(WM3_LP_ILK, tmp); + + return; + +disable_wm1: + I915_WRITE(WM3_LP_ILK, 0); + I915_WRITE(WM2_LP_ILK, 0); + I915_WRITE(WM1_LP_ILK, 0); + return; + +disable_wm2: + I915_WRITE(WM3_LP_ILK, 0); + I915_WRITE(WM2_LP_ILK, 0); + return; + +disable_wm3: + I915_WRITE(WM3_LP_ILK, 0); + return; +} + /** * intel_update_watermarks - update FIFO watermark values based on current modes * @@ -6010,6 +6280,35 @@ void intel_enable_clock_gating(struct drm_device *dev) _3D_CHICKEN2_WM_READ_PIPELINED); } + /* + * According to the spec the following bits should be set in + * order to enable memory self-refresh and fbc: + * The bit21 and bit22 of 0x42000 + * The bit21 and bit22 of 0x42004 + * The bit5 and bit7 of 0x42020 + * The bit14 of 0x70180 + * The bit14 of 0x71180 + */ + if (IS_GEN6(dev)) { + I915_WRITE(ILK_DISPLAY_CHICKEN1, + (I915_READ(ILK_DISPLAY_CHICKEN1) | + ILK_FBCQ_DIS | ILK_PABSTRETCH_DIS)); + 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 | + ILK_DPFD_CLK_GATE)); + I915_WRITE(DSPACNTR, + (I915_READ(DSPACNTR) | + DISPPLANE_TRICKLE_FEED_DISABLE)); + I915_WRITE(DSPBCNTR, + (I915_READ(DSPBCNTR) | + DISPPLANE_TRICKLE_FEED_DISABLE)); + } + + } else if (IS_G4X(dev)) { uint32_t dspclk_gate; I915_WRITE(RENCLK_GATE_D1, 0); @@ -6176,6 +6475,14 @@ static void intel_init_display(struct drm_device *dev) "Disable CxSR\n"); dev_priv->display.update_wm = NULL; } + } else if (IS_GEN6(dev)) { + if (SNB_WM0_LATENCY) { + dev_priv->display.update_wm = sandybridge_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)) {