[Intel-gfx] [PATCH 30/35] drm/i915: Replace the ILK/SNB/IVB/HSW watermark code

Paulo Zanoni przanoni at gmail.com
Fri Jul 5 19:51:36 CEST 2013


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, &params[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 = &params[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 = &params[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,
> -                                                            &params[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



More information about the Intel-gfx mailing list