2013/7/5 <ville.syrjala at linux.intel.com>: > From: Ville Syrj?l? <ville.syrjala at linux.intel.com> > > There is a major problem with the watermark registers; they're not > double buffered. So we need to make sure we update them at the correct > time when messing about with planes. The correct time is the beginning > of vblank. > > So when we determine that the watermarks need to updated hand in hand > with the next vblank, we store the pre-computed watermarks under > intel_crtc, and when the vblank happens, we promote the pending > watermarks to active status. > > on HSW when the watermarks for any pipe change, we must merge the > watermarks from all pipes so that we can determine the correct LP1+ > watermark levels. For simplicity we follow the same codepaths for > pre-HSW hardware as well, but there all the LP1+ watermarks will be > disabled when multiple pipes are enabled. Once the watermarks are > merged we check them for validity, disabling any invalid levels. > > Touching the watermark registers causes the hardware to re-evaluate the > watermarks, which expeds some power. So after merging the watermarks > we check which watermark registers actually need to be changed. And > finally we write the watermarks registers in the correct order. This patch is way too big for us, poor reviewers. Can you split this into many many many tiny patches? I see *a lot* of different changes here. > > Signed-off-by: Ville Syrj?l? <ville.syrjala at linux.intel.com> > --- > drivers/gpu/drm/i915/i915_drv.h | 12 + > drivers/gpu/drm/i915/i915_irq.c | 12 +- > drivers/gpu/drm/i915/i915_reg.h | 2 + > drivers/gpu/drm/i915/intel_drv.h | 22 + > drivers/gpu/drm/i915/intel_pm.c | 1546 ++++++++++++++++---------------------- > 5 files changed, 686 insertions(+), 908 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h > index fd57bed..446be9a 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -1198,6 +1198,7 @@ typedef struct drm_i915_private { > > struct i915_suspend_saved_registers regfile; > > + /* per-device watermark state */ > struct { > /* watermark latency values for primary */ > uint16_t pri_latency[5]; > @@ -1205,6 +1206,17 @@ typedef struct drm_i915_private { > uint16_t spr_latency[5]; > /* watermark latency values for cursor */ > uint16_t cur_latency[5]; > + /* protects all watermark state */ > + spinlock_t lock; > + /* current state of FBC_WM */ > + bool fbc_wm_enabled; > + /* current state of DDB partitioning */ > + enum intel_ddb_partitioning ddb_partitioning; > + /* > + * LP1+ values currently programmed into the hardware > + * [0] = LP1, [1] = LP2, [2] = LP3 > + */ > + struct intel_wm_level hw[3]; > } wm; > > /* Old dri1 support infrastructure, beware the dragons ya fools entering > diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c > index 4c1b1e3..ee5127f 100644 > --- a/drivers/gpu/drm/i915/i915_irq.c > +++ b/drivers/gpu/drm/i915/i915_irq.c > @@ -1240,8 +1240,10 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) > intel_opregion_asle_intr(dev); > > for (i = 0; i < 3; i++) { > - if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) > + if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) { > + ilk_update_pipe_wm(dev, i); > drm_handle_vblank(dev, i); > + } > if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) { > intel_prepare_page_flip(dev, i); > intel_finish_page_flip_plane(dev, i); > @@ -1343,11 +1345,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) > if (de_iir & DE_GSE) > intel_opregion_asle_intr(dev); > > - if (de_iir & DE_PIPEA_VBLANK) > + if (de_iir & DE_PIPEA_VBLANK) { > + ilk_update_pipe_wm(dev, 0); > drm_handle_vblank(dev, 0); > + } > > - if (de_iir & DE_PIPEB_VBLANK) > + if (de_iir & DE_PIPEB_VBLANK) { > + ilk_update_pipe_wm(dev, 1); > drm_handle_vblank(dev, 1); > + } > > if (de_iir & DE_POISON) > DRM_ERROR("Poison interrupt\n"); > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > index 9b51be8..e595f54 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -3762,6 +3762,8 @@ > #define DISP_ARB_CTL 0x45000 > #define DISP_TILE_SURFACE_SWIZZLING (1<<13) > #define DISP_FBC_WM_DIS (1<<15) > +#define DISP_ARB_CTL2 0x45004 > +#define DISP_DATA_PARTITION_5_6 (1<<6) > #define GEN7_MSG_CTL 0x45010 > #define WAIT_FOR_PCH_RESET_ACK (1<<1) > #define WAIT_FOR_PCH_FLR_ACK (1<<0) > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h > index 4afaeb2..a5c15ab 100644 > --- a/drivers/gpu/drm/i915/intel_drv.h > +++ b/drivers/gpu/drm/i915/intel_drv.h > @@ -292,6 +292,15 @@ struct intel_crtc_config { > bool ips_enabled; > }; > > +struct intel_pipe_wm { > + bool pipe_enabled; > + bool sprites_enabled; > + bool sprites_scaled; > + bool fbc_wm_enabled; > + uint32_t linetime; > + struct intel_wm_level wm[5]; > +}; > + > struct intel_crtc { > struct drm_crtc base; > enum pipe pipe; > @@ -332,6 +341,18 @@ struct intel_crtc { > /* Access to these should be protected by dev_priv->irq_lock. */ > bool cpu_fifo_underrun_disabled; > bool pch_fifo_underrun_disabled; > + > + /* per-pipe watermark state */ > + struct { > + /* watermarks queued for next vblank */ > + struct intel_pipe_wm pending; > + /* watermarks currently being used */ > + struct intel_pipe_wm active; > + /* LP0 values currently programmed into the hardware */ > + struct intel_wm_level hw; > + /* indicates that 'pending' contains changed watermarks */ > + bool dirty; > + } wm; > }; > > struct intel_plane_wm_parameters { > @@ -838,5 +859,6 @@ extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, > extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, > enum transcoder pch_transcoder, > bool enable); > +extern void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe); > > #endif /* __INTEL_DRV_H__ */ > diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c > index c4e94bb..7fd922dc 100644 > --- a/drivers/gpu/drm/i915/intel_pm.c > +++ b/drivers/gpu/drm/i915/intel_pm.c > @@ -32,6 +32,11 @@ > #include <linux/module.h> > #include <drm/i915_powerwell.h> > > +static const char *yesno(int v) > +{ > + return v ? "yes" : "no"; > +} > + > #define FORCEWAKE_ACK_TIMEOUT_MS 2 > > /* FBC, or Frame Buffer Compression, is a technique employed to compress the > @@ -1668,424 +1673,6 @@ static void i830_update_wm(struct drm_crtc *crtc) > I915_WRITE(FW_BLC, fwater_lo); > } > > -#define ILK_LP0_PLANE_LATENCY 700 > -#define ILK_LP0_CURSOR_LATENCY 1300 > - > -/* > - * 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. > - */ > -static bool ironlake_check_srwm(struct drm_device *dev, int level, > - int fbc_wm, int display_wm, int cursor_wm, > - const struct intel_watermark_params *display, > - const struct intel_watermark_params *cursor) > -{ > - 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) { > - DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n", > - fbc_wm, SNB_FBC_MAX_SRWM, level); > - > - /* fbc has it's own way to disable FBC WM */ > - I915_WRITE(DISP_ARB_CTL, > - I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS); > - return false; > - } else if (INTEL_INFO(dev)->gen >= 6) { > - /* enable FBC WM (except on ILK, where it must remain off) */ > - I915_WRITE(DISP_ARB_CTL, > - I915_READ(DISP_ARB_CTL) & ~DISP_FBC_WM_DIS); > - } > - > - if (display_wm > display->max_wm) { > - DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n", > - display_wm, SNB_DISPLAY_MAX_SRWM, level); > - return false; > - } > - > - if (cursor_wm > cursor->max_wm) { > - DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n", > - cursor_wm, SNB_CURSOR_MAX_SRWM, level); > - return false; > - } > - > - if (!(fbc_wm || display_wm || cursor_wm)) { > - DRM_DEBUG_KMS("latency %d is 0, disabling wm%d+\n", level, level); > - return false; > - } > - > - return true; > -} > - > -/* > - * Compute watermark values of WM[1-3], > - */ > -static bool ironlake_compute_srwm(struct drm_device *dev, int level, int plane, > - int latency_ns, > - const struct intel_watermark_params *display, > - const struct intel_watermark_params *cursor, > - int *fbc_wm, int *display_wm, int *cursor_wm) > -{ > - struct drm_crtc *crtc; > - unsigned long line_time_us; > - int hdisplay, htotal, pixel_size, clock; > - int line_count, line_size; > - int small, large; > - int entries; > - > - if (!latency_ns) { > - *fbc_wm = *display_wm = *cursor_wm = 0; > - return false; > - } > - > - crtc = intel_get_crtc_for_plane(dev, plane); > - hdisplay = crtc->mode.hdisplay; > - htotal = crtc->mode.htotal; > - clock = crtc->mode.clock; > - pixel_size = crtc->fb->bits_per_pixel / 8; > - > - 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), display->cacheline_size); > - *display_wm = entries + display->guard_size; > - > - /* > - * Spec says: > - * 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, cursor->cacheline_size); > - *cursor_wm = entries + cursor->guard_size; > - > - return ironlake_check_srwm(dev, level, > - *fbc_wm, *display_wm, *cursor_wm, > - display, cursor); > -} > - > -static void ironlake_update_wm(struct drm_crtc *crtc) > -{ > - struct drm_device *dev = crtc->dev; > - struct drm_i915_private *dev_priv = dev->dev_private; > - int fbc_wm, plane_wm, cursor_wm; > - unsigned int enabled; > - > - enabled = 0; > - if (g4x_compute_wm0(dev, PIPE_A, > - &ironlake_display_wm_info, > - ILK_LP0_PLANE_LATENCY, > - &ironlake_cursor_wm_info, > - ILK_LP0_CURSOR_LATENCY, > - &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 |= 1 << PIPE_A; > - } > - > - if (g4x_compute_wm0(dev, PIPE_B, > - &ironlake_display_wm_info, > - ILK_LP0_PLANE_LATENCY, > - &ironlake_cursor_wm_info, > - ILK_LP0_CURSOR_LATENCY, > - &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 |= 1 << PIPE_B; > - } > - > - /* > - * Calculate and update the self-refresh watermark only when one > - * display plane is used. > - */ > - I915_WRITE(WM3_LP_ILK, 0); > - I915_WRITE(WM2_LP_ILK, 0); > - I915_WRITE(WM1_LP_ILK, 0); > - > - if (!single_plane_enabled(enabled)) > - return; > - enabled = ffs(enabled) - 1; > - > - /* WM1 */ > - if (!ironlake_compute_srwm(dev, 1, enabled, > - ILK_READ_WM1_LATENCY() * 500, > - &ironlake_display_srwm_info, > - &ironlake_cursor_srwm_info, > - &fbc_wm, &plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM1_LP_ILK, > - WM1_LP_SR_EN | > - (ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > - > - /* WM2 */ > - if (!ironlake_compute_srwm(dev, 2, enabled, > - ILK_READ_WM2_LATENCY() * 500, > - &ironlake_display_srwm_info, > - &ironlake_cursor_srwm_info, > - &fbc_wm, &plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM2_LP_ILK, > - WM2_LP_EN | > - (ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > - > - /* > - * WM3 is unsupported on ILK, probably because we don't have latency > - * data for that power state > - */ > -} > - > -static void sandybridge_update_wm(struct drm_crtc *crtc) > -{ > - struct drm_device *dev = crtc->dev; > - struct drm_i915_private *dev_priv = dev->dev_private; > - int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */ > - u32 val; > - int fbc_wm, plane_wm, cursor_wm; > - unsigned int enabled; > - > - enabled = 0; > - if (g4x_compute_wm0(dev, PIPE_A, > - &sandybridge_display_wm_info, latency, > - &sandybridge_cursor_wm_info, latency, > - &plane_wm, &cursor_wm)) { > - val = I915_READ(WM0_PIPEA_ILK); > - val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK); > - I915_WRITE(WM0_PIPEA_ILK, val | > - ((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 |= 1 << PIPE_A; > - } > - > - if (g4x_compute_wm0(dev, PIPE_B, > - &sandybridge_display_wm_info, latency, > - &sandybridge_cursor_wm_info, latency, > - &plane_wm, &cursor_wm)) { > - val = I915_READ(WM0_PIPEB_ILK); > - val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK); > - I915_WRITE(WM0_PIPEB_ILK, val | > - ((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 |= 1 << PIPE_B; > - } > - > - /* > - * Calculate and update the self-refresh watermark only when one > - * display plane is used. > - * > - * SNB support 3 levels of watermark. > - * > - * WM1/WM2/WM2 watermarks have to be enabled in the ascending order, > - * and disabled in the descending order > - * > - */ > - I915_WRITE(WM3_LP_ILK, 0); > - I915_WRITE(WM2_LP_ILK, 0); > - I915_WRITE(WM1_LP_ILK, 0); > - > - if (!single_plane_enabled(enabled) || > - dev_priv->sprite_scaling_enabled) > - return; > - enabled = ffs(enabled) - 1; > - > - /* WM1 */ > - if (!ironlake_compute_srwm(dev, 1, enabled, > - SNB_READ_WM1_LATENCY() * 500, > - &sandybridge_display_srwm_info, > - &sandybridge_cursor_srwm_info, > - &fbc_wm, &plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM1_LP_ILK, > - WM1_LP_SR_EN | > - (SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > - > - /* WM2 */ > - if (!ironlake_compute_srwm(dev, 2, enabled, > - SNB_READ_WM2_LATENCY() * 500, > - &sandybridge_display_srwm_info, > - &sandybridge_cursor_srwm_info, > - &fbc_wm, &plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM2_LP_ILK, > - WM2_LP_EN | > - (SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > - > - /* WM3 */ > - if (!ironlake_compute_srwm(dev, 3, enabled, > - SNB_READ_WM3_LATENCY() * 500, > - &sandybridge_display_srwm_info, > - &sandybridge_cursor_srwm_info, > - &fbc_wm, &plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM3_LP_ILK, > - WM3_LP_EN | > - (SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > -} > - > -static void ivybridge_update_wm(struct drm_crtc *crtc) > -{ > - struct drm_device *dev = crtc->dev; > - struct drm_i915_private *dev_priv = dev->dev_private; > - int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */ > - u32 val; > - int fbc_wm, plane_wm, cursor_wm; > - int ignore_fbc_wm, ignore_plane_wm, ignore_cursor_wm; > - unsigned int enabled; > - > - enabled = 0; > - if (g4x_compute_wm0(dev, PIPE_A, > - &sandybridge_display_wm_info, latency, > - &sandybridge_cursor_wm_info, latency, > - &plane_wm, &cursor_wm)) { > - val = I915_READ(WM0_PIPEA_ILK); > - val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK); > - I915_WRITE(WM0_PIPEA_ILK, val | > - ((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 |= 1 << PIPE_A; > - } > - > - if (g4x_compute_wm0(dev, PIPE_B, > - &sandybridge_display_wm_info, latency, > - &sandybridge_cursor_wm_info, latency, > - &plane_wm, &cursor_wm)) { > - val = I915_READ(WM0_PIPEB_ILK); > - val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK); > - I915_WRITE(WM0_PIPEB_ILK, val | > - ((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 |= 1 << PIPE_B; > - } > - > - if (g4x_compute_wm0(dev, PIPE_C, > - &sandybridge_display_wm_info, latency, > - &sandybridge_cursor_wm_info, latency, > - &plane_wm, &cursor_wm)) { > - val = I915_READ(WM0_PIPEC_IVB); > - val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK); > - I915_WRITE(WM0_PIPEC_IVB, val | > - ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm)); > - DRM_DEBUG_KMS("FIFO watermarks For pipe C -" > - " plane %d, cursor: %d\n", > - plane_wm, cursor_wm); > - enabled |= 1 << PIPE_C; > - } > - > - /* > - * Calculate and update the self-refresh watermark only when one > - * display plane is used. > - * > - * SNB support 3 levels of watermark. > - * > - * WM1/WM2/WM2 watermarks have to be enabled in the ascending order, > - * and disabled in the descending order > - * > - */ > - I915_WRITE(WM3_LP_ILK, 0); > - I915_WRITE(WM2_LP_ILK, 0); > - I915_WRITE(WM1_LP_ILK, 0); > - > - if (!single_plane_enabled(enabled) || > - dev_priv->sprite_scaling_enabled) > - return; > - enabled = ffs(enabled) - 1; > - > - /* WM1 */ > - if (!ironlake_compute_srwm(dev, 1, enabled, > - SNB_READ_WM1_LATENCY() * 500, > - &sandybridge_display_srwm_info, > - &sandybridge_cursor_srwm_info, > - &fbc_wm, &plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM1_LP_ILK, > - WM1_LP_SR_EN | > - (SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > - > - /* WM2 */ > - if (!ironlake_compute_srwm(dev, 2, enabled, > - SNB_READ_WM2_LATENCY() * 500, > - &sandybridge_display_srwm_info, > - &sandybridge_cursor_srwm_info, > - &fbc_wm, &plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM2_LP_ILK, > - WM2_LP_EN | > - (SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > - > - /* WM3, note we have to correct the cursor latency */ > - if (!ironlake_compute_srwm(dev, 3, enabled, > - SNB_READ_WM3_LATENCY() * 500, > - &sandybridge_display_srwm_info, > - &sandybridge_cursor_srwm_info, > - &fbc_wm, &plane_wm, &ignore_cursor_wm) || > - !ironlake_compute_srwm(dev, 3, enabled, > - 2 * SNB_READ_WM3_LATENCY() * 500, > - &sandybridge_display_srwm_info, > - &sandybridge_cursor_srwm_info, > - &ignore_fbc_wm, &ignore_plane_wm, &cursor_wm)) > - return; > - > - I915_WRITE(WM3_LP_ILK, > - WM3_LP_EN | > - (SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) | > - (fbc_wm << WM1_LP_FBC_SHIFT) | > - (plane_wm << WM1_LP_SR_SHIFT) | > - cursor_wm); > -} > - > static uint32_t ilk_pipe_pixel_rate(struct drm_device *dev, > struct drm_crtc *crtc) > { > @@ -2168,14 +1755,6 @@ struct hsw_wm_maximums { > uint16_t fbc; > }; > > -struct hsw_wm_values { > - uint32_t wm_pipe[3]; > - uint32_t wm_lp[3]; > - uint32_t wm_lp_spr[3]; > - uint32_t wm_linetime[3]; > - bool enable_fbc_wm; > -}; > - > /* used in computing the new watermarks state */ > struct intel_wm_config { > unsigned int pipes_active; > @@ -2351,6 +1930,16 @@ static void ilk_wm_max(struct drm_device *dev, > max->fbc = ilk_fbc_wm_max(); > } > > +/* > + * We use memcmp() to determine if watermarks need updating, > + * so keeping all disabled watermark levels consistently > + * zeroed avoids false positives. > + */ > +static void ilk_disable_wm_level(struct intel_wm_level *wm) > +{ > + memset(wm, 0, sizeof(*wm)); > +} > + > static bool ilk_check_wm(int level, > const struct hsw_wm_maximums *max, > struct intel_wm_level *result) > @@ -2381,6 +1970,9 @@ static bool ilk_check_wm(int level, > > DRM_DEBUG_KMS("WM%d: %sabled\n", level, result->enable ? "en" : "dis"); > > + if (!result->enable) > + ilk_disable_wm_level(result); > + > return ret; > } > > @@ -2406,52 +1998,603 @@ static void ilk_compute_wm_level(struct drm_i915_private *dev_priv, > result->enable = true; > } > > -static bool hsw_compute_lp_wm(struct drm_i915_private *dev_priv, > - int level, struct hsw_wm_maximums *max, > - struct hsw_pipe_wm_parameters *params, > - struct intel_wm_level *result) > -{ > - enum pipe pipe; > - struct intel_wm_level res[3]; > - > - for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) > - ilk_compute_wm_level(dev_priv, level, ¶ms[pipe], &res[pipe]); > - > - result->pri_val = max3(res[0].pri_val, res[1].pri_val, res[2].pri_val); > - result->spr_val = max3(res[0].spr_val, res[1].spr_val, res[2].spr_val); > - result->cur_val = max3(res[0].cur_val, res[1].cur_val, res[2].cur_val); > - result->fbc_val = max3(res[0].fbc_val, res[1].fbc_val, res[2].fbc_val); > - result->enable = true; > - > - return ilk_check_wm(level, max, result); > -} > - > -static uint32_t hsw_compute_wm_pipe(struct drm_i915_private *dev_priv, > - enum pipe pipe, > - struct hsw_pipe_wm_parameters *params) > -{ > - uint32_t pri_val, cur_val, spr_val; > - uint16_t pri_latency = dev_priv->wm.pri_latency[0]; > - uint16_t spr_latency = dev_priv->wm.spr_latency[0]; > - uint16_t cur_latency = dev_priv->wm.cur_latency[0]; > - > - pri_val = ilk_compute_pri_wm(params, pri_latency, 0); > - spr_val = ilk_compute_spr_wm(params, spr_latency); > - cur_val = ilk_compute_cur_wm(params, cur_latency); > - > - WARN(pri_val > 127, > - "Primary WM error, mode not supported for pipe %c\n", > - pipe_name(pipe)); > - WARN(spr_val > 127, > - "Sprite WM error, mode not supported for pipe %c\n", > - pipe_name(pipe)); > - WARN(cur_val > 63, > - "Cursor WM error, mode not supported for pipe %c\n", > - pipe_name(pipe)); > - > - return (pri_val << WM0_PIPE_PLANE_SHIFT) | > - (spr_val << WM0_PIPE_SPRITE_SHIFT) | > - cur_val; > +/* The value we need to program into the WM_LPx latency field */ > +static unsigned int ilk_wm_lp_latency(struct drm_device *dev, int level) > +{ > + struct drm_i915_private *dev_priv = dev->dev_private; > + > + if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev)) > + return 2 * level; > + else > + return dev_priv->wm.pri_latency[level]; > +} > + > +/* > + * Merge the watermarks from all active pipes for a specific level. > + */ > +static void ilk_merge_wm_level(struct drm_device *dev, > + int level, > + struct intel_wm_level *ret_wm) > +{ > + struct intel_crtc *intel_crtc; > + > + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) { > + const struct intel_pipe_wm *pipe_wm = &intel_crtc->wm.active; > + const struct intel_wm_level *wm = &pipe_wm->wm[level]; > + > + if (!pipe_wm->pipe_enabled) > + continue; > + > + if (!wm->enable) > + break; > + > + ret_wm->enable = true; > + ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val); > + ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val); > + ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val); > + ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val); > + } > +} > + > +/* > + * Merge all low power watermarks for all active pipes. > + */ > +static void ilk_wm_merge(struct drm_device *dev, > + struct intel_wm_config *config, > + struct intel_pipe_wm *merged) > +{ > + int level; > + > + /* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */ > + if (config->pipes_active > 1 && > + (INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev))) > + return; > + > + /* ILK: FBC WM must remain disabled */ > + merged->fbc_wm_enabled = INTEL_INFO(dev)->gen >= 6; > + > + /* merge each level */ > + for (level = 1; level <= 4; level++) { > + struct intel_wm_level *wm = &merged->wm[level]; > + > + ilk_merge_wm_level(dev, level, wm); > + } > +} > + > +static void ilk_wm_limit(struct drm_device *dev, > + const struct intel_wm_config *config, > + const struct hsw_wm_maximums *max, > + struct intel_pipe_wm *merged) > +{ > + int level; > + > + for (level = 1; level <= 4; level++) { > + struct intel_wm_level *wm = &merged->wm[level]; > + > + if (!ilk_check_wm(level, max, wm)) > + break; > + > + /* > + * If FBC WM is exceeded, disable just FBC WM > + * instead if the whole level. > + */ > + if (wm->fbc_val > max->fbc) > + merged->fbc_wm_enabled = false; > + } > + > + if (merged->fbc_wm_enabled) > + return; > + > + /* Make sure we don't write garbage to the registers, */ > + for (level = 1; level <= 4; level++) { > + struct intel_wm_level *wm = &merged->wm[level]; > + > + wm->fbc_val = 0; > + } > + > + /* > + * LP2+ watermarks must be disabled when FBC watermark is disabled on ILK > + * In practice this is always happens since we always disable FBC WM on ILK. > + */ > + if (INTEL_INFO(dev)->gen == 5 && intel_fbc_enabled(dev)) { > + for (level = 2; level <= 4; level++) { > + struct intel_wm_level *wm = &merged->wm[level]; > + > + ilk_disable_wm_level(wm); > + } > + } > +} > + > +static int ilk_wm_lp_to_level(int wm_lp, const struct intel_pipe_wm *merged) > +{ > + /* LP1,LP2,LP3 levels are either 1,2,3 or 1,3,4 */ > + return wm_lp + (wm_lp >= 2 && merged->wm[4].enable); > +} > + > +/* dirty bits used to track which watermarks need changes */ > +#define WM_DIRTY_PIPE(pipe) (1 << (pipe)) > +#define WM_DIRTY_LP(wm_lp) (1 << (15 + (wm_lp))) > +#define WM_DIRTY_FBC (1 << 24) > +#define WM_DIRTY_DDB (1 << 25) > + > +/* Determine which watermarks registers need reprogramming. */ > +static unsigned int ilk_compute_wm_dirty(struct drm_device *dev, > + const struct intel_wm_config *config, > + enum intel_ddb_partitioning ddb_partitioning, > + const struct intel_pipe_wm *merged) > +{ > + struct drm_i915_private *dev_priv = dev->dev_private; > + struct intel_crtc *intel_crtc; > + unsigned int dirty = 0; > + int wm_lp; > + > + /* Do LP0 watermarks need updates? */ > + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) { > + const struct intel_wm_level *wm = &intel_crtc->wm.active.wm[0]; > + > + if (memcmp(&intel_crtc->wm.hw, wm, sizeof(*wm))) > + dirty |= WM_DIRTY_PIPE(intel_crtc->pipe); > + } > + > + if (config->fbc_wm_enabled != dev_priv->wm.fbc_wm_enabled) > + dirty |= WM_DIRTY_FBC; > + > + if (ddb_partitioning != dev_priv->wm.ddb_partitioning) > + dirty |= WM_DIRTY_DDB; > + > + /* Need to disable LP1+ watermarks when changing DDB/FBC WM */ > + if (dirty & (WM_DIRTY_FBC | WM_DIRTY_DDB)) { > + dirty |= WM_DIRTY_LP(1) | WM_DIRTY_LP(2) | WM_DIRTY_LP(3); > + > + /* LP1+ watermarks already deemed dirty, no need to continue */ > + return dirty; > + } > + > + /* Find the lowest numbered LP1+ watermark in need of an update... */ > + for (wm_lp = 1; wm_lp <= 3; wm_lp++) { > + int level = ilk_wm_lp_to_level(wm_lp, merged); > + const struct intel_wm_level *wm = &merged->wm[level]; > + > + if (memcmp(&dev_priv->wm.hw[wm_lp - 1], wm, sizeof(*wm))) > + break; > + } > + > + /* ...and mark it and all higher numbered LP1+ watermarks as dirty */ > + for (; wm_lp <= 3; wm_lp++) > + dirty |= WM_DIRTY_LP(wm_lp); > + > + return dirty; > +} > + > +/* Disable LP1+ watermarks */ > +static void ilk_disable_lp_wm(struct drm_i915_private *dev_priv, > + unsigned int dirty) > +{ > + int wm_lp; > + > + for (wm_lp = 3; wm_lp >= 1; wm_lp--) { > + static const unsigned int ilk_wm_lp_reg[] = { > + [1] = WM1_LP_ILK, > + [2] = WM2_LP_ILK, > + [3] = WM3_LP_ILK, > + }; > + struct intel_wm_level *hw = &dev_priv->wm.hw[wm_lp - 1]; > + > + if (!(dirty & WM_DIRTY_LP(wm_lp))) { > + /* all lower dirty bits must be set when a higher bit is set */ > + WARN_ON(wm_lp > 1 && dirty & WM_DIRTY_LP(wm_lp - 1)); > + WARN_ON(wm_lp > 2 && dirty & WM_DIRTY_LP(wm_lp - 2)); > + break; > + } > + > + /* This LP watermark already disabled? */ > + if (!hw->enable) > + continue; > + > + DRM_DEBUG_KMS("disabling LP%d watermark\n", wm_lp); > + > + /* > + * Simply writing 0 to the register in the middle of the frame > + * can cause immediate underruns on ILK. Instead we must clear > + * only the enable bit. I assume the hardware will take a while > + * to get out of low power mode, during which time it still > + * consults the watermarks levels. > + */ > + I915_WRITE(ilk_wm_lp_reg[wm_lp], > + I915_READ(ilk_wm_lp_reg[wm_lp]) & ~WM1_LP_SR_EN); > + > + /* ILK/SNB have a separate enable bit for sprite LP1 watermark */ > + if (hw->spr_val && INTEL_INFO(dev_priv->dev)->gen <= 6) { > + WARN_ON(wm_lp != 1); > + I915_WRITE(WM1S_LP_ILK, I915_READ(WM1S_LP_ILK) & ~WM1S_LP_EN); > + } > + > + ilk_disable_wm_level(hw); > + } > +} > + > +struct intel_pipe_wm *ivb_find_best_wm(struct intel_pipe_wm *r1, > + struct intel_pipe_wm *r2) > +{ > + int level, level1 = 0, level2 = 0; > + > + for (level = 1; level <= 4; level++) { > + if (r1->wm[level].enable) > + level1 = level; > + if (r2->wm[level].enable) > + level2 = level; > + } > + > + if (level1 == level2) { > + if (r2->fbc_wm_enabled && !r1->fbc_wm_enabled) > + return r2; > + else > + return r1; > + } else if (level1 > level2) { > + return r1; > + } else { > + return r2; > + } > +} > + > +/* Program the watermark registers */ > +static void ilk_program_watermarks(struct drm_device *dev) > +{ > + struct drm_i915_private *dev_priv = dev->dev_private; > + struct drm_crtc *crtc; > + struct intel_pipe_wm merged_1_2 = {}, merged_5_6 = {}, *merged; > + struct hsw_wm_maximums lp_max_1_2, lp_max_5_6; > + int wm_lp; > + unsigned int dirty; > + struct intel_wm_config config = {}; > + struct intel_crtc *intel_crtc; > + enum intel_ddb_partitioning ddb_partitioning; > + > + /* ILK: FBC WM must remain disabled */ > + config.fbc_wm_enabled = INTEL_INFO(dev)->gen >= 6 && intel_fbc_enabled(dev); > + > + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) { > + const struct intel_pipe_wm *wm = &intel_crtc->wm.active; > + > + if (!wm->pipe_enabled) > + continue; > + > + config.sprites_enabled |= wm->sprites_enabled; > + config.sprites_scaled |= wm->sprites_scaled; > + config.pipes_active++; > + } > + > + ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_1_2, &lp_max_1_2); > + > + ilk_wm_merge(dev, &config, &merged_1_2); > + > + /* 5/6 DDB partitioning only in single pipe config on IVB+ */ > + if (INTEL_INFO(dev)->gen >= 7 && config.pipes_active <= 1) { > + ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_5_6, &lp_max_5_6); > + > + merged_5_6 = merged_1_2; > + > + ilk_wm_limit(dev, &config, &lp_max_1_2, &merged_1_2); > + ilk_wm_limit(dev, &config, &lp_max_5_6, &merged_5_6); > + > + merged = ivb_find_best_wm(&merged_1_2, &merged_5_6); > + } else { > + ilk_wm_limit(dev, &config, &lp_max_1_2, &merged_1_2); > + merged = &merged_1_2; > + } > + > + ddb_partitioning = merged == &merged_1_2 ? > + INTEL_DDB_PART_1_2 : INTEL_DDB_PART_5_6; > + > + DRM_DEBUG_KMS("WM: pipes active=%u, sprites enabled=%s, " > + "sprites scaled=%s, FBC WM enabled=%s, " > + "DDB partitioning=%s\n", > + config.pipes_active, > + yesno(config.sprites_enabled), > + yesno(config.sprites_scaled), > + yesno(config.fbc_wm_enabled), > + ddb_partitioning == INTEL_DDB_PART_1_2 ? "1/2" : "5/6"); > + > + dirty = ilk_compute_wm_dirty(dev, &config, ddb_partitioning, merged); > + if (!dirty) { > + DRM_DEBUG_KMS("WM: all clean\n"); > + return; > + } > + > + DRM_DEBUG_KMS("WM: dirty %s%s%s%s%s%s%s%s\n", > + dirty & WM_DIRTY_DDB ? "DDB," : "", > + dirty & WM_DIRTY_FBC ? "FBC," : "", > + dirty & WM_DIRTY_LP(3) ? "LP3," : "", > + dirty & WM_DIRTY_LP(2) ? "LP2," : "", > + dirty & WM_DIRTY_LP(1) ? "LP1," : "", > + dirty & WM_DIRTY_PIPE(PIPE_A) ? "LP0(A)," : "", > + dirty & WM_DIRTY_PIPE(PIPE_B) ? "LP0(B)," : "", > + dirty & WM_DIRTY_PIPE(PIPE_C) ? "LP0(C)," : ""); > + > + /* > + * LP1+ watermarks need to be disabled prior to > + * changing DDB partitioning, FBC watermark disable, > + * or lower numbered LP1+ watermarks. "dirty" knows > + * which watermarks need disabling. > + */ > + ilk_disable_lp_wm(dev_priv, dirty); > + > + /* program LP0 watermarks */ > + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { > + static const unsigned int ilk_wm0_pipe_reg[] = { > + [PIPE_A] = WM0_PIPEA_ILK, > + [PIPE_B] = WM0_PIPEB_ILK, > + [PIPE_C] = WM0_PIPEC_IVB, > + }; > + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); > + const struct intel_pipe_wm *pipe_wm = &intel_crtc->wm.active; > + const struct intel_wm_level *wm = &pipe_wm->wm[0]; > + > + if (!(dirty & WM_DIRTY_PIPE(intel_crtc->pipe))) > + continue; > + > + DRM_DEBUG_KMS("Pipe %c LP0: pri=%u, spr=%u, cur=%u\n", > + pipe_name(intel_crtc->pipe), > + wm->pri_val, wm->spr_val, wm->cur_val); > + > + I915_WRITE(ilk_wm0_pipe_reg[intel_crtc->pipe], > + wm->pri_val << WM0_PIPE_PLANE_SHIFT | > + wm->spr_val << WM0_PIPE_SPRITE_SHIFT | > + wm->cur_val); > + > + if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev)) > + I915_WRITE(PIPE_WM_LINETIME(intel_crtc->pipe), > + pipe_wm->linetime); > + > + intel_crtc->wm.hw = *wm; > + } > + > + /* configure FBC watermark */ > + if (dirty & WM_DIRTY_FBC) { > + uint32_t tmp = I915_READ(DISP_ARB_CTL); > + > + DRM_DEBUG_KMS("WM: FBC watermark = %s\n", yesno(config.fbc_wm_enabled)); > + > + if (config.fbc_wm_enabled) > + tmp &= ~DISP_FBC_WM_DIS; > + else > + tmp |= DISP_FBC_WM_DIS; > + > + I915_WRITE(DISP_ARB_CTL, tmp); > + > + dev_priv->wm.fbc_wm_enabled = config.fbc_wm_enabled; > + } > + > + /* configure DDB partitioning */ > + if (dirty & WM_DIRTY_DDB) { > + DRM_DEBUG_KMS("WM: DDB partitioning = %s\n", > + ddb_partitioning == INTEL_DDB_PART_1_2 ? "1/2" : "5/6"); > + > + if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev)) { > + uint32_t tmp = I915_READ(WM_MISC); > + > + if (ddb_partitioning == INTEL_DDB_PART_5_6) > + tmp |= WM_MISC_DATA_PARTITION_5_6; > + else > + tmp &= ~WM_MISC_DATA_PARTITION_5_6; > + > + I915_WRITE(DISP_ARB_CTL, tmp); > + } else { > + uint32_t tmp = I915_READ(DISP_ARB_CTL2); > + > + if (ddb_partitioning == INTEL_DDB_PART_5_6) > + tmp |= DISP_DATA_PARTITION_5_6; > + else > + tmp &= ~DISP_DATA_PARTITION_5_6; > + > + I915_WRITE(DISP_ARB_CTL2, tmp); > + } > + > + dev_priv->wm.ddb_partitioning = ddb_partitioning; > + } > + > + /* program LP1+ watermarks */ > + for (wm_lp = 1; wm_lp <= 3; wm_lp++) { > + static const unsigned int wm_lp_reg[] = { > + [1] = WM1_LP_ILK, > + [2] = WM2_LP_ILK, > + [3] = WM3_LP_ILK, > + }; > + static const unsigned int wm_spr_lp_reg[] = { > + [1] = WM1S_LP_ILK, > + [2] = WM2S_LP_IVB, > + [3] = WM3S_LP_IVB, > + }; > + int level = ilk_wm_lp_to_level(wm_lp, merged); > + const struct intel_wm_level *wm = &merged->wm[level]; > + > + if (!wm->enable) > + break; > + > + if (!(dirty & WM_DIRTY_LP(wm_lp))) > + continue; > + > + DRM_DEBUG_KMS("LP%d (level %d): pri=%u, spr=%u, cur=%u, fbc=%u, latency=%u\n", > + wm_lp, level, wm->pri_val, wm->spr_val, > + wm->cur_val, wm->fbc_val, ilk_wm_lp_latency(dev, level)); > + > + if (wm->spr_val) { > + if (INTEL_INFO(dev)->gen <= 6) { > + WARN_ON(wm_lp != 1); > + I915_WRITE(WM1S_LP_ILK, WM1S_LP_EN | wm->spr_val); > + } else { > + I915_WRITE(wm_spr_lp_reg[wm_lp], wm->spr_val); > + } > + } > + > + I915_WRITE(wm_lp_reg[wm_lp], WM1_LP_SR_EN | > + ilk_wm_lp_latency(dev, level) << WM1_LP_LATENCY_SHIFT | > + wm->fbc_val << WM1_LP_FBC_SHIFT | > + wm->pri_val << WM1_LP_SR_SHIFT | > + wm->cur_val); > + > + dev_priv->wm.hw[wm_lp - 1] = *wm; > + } > +} > + > +static uint32_t > +hsw_compute_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc); > + > +static int ilk_max_wm_level(const struct drm_device *dev, > + const struct intel_pipe_wm *pipe_wm) > +{ > + /* HSW: LP1+ watermarks allowed even with multiple pipes */ > + if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev)) { > + WARN_ON(pipe_wm->sprites_scaled); > + return 4; > + } > + > + /* ILK/SNB/IVB: LP1+ watermarks only w/o scaling */ > + if (pipe_wm->sprites_scaled) > + return 0; > + > + /* ILK/SNB: LP2+ watermarks only w/o sprites */ > + if (INTEL_INFO(dev)->gen <= 6 && pipe_wm->sprites_enabled) > + return 1; > + > + return 3; > +} > + > +/* Compute new watermarks for the pipe */ > +static bool intel_compute_pipe_wm(struct drm_crtc *crtc, > + struct intel_pipe_wm *pipe_wm) > +{ > + struct drm_device *dev = crtc->dev; > + struct drm_i915_private *dev_priv = dev->dev_private; > + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); > + struct hsw_wm_maximums max; > + struct drm_plane *plane; > + struct hsw_pipe_wm_parameters p = {}; > + struct intel_wm_config config = {}; > + int level, max_level; > + > + p.active = intel_crtc_active(crtc); > + > + if (p.active) { > + p.pipe_htotal = intel_crtc->config.adjusted_mode.htotal; > + p.pixel_rate = ilk_pipe_pixel_rate(dev, crtc); > + > + /* TODO: for now, assume primary and cursor planes are always enabled. */ > + p.pri.enabled = true; > + p.pri.bytes_per_pixel = crtc->fb->bits_per_pixel / 8; > + p.pri.horiz_pixels = intel_crtc->config.requested_mode.hdisplay; > + > + p.cur.enabled = true; > + p.cur.bytes_per_pixel = 4; > + p.cur.horiz_pixels = 64; > + > + config.pipes_active = 1; > + } > + > + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > + struct intel_plane *intel_plane = to_intel_plane(plane); > + > + if (intel_plane->pipe != intel_crtc->pipe) > + continue; > + > + p.spr = intel_plane->wm; > + > + config.sprites_enabled |= p.spr.enabled; > + config.sprites_scaled |= p.spr.scaled; > + } > + > + pipe_wm->pipe_enabled = p.active; > + pipe_wm->sprites_enabled = config.sprites_enabled; > + pipe_wm->sprites_scaled = config.sprites_scaled; > + > + max_level = ilk_max_wm_level(dev, pipe_wm); > + > + for (level = 0; level <= max_level; level++) > + ilk_compute_wm_level(dev_priv, level, &p, &pipe_wm->wm[level]); > + > + if (IS_HASWELL(dev)) > + pipe_wm->linetime = hsw_compute_linetime_wm(dev, crtc); > + > + ilk_wm_max(dev, 0, &config, false, &max); > + > + /* At least LP0 must be valid */ > + return ilk_check_wm(0, &max, &pipe_wm->wm[0]); > +} > + > +/* Prepare the pipe to update the its watermarks on the next vblank */ > +static void intel_setup_pipe_wm(struct intel_crtc *intel_crtc, > + const struct intel_pipe_wm *pipe_wm) > +{ > + struct drm_device *dev = intel_crtc->base.dev; > + struct drm_i915_private *dev_priv = dev->dev_private; > + int pipe = intel_crtc->pipe; > + unsigned long flags; > + > + spin_lock_irqsave(&dev_priv->wm.lock, flags); > + > + /* do the watermarks actually need changing? */ > + if (!memcmp(&intel_crtc->wm.pending, pipe_wm, sizeof(*pipe_wm))) > + goto out; > + > + /* > + * We might already have a pending watermark update, in > + * which case we shouldn't grab another vblank reference. > + */ > + if (!intel_crtc->wm.dirty && drm_vblank_get(dev, pipe)) { > + if (intel_crtc->active) > + DRM_ERROR("can't setup watermarks for pipe %c\n", pipe_name(pipe)); > + /* copy the new stuff over anyway. */ > + intel_crtc->wm.pending = *pipe_wm; > + goto out; > + } > + > + /* > + * When going from no-scaling to scaling, disable LP1+ > + * watermarks ahead of time. > + * > + * WaCxSRDisabledForSpriteScaling:ivb > + */ > + /* > + * FIXME is this sufficient of do we need extra vbl waits? > + * Something like this is needed on IVB. Do we need this on ILK/SNB too? > + */ > + if (!intel_crtc->wm.active.sprites_scaled && pipe_wm->sprites_scaled) { > + DRM_DEBUG_KMS("going to enable scaling, disabling LP1+ watermarks\n"); > + ilk_disable_lp_wm(dev_priv, WM_DIRTY_LP(1) | > + WM_DIRTY_LP(2) | WM_DIRTY_LP(3)); > + } > + > + intel_crtc->wm.pending = *pipe_wm; > + intel_crtc->wm.dirty = true; > + DRM_DEBUG_KMS("pipe %c new watermarks are pending\n", pipe_name(pipe)); > + > + out: > + spin_unlock_irqrestore(&dev_priv->wm.lock, flags); > +} > + > +/* Call from vblank irq */ > +void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe) > +{ > + struct drm_i915_private *dev_priv = dev->dev_private; > + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; > + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); > + unsigned long flags; > + > + spin_lock_irqsave(&dev_priv->wm.lock, flags); > + > + if (intel_crtc->wm.dirty) { > + DRM_DEBUG_KMS("pipe %c vblank, programming new watermarks\n", > + pipe_name(pipe)); > + > + drm_vblank_put(dev, pipe); > + > + intel_crtc->wm.active = intel_crtc->wm.pending; > + intel_crtc->wm.dirty = false; > + > + ilk_program_watermarks(dev); > + } > + > + spin_unlock_irqrestore(&dev_priv->wm.lock, flags); > } > > static uint32_t > @@ -2565,271 +2708,24 @@ static void intel_setup_wm_latency(struct drm_device *dev) > intel_print_wm_latency(dev, dev_priv->wm.cur_latency); > } > > -static void hsw_compute_wm_parameters(struct drm_device *dev, > - struct hsw_pipe_wm_parameters *params, > - struct hsw_wm_maximums *lp_max_1_2, > - struct hsw_wm_maximums *lp_max_5_6) > +static void ilk_update_wm(struct drm_crtc *crtc) > { > - struct drm_crtc *crtc; > - struct drm_plane *plane; > - enum pipe pipe; > - struct intel_wm_config config = {}; > + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); > + struct intel_pipe_wm pipe_wm = {}; > + bool ret; > > - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { > - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); > - struct hsw_pipe_wm_parameters *p; > + ret = intel_compute_pipe_wm(crtc, &pipe_wm); > + if (!ret) > + DRM_ERROR("crtc %u (pipe %c) LP0 watermarks invalid\n", > + crtc->base.id, pipe_name(intel_crtc->pipe)); > > - pipe = intel_crtc->pipe; > - p = ¶ms[pipe]; > - > - p->active = intel_crtc_active(crtc); > - if (!p->active) > - continue; > - > - config.pipes_active++; > - > - p->pipe_htotal = intel_crtc->config.adjusted_mode.htotal; > - p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc); > - p->pri.bytes_per_pixel = crtc->fb->bits_per_pixel / 8; > - p->cur.bytes_per_pixel = 4; > - p->pri.horiz_pixels = > - intel_crtc->config.requested_mode.hdisplay; > - p->cur.horiz_pixels = 64; > - /* TODO: for now, assume primary and cursor planes are always enabled. */ > - p->pri.enabled = true; > - p->cur.enabled = true; > - } > - > - list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > - struct intel_plane *intel_plane = to_intel_plane(plane); > - struct hsw_pipe_wm_parameters *p; > - > - pipe = intel_plane->pipe; > - p = ¶ms[pipe]; > - > - p->spr = intel_plane->wm; > - > - config.sprites_enabled |= p->spr.enabled; > - config.sprites_scaled |= p->spr.scaled; > - } > - > - ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_1_2, lp_max_1_2); > - > - /* 5/6 split only in single pipe config on IVB+ */ > - if (INTEL_INFO(dev)->gen >= 7 && config.pipes_active <= 1) > - ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_5_6, lp_max_5_6); > - else > - *lp_max_5_6 = *lp_max_1_2; > -} > - > -static void hsw_compute_wm_results(struct drm_device *dev, > - struct hsw_pipe_wm_parameters *params, > - struct hsw_wm_maximums *lp_maximums, > - struct hsw_wm_values *results) > -{ > - struct drm_i915_private *dev_priv = dev->dev_private; > - struct drm_crtc *crtc; > - struct intel_wm_level lp_results[4] = {}; > - enum pipe pipe; > - int level, max_level, wm_lp; > - > - for (level = 1; level <= 4; level++) > - if (!hsw_compute_lp_wm(dev_priv, level, > - lp_maximums, params, > - &lp_results[level - 1])) > - break; > - max_level = level - 1; > - > - /* The spec says it is preferred to disable FBC WMs instead of disabling > - * a WM level. */ > - results->enable_fbc_wm = true; > - for (level = 1; level <= max_level; level++) { > - if (!lp_results[level - 1].fbc_val > lp_maximums->fbc) { > - results->enable_fbc_wm = false; > - lp_results[level - 1].fbc_val = 0; > - } > - } > - > - memset(results, 0, sizeof(*results)); > - for (wm_lp = 1; wm_lp <= 3; wm_lp++) { > - const struct intel_wm_level *r; > - > - level = (max_level == 4 && wm_lp > 1) ? wm_lp + 1 : wm_lp; > - if (level > max_level) > - break; > - > - r = &lp_results[level - 1]; > - results->wm_lp[wm_lp - 1] = HSW_WM_LP_VAL(level * 2, > - r->fbc_val, > - r->pri_val, > - r->cur_val); > - results->wm_lp_spr[wm_lp - 1] = r->spr_val; > - } > - > - for_each_pipe(pipe) > - results->wm_pipe[pipe] = hsw_compute_wm_pipe(dev_priv, pipe, > - ¶ms[pipe]); > - > - for_each_pipe(pipe) { > - crtc = dev_priv->pipe_to_crtc_mapping[pipe]; > - results->wm_linetime[pipe] = hsw_compute_linetime_wm(dev, crtc); > - } > -} > - > -/* Find the result with the highest level enabled. Check for enable_fbc_wm in > - * case both are at the same level. Prefer r1 in case they're the same. */ > -static struct hsw_wm_values *hsw_find_best_result(struct hsw_wm_values *r1, > - struct hsw_wm_values *r2) > -{ > - int i, val_r1 = 0, val_r2 = 0; > - > - for (i = 0; i < 3; i++) { > - if (r1->wm_lp[i] & WM3_LP_EN) > - val_r1 = r1->wm_lp[i] & WM1_LP_LATENCY_MASK; > - if (r2->wm_lp[i] & WM3_LP_EN) > - val_r2 = r2->wm_lp[i] & WM1_LP_LATENCY_MASK; > - } > - > - if (val_r1 == val_r2) { > - if (r2->enable_fbc_wm && !r1->enable_fbc_wm) > - return r2; > - else > - return r1; > - } else if (val_r1 > val_r2) { > - return r1; > - } else { > - return r2; > - } > -} > - > -/* > - * The spec says we shouldn't write when we don't need, because every write > - * causes WMs to be re-evaluated, expending some power. > - */ > -static void hsw_write_wm_values(struct drm_i915_private *dev_priv, > - struct hsw_wm_values *results, > - enum intel_ddb_partitioning partitioning) > -{ > - struct hsw_wm_values previous; > - uint32_t val; > - enum intel_ddb_partitioning prev_partitioning; > - bool prev_enable_fbc_wm; > - > - previous.wm_pipe[0] = I915_READ(WM0_PIPEA_ILK); > - previous.wm_pipe[1] = I915_READ(WM0_PIPEB_ILK); > - previous.wm_pipe[2] = I915_READ(WM0_PIPEC_IVB); > - previous.wm_lp[0] = I915_READ(WM1_LP_ILK); > - previous.wm_lp[1] = I915_READ(WM2_LP_ILK); > - previous.wm_lp[2] = I915_READ(WM3_LP_ILK); > - previous.wm_lp_spr[0] = I915_READ(WM1S_LP_ILK); > - previous.wm_lp_spr[1] = I915_READ(WM2S_LP_IVB); > - previous.wm_lp_spr[2] = I915_READ(WM3S_LP_IVB); > - previous.wm_linetime[0] = I915_READ(PIPE_WM_LINETIME(PIPE_A)); > - previous.wm_linetime[1] = I915_READ(PIPE_WM_LINETIME(PIPE_B)); > - previous.wm_linetime[2] = I915_READ(PIPE_WM_LINETIME(PIPE_C)); > - > - prev_partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ? > - INTEL_DDB_PART_5_6 : INTEL_DDB_PART_1_2; > - > - prev_enable_fbc_wm = !(I915_READ(DISP_ARB_CTL) & DISP_FBC_WM_DIS); > - > - if (memcmp(results->wm_pipe, previous.wm_pipe, > - sizeof(results->wm_pipe)) == 0 && > - memcmp(results->wm_lp, previous.wm_lp, > - sizeof(results->wm_lp)) == 0 && > - memcmp(results->wm_lp_spr, previous.wm_lp_spr, > - sizeof(results->wm_lp_spr)) == 0 && > - memcmp(results->wm_linetime, previous.wm_linetime, > - sizeof(results->wm_linetime)) == 0 && > - partitioning == prev_partitioning && > - results->enable_fbc_wm == prev_enable_fbc_wm) > - return; > - > - if (previous.wm_lp[2] != 0) > - I915_WRITE(WM3_LP_ILK, 0); > - if (previous.wm_lp[1] != 0) > - I915_WRITE(WM2_LP_ILK, 0); > - if (previous.wm_lp[0] != 0) > - I915_WRITE(WM1_LP_ILK, 0); > - > - if (previous.wm_pipe[0] != results->wm_pipe[0]) > - I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]); > - if (previous.wm_pipe[1] != results->wm_pipe[1]) > - I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]); > - if (previous.wm_pipe[2] != results->wm_pipe[2]) > - I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]); > - > - if (previous.wm_linetime[0] != results->wm_linetime[0]) > - I915_WRITE(PIPE_WM_LINETIME(PIPE_A), results->wm_linetime[0]); > - if (previous.wm_linetime[1] != results->wm_linetime[1]) > - I915_WRITE(PIPE_WM_LINETIME(PIPE_B), results->wm_linetime[1]); > - if (previous.wm_linetime[2] != results->wm_linetime[2]) > - I915_WRITE(PIPE_WM_LINETIME(PIPE_C), results->wm_linetime[2]); > - > - if (prev_partitioning != partitioning) { > - val = I915_READ(WM_MISC); > - if (partitioning == INTEL_DDB_PART_1_2) > - val &= ~WM_MISC_DATA_PARTITION_5_6; > - else > - val |= WM_MISC_DATA_PARTITION_5_6; > - I915_WRITE(WM_MISC, val); > - } > - > - if (prev_enable_fbc_wm != results->enable_fbc_wm) { > - val = I915_READ(DISP_ARB_CTL); > - if (results->enable_fbc_wm) > - val &= ~DISP_FBC_WM_DIS; > - else > - val |= DISP_FBC_WM_DIS; > - I915_WRITE(DISP_ARB_CTL, val); > - } > - > - if (previous.wm_lp_spr[0] != results->wm_lp_spr[0]) > - I915_WRITE(WM1S_LP_ILK, results->wm_lp_spr[0]); > - if (previous.wm_lp_spr[1] != results->wm_lp_spr[1]) > - I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]); > - if (previous.wm_lp_spr[2] != results->wm_lp_spr[2]) > - I915_WRITE(WM3S_LP_IVB, results->wm_lp_spr[2]); > - > - if (results->wm_lp[0] != 0) > - I915_WRITE(WM1_LP_ILK, results->wm_lp[0]); > - if (results->wm_lp[1] != 0) > - I915_WRITE(WM2_LP_ILK, results->wm_lp[1]); > - if (results->wm_lp[2] != 0) > - I915_WRITE(WM3_LP_ILK, results->wm_lp[2]); > -} > - > -static void haswell_update_wm(struct drm_crtc *crtc) > -{ > - struct drm_device *dev = crtc->dev; > - struct drm_i915_private *dev_priv = dev->dev_private; > - struct hsw_wm_maximums lp_max_1_2, lp_max_5_6; > - struct hsw_pipe_wm_parameters params[3]; > - struct hsw_wm_values results_1_2, results_5_6, *best_results; > - enum intel_ddb_partitioning partitioning; > - > - hsw_compute_wm_parameters(dev, params, &lp_max_1_2, &lp_max_5_6); > - > - hsw_compute_wm_results(dev, params, > - &lp_max_1_2, &results_1_2); > - if (lp_max_1_2.pri != lp_max_5_6.pri) { > - hsw_compute_wm_results(dev, params, > - &lp_max_5_6, &results_5_6); > - best_results = hsw_find_best_result(&results_1_2, &results_5_6); > - } else { > - best_results = &results_1_2; > - } > - > - partitioning = (best_results == &results_1_2) ? > - INTEL_DDB_PART_1_2 : INTEL_DDB_PART_5_6; > - > - hsw_write_wm_values(dev_priv, best_results, partitioning); > + intel_setup_pipe_wm(intel_crtc, &pipe_wm); > } > > -static void haswell_update_sprite_wm(struct drm_plane *plane, > - struct drm_crtc *crtc, > - uint32_t sprite_width, int pixel_size, > - bool enabled, bool scaled) > +static void ilk_update_sprite_wm(struct drm_plane *plane, > + struct drm_crtc *crtc, > + uint32_t sprite_width, int pixel_size, > + bool enabled, bool scaled) > { > struct intel_plane *intel_plane = to_intel_plane(plane); > > @@ -2838,169 +2734,7 @@ static void haswell_update_sprite_wm(struct drm_plane *plane, > intel_plane->wm.horiz_pixels = sprite_width; > intel_plane->wm.bytes_per_pixel = pixel_size; > > - haswell_update_wm(crtc); > -} > - > -static bool > -sandybridge_compute_sprite_wm(struct drm_device *dev, int plane, > - uint32_t sprite_width, int pixel_size, > - const struct intel_watermark_params *display, > - int display_latency_ns, int *sprite_wm) > -{ > - struct drm_crtc *crtc; > - int clock; > - int entries, tlb_miss; > - > - crtc = intel_get_crtc_for_plane(dev, plane); > - if (!intel_crtc_active(crtc)) { > - *sprite_wm = display->guard_size; > - return false; > - } > - > - clock = crtc->mode.clock; > - > - /* Use the small buffer method to calculate the sprite watermark */ > - entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000; > - tlb_miss = display->fifo_size*display->cacheline_size - > - sprite_width * 8; > - if (tlb_miss > 0) > - entries += tlb_miss; > - entries = DIV_ROUND_UP(entries, display->cacheline_size); > - *sprite_wm = entries + display->guard_size; > - if (*sprite_wm > (int)display->max_wm) > - *sprite_wm = display->max_wm; > - > - return true; > -} > - > -static bool > -sandybridge_compute_sprite_srwm(struct drm_device *dev, int plane, > - uint32_t sprite_width, int pixel_size, > - const struct intel_watermark_params *display, > - int latency_ns, int *sprite_wm) > -{ > - struct drm_crtc *crtc; > - unsigned long line_time_us; > - int clock; > - int line_count, line_size; > - int small, large; > - int entries; > - > - if (!latency_ns) { > - *sprite_wm = 0; > - return false; > - } > - > - crtc = intel_get_crtc_for_plane(dev, plane); > - clock = crtc->mode.clock; > - if (!clock) { > - *sprite_wm = 0; > - return false; > - } > - > - line_time_us = (sprite_width * 1000) / clock; > - if (!line_time_us) { > - *sprite_wm = 0; > - return false; > - } > - > - line_count = (latency_ns / line_time_us + 1000) / 1000; > - line_size = sprite_width * 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), display->cacheline_size); > - *sprite_wm = entries + display->guard_size; > - > - return *sprite_wm > 0x3ff ? false : true; > -} > - > -static void sandybridge_update_sprite_wm(struct drm_plane *plane, > - struct drm_crtc *crtc, > - uint32_t sprite_width, int pixel_size, > - bool enable, bool scaled) > -{ > - struct drm_device *dev = plane->dev; > - struct drm_i915_private *dev_priv = dev->dev_private; > - int pipe = to_intel_plane(plane)->pipe; > - int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */ > - u32 val; > - int sprite_wm, reg; > - int ret; > - > - if (!enable) > - return; > - > - switch (pipe) { > - case 0: > - reg = WM0_PIPEA_ILK; > - break; > - case 1: > - reg = WM0_PIPEB_ILK; > - break; > - case 2: > - reg = WM0_PIPEC_IVB; > - break; > - default: > - return; /* bad pipe */ > - } > - > - ret = sandybridge_compute_sprite_wm(dev, pipe, sprite_width, pixel_size, > - &sandybridge_display_wm_info, > - latency, &sprite_wm); > - if (!ret) { > - DRM_DEBUG_KMS("failed to compute sprite wm for pipe %c\n", > - pipe_name(pipe)); > - return; > - } > - > - val = I915_READ(reg); > - val &= ~WM0_PIPE_SPRITE_MASK; > - I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT)); > - DRM_DEBUG_KMS("sprite watermarks For pipe %c - %d\n", pipe_name(pipe), sprite_wm); > - > - > - ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, > - pixel_size, > - &sandybridge_display_srwm_info, > - SNB_READ_WM1_LATENCY() * 500, > - &sprite_wm); > - if (!ret) { > - DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %c\n", > - pipe_name(pipe)); > - return; > - } > - I915_WRITE(WM1S_LP_ILK, sprite_wm); > - > - /* Only IVB has two more LP watermarks for sprite */ > - if (!IS_IVYBRIDGE(dev)) > - return; > - > - ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, > - pixel_size, > - &sandybridge_display_srwm_info, > - SNB_READ_WM2_LATENCY() * 500, > - &sprite_wm); > - if (!ret) { > - DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %c\n", > - pipe_name(pipe)); > - return; > - } > - I915_WRITE(WM2S_LP_IVB, sprite_wm); > - > - ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, > - pixel_size, > - &sandybridge_display_srwm_info, > - SNB_READ_WM3_LATENCY() * 500, > - &sprite_wm); > - if (!ret) { > - DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %c\n", > - pipe_name(pipe)); > - return; > - } > - I915_WRITE(WM3S_LP_IVB, sprite_wm); > + ilk_update_wm(crtc); > } > > /** > @@ -5372,6 +5106,8 @@ void intel_init_pm(struct drm_device *dev) > { > struct drm_i915_private *dev_priv = dev->dev_private; > > + spin_lock_init(&dev_priv->wm.lock); > + > if (I915_HAS_FBC(dev)) { > if (HAS_PCH_SPLIT(dev)) { > dev_priv->display.fbc_enabled = ironlake_fbc_enabled; > @@ -5407,9 +5143,10 @@ void intel_init_pm(struct drm_device *dev) > if (IS_GEN5(dev)) { > if (dev_priv->wm.pri_latency[1] && > dev_priv->wm.spr_latency[1] && > - dev_priv->wm.cur_latency[1]) > - dev_priv->display.update_wm = ironlake_update_wm; > - else { > + dev_priv->wm.cur_latency[1]) { > + dev_priv->display.update_wm = ilk_update_wm; > + dev_priv->display.update_sprite_wm = ilk_update_sprite_wm; > + } else { > DRM_DEBUG_KMS("Failed to get proper latency. " > "Disable CxSR\n"); > dev_priv->display.update_wm = NULL; > @@ -5419,8 +5156,8 @@ void intel_init_pm(struct drm_device *dev) > if (dev_priv->wm.pri_latency[0] && > dev_priv->wm.spr_latency[0] && > dev_priv->wm.cur_latency[0]) { > - dev_priv->display.update_wm = sandybridge_update_wm; > - dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; > + dev_priv->display.update_wm = ilk_update_wm; > + dev_priv->display.update_sprite_wm = ilk_update_sprite_wm; > } else { > DRM_DEBUG_KMS("Failed to read display plane latency. " > "Disable CxSR\n"); > @@ -5431,8 +5168,8 @@ void intel_init_pm(struct drm_device *dev) > if (dev_priv->wm.pri_latency[0] && > dev_priv->wm.spr_latency[0] && > dev_priv->wm.cur_latency[0]) { > - dev_priv->display.update_wm = ivybridge_update_wm; > - dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; > + dev_priv->display.update_wm = ilk_update_wm; > + dev_priv->display.update_sprite_wm = ilk_update_sprite_wm; > } else { > DRM_DEBUG_KMS("Failed to read display plane latency. " > "Disable CxSR\n"); > @@ -5443,9 +5180,8 @@ void intel_init_pm(struct drm_device *dev) > if (dev_priv->wm.pri_latency[0] && > dev_priv->wm.spr_latency[0] && > dev_priv->wm.cur_latency[0]) { > - dev_priv->display.update_wm = haswell_update_wm; > - dev_priv->display.update_sprite_wm = > - haswell_update_sprite_wm; > + dev_priv->display.update_wm = ilk_update_wm; > + dev_priv->display.update_sprite_wm = ilk_update_sprite_wm; > } else { > DRM_DEBUG_KMS("Failed to read display plane latency. " > "Disable CxSR\n"); > -- > 1.8.1.5 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx at lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Paulo Zanoni