On Mon, Apr 24, 2017 at 09:34:42AM +0200, Maarten Lankhorst wrote:
> On 21-04-17 20:14, ville.syrj...@linux.intel.com wrote:
> > From: Ville Syrjälä <ville.syrj...@linux.intel.com>
> >
> > Implement proper two stage watermark programming for g4x. As with
> > other pre-SKL platforms, the watermark registers aren't double
> > buffered on g4x. Hence we must sequence the watermark update
> > carefully around plane updates.
> >
> > The code is quite heavily modelled on the VLV/CHV code, with some
> > fairly significant differences due to the different hardware
> > architecture:
> > * g4x doesn't use inverted watermark values
> > * CxSR actually affects the watermarks since it controls memory self
> >   refresh in addition to the max FIFO mode
> > * A further HPLL SR mode is possible with higher memory wakeup
> >   latency
> > * g4x has FBC2 and so it also has FBC watermarks
> > * max FIFO mode for primary plane only (cursor is allowed, sprite is not)
> > * g4x has no manual FIFO repartitioning
> > * some TLB miss related workarounds are needed for the watermarks
> >
> > Actually the hardware is quite similar to ILK+ in many ways. The
> > most visible differences are in the actual watermakr register
> > layout. ILK revamped that part quite heavily whereas g4x is still
> > using the layout inherited from earlier platforms.
> >
> > Note that we didn't previously enable the HPLL SR on g4x. So in order
> > to not introduce too many functional changes in this patch I've not
> > actually enabled it here either, even though the code is now fully
> > ready for it. We'll enable it separately later on.
> >
> > Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
> > ---
> >  drivers/gpu/drm/i915/i915_debugfs.c  |  12 +-
> >  drivers/gpu/drm/i915/i915_drv.h      |  12 +
> >  drivers/gpu/drm/i915/intel_display.c |  25 +-
> >  drivers/gpu/drm/i915/intel_drv.h     |  28 ++
> >  drivers/gpu/drm/i915/intel_pm.c      | 942 
> > +++++++++++++++++++++++++++--------
> >  5 files changed, 792 insertions(+), 227 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/i915_debugfs.c 
> > b/drivers/gpu/drm/i915/i915_debugfs.c
> > index 870c470177b5..69550d31099e 100644
> > --- a/drivers/gpu/drm/i915/i915_debugfs.c
> > +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> > @@ -3898,6 +3898,8 @@ static void wm_latency_show(struct seq_file *m, const 
> > uint16_t wm[8])
> >             num_levels = 3;
> >     else if (IS_VALLEYVIEW(dev_priv))
> >             num_levels = 1;
> > +   else if (IS_G4X(dev_priv))
> > +           num_levels = 3;
> >     else
> >             num_levels = ilk_wm_max_level(dev_priv) + 1;
> >  
> > @@ -3910,8 +3912,10 @@ static void wm_latency_show(struct seq_file *m, 
> > const uint16_t wm[8])
> >              * - WM1+ latency values in 0.5us units
> >              * - latencies are in us on gen9/vlv/chv
> >              */
> > -           if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) ||
> > -               IS_CHERRYVIEW(dev_priv))
> > +           if (INTEL_GEN(dev_priv) >= 9 ||
> > +               IS_VALLEYVIEW(dev_priv) ||
> > +               IS_CHERRYVIEW(dev_priv) ||
> > +               IS_G4X(dev_priv))
> >                     latency *= 10;
> >             else if (level > 0)
> >                     latency *= 5;
> > @@ -3972,7 +3976,7 @@ static int pri_wm_latency_open(struct inode *inode, 
> > struct file *file)
> >  {
> >     struct drm_i915_private *dev_priv = inode->i_private;
> >  
> > -   if (INTEL_GEN(dev_priv) < 5)
> > +   if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >             return -ENODEV;
> >  
> >     return single_open(file, pri_wm_latency_show, dev_priv);
> > @@ -4014,6 +4018,8 @@ static ssize_t wm_latency_write(struct file *file, 
> > const char __user *ubuf,
> >             num_levels = 3;
> >     else if (IS_VALLEYVIEW(dev_priv))
> >             num_levels = 1;
> > +   else if (IS_G4X(dev_priv))
> > +           num_levels = 3;
> >     else
> >             num_levels = ilk_wm_max_level(dev_priv) + 1;
> >  
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h 
> > b/drivers/gpu/drm/i915/i915_drv.h
> > index 0a393fdc53d1..6df8bff7f5a7 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -1762,11 +1762,13 @@ struct ilk_wm_values {
> >  
> >  struct g4x_pipe_wm {
> >     uint16_t plane[I915_MAX_PLANES];
> > +   uint16_t fbc;
> >  };
> >  
> >  struct g4x_sr_wm {
> >     uint16_t plane;
> >     uint16_t cursor;
> > +   uint16_t fbc;
> >  };
> >  
> >  struct vlv_wm_ddl_values {
> > @@ -1781,6 +1783,15 @@ struct vlv_wm_values {
> >     bool cxsr;
> >  };
> >  
> > +struct g4x_wm_values {
> > +   struct g4x_pipe_wm pipe[2];
> > +   struct g4x_sr_wm sr;
> > +   struct g4x_sr_wm hpll;
> > +   bool cxsr;
> > +   bool hpll_en;
> > +   bool fbc_en;
> > +};
> > +
> >  struct skl_ddb_entry {
> >     uint16_t start, end;    /* in number of blocks, 'end' is exclusive */
> >  };
> > @@ -2410,6 +2421,7 @@ struct drm_i915_private {
> >                     struct ilk_wm_values hw;
> >                     struct skl_wm_values skl_hw;
> >                     struct vlv_wm_values vlv;
> > +                   struct g4x_wm_values g4x;
> >             };
> >  
> >             uint8_t max_level;
> > diff --git a/drivers/gpu/drm/i915/intel_display.c 
> > b/drivers/gpu/drm/i915/intel_display.c
> > index 85b9e2f521a0..0f42263c3f76 100644
> > --- a/drivers/gpu/drm/i915/intel_display.c
> > +++ b/drivers/gpu/drm/i915/intel_display.c
> > @@ -5719,6 +5719,8 @@ static void i9xx_set_pll_dividers(struct intel_crtc 
> > *crtc)
> >  static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
> >                          struct drm_atomic_state *old_state)
> >  {
> > +   struct intel_atomic_state *old_intel_state =
> > +           to_intel_atomic_state(old_state);
> >     struct drm_crtc *crtc = pipe_config->base.crtc;
> >     struct drm_device *dev = crtc->dev;
> >     struct drm_i915_private *dev_priv = to_i915(dev);
> > @@ -5751,7 +5753,11 @@ static void i9xx_crtc_enable(struct intel_crtc_state 
> > *pipe_config,
> >  
> >     intel_color_load_luts(&pipe_config->base);
> >  
> > -   intel_update_watermarks(intel_crtc);
> > +   if (dev_priv->display.initial_watermarks != NULL)
> > +           dev_priv->display.initial_watermarks(old_intel_state,
> > +                                                intel_crtc->config);
> > +   else
> > +           intel_update_watermarks(intel_crtc);
> >     intel_enable_pipe(intel_crtc);
> >  
> >     assert_vblank_disabled(crtc);
> > @@ -10852,21 +10858,21 @@ int intel_plane_atomic_calc_changes(struct 
> > drm_crtc_state *crtc_state,
> >                      turn_off, turn_on, mode_changed);
> >  
> >     if (turn_on) {
> > -           if (INTEL_GEN(dev_priv) < 5)
> > +           if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >                     pipe_config->update_wm_pre = true;
> >  
> >             /* must disable cxsr around plane enable/disable */
> >             if (plane->id != PLANE_CURSOR)
> >                     pipe_config->disable_cxsr = true;
> >     } else if (turn_off) {
> > -           if (INTEL_GEN(dev_priv) < 5)
> > +           if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >                     pipe_config->update_wm_post = true;
> >  
> >             /* must disable cxsr around plane enable/disable */
> >             if (plane->id != PLANE_CURSOR)
> >                     pipe_config->disable_cxsr = true;
> >     } else if (intel_wm_need_update(&plane->base, plane_state)) {
> > -           if (INTEL_GEN(dev_priv) < 5) {
> > +           if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv)) {
> >                     /* FIXME bollocks */
> >                     pipe_config->update_wm_pre = true;
> >                     pipe_config->update_wm_post = true;
> > @@ -11290,7 +11296,8 @@ clear_intel_crtc_state(struct intel_crtc_state 
> > *crtc_state)
> >     shared_dpll = crtc_state->shared_dpll;
> >     dpll_hw_state = crtc_state->dpll_hw_state;
> >     force_thru = crtc_state->pch_pfit.force_thru;
> > -   if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +   if (IS_G4X(dev_priv) ||
> > +       IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> >             wm_state = crtc_state->wm;
> >  
> >     /* Keep base drm_crtc_state intact, only clear our extended struct */
> > @@ -11302,7 +11309,8 @@ clear_intel_crtc_state(struct intel_crtc_state 
> > *crtc_state)
> >     crtc_state->shared_dpll = shared_dpll;
> >     crtc_state->dpll_hw_state = dpll_hw_state;
> >     crtc_state->pch_pfit.force_thru = force_thru;
> > -   if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +   if (IS_G4X(dev_priv) ||
> > +       IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> >             crtc_state->wm = wm_state;
> >  }
> >  
> > @@ -15527,7 +15535,10 @@ intel_modeset_setup_hw_state(struct drm_device 
> > *dev)
> >             pll->on = false;
> >     }
> >  
> > -   if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
> > +   if (IS_G4X(dev_priv)) {
> > +           g4x_wm_get_hw_state(dev);
> > +           g4x_wm_sanitize(dev_priv);
> > +   } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
> >             vlv_wm_get_hw_state(dev);
> >             vlv_wm_sanitize(dev_priv);
> >     } else if (IS_GEN9(dev_priv)) {
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h 
> > b/drivers/gpu/drm/i915/intel_drv.h
> > index d1cdd10998fa..f530df71a480 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -522,6 +522,22 @@ struct vlv_fifo_state {
> >     u16 plane[I915_MAX_PLANES];
> >  };
> >  
> > +enum g4x_wm_level {
> > +   G4X_WM_LEVEL_NORMAL,
> > +   G4X_WM_LEVEL_SR,
> > +   G4X_WM_LEVEL_HPLL,
> > +   NUM_G4X_WM_LEVELS,
> > +};
> > +
> > +struct g4x_wm_state {
> > +   struct g4x_pipe_wm wm;
> > +   struct g4x_sr_wm sr;
> > +   struct g4x_sr_wm hpll;
> > +   bool cxsr;
> > +   bool hpll_en;
> > +   bool fbc_en;
> > +};
> > +
> >  struct intel_crtc_wm_state {
> >     union {
> >             struct {
> > @@ -557,6 +573,15 @@ struct intel_crtc_wm_state {
> >                     /* display FIFO split */
> >                     struct vlv_fifo_state fifo_state;
> >             } vlv;
> > +
> > +           struct {
> > +                   /* "raw" watermarks */
> > +                   struct g4x_pipe_wm raw[NUM_G4X_WM_LEVELS];
> > +                   /* intermediate watermarks */
> > +                   struct g4x_wm_state intermediate;
> > +                   /* optimal watermarks */
> > +                   struct g4x_wm_state optimal;
> > +           } g4x;
> >     };
> >  
> >     /*
> > @@ -794,6 +819,7 @@ struct intel_crtc {
> >             union {
> >                     struct intel_pipe_wm ilk;
> >                     struct vlv_wm_state vlv;
> > +                   struct g4x_wm_state g4x;
> >             } active;
> >     } wm;
> >  
> > @@ -1841,6 +1867,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
> >                 struct intel_rps_client *rps,
> >                 unsigned long submitted);
> >  void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req);
> > +void g4x_wm_get_hw_state(struct drm_device *dev);
> >  void vlv_wm_get_hw_state(struct drm_device *dev);
> >  void ilk_wm_get_hw_state(struct drm_device *dev);
> >  void skl_wm_get_hw_state(struct drm_device *dev);
> > @@ -1848,6 +1875,7 @@ void skl_ddb_get_hw_state(struct drm_i915_private 
> > *dev_priv,
> >                       struct skl_ddb_allocation *ddb /* out */);
> >  void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc,
> >                           struct skl_pipe_wm *out);
> > +void g4x_wm_sanitize(struct drm_i915_private *dev_priv);
> >  void vlv_wm_sanitize(struct drm_i915_private *dev_priv);
> >  bool intel_can_enable_sagv(struct drm_atomic_state *state);
> >  int intel_enable_sagv(struct drm_i915_private *dev_priv);
> > diff --git a/drivers/gpu/drm/i915/intel_pm.c 
> > b/drivers/gpu/drm/i915/intel_pm.c
> > index 61b67994c4a8..9b0a6a4572ce 100644
> > --- a/drivers/gpu/drm/i915/intel_pm.c
> > +++ b/drivers/gpu/drm/i915/intel_pm.c
> > @@ -429,7 +429,10 @@ bool intel_set_memory_cxsr(struct drm_i915_private 
> > *dev_priv, bool enable)
> >  
> >     mutex_lock(&dev_priv->wm.wm_mutex);
> >     ret = _intel_set_memory_cxsr(dev_priv, enable);
> > -   dev_priv->wm.vlv.cxsr = enable;
> > +   if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +           dev_priv->wm.vlv.cxsr = enable;
> > +   else if (IS_G4X(dev_priv))
> > +           dev_priv->wm.g4x.cxsr = enable;
> >     mutex_unlock(&dev_priv->wm.wm_mutex);
> >  
> >     return ret;
> > @@ -568,20 +571,6 @@ static const struct intel_watermark_params 
> > pineview_cursor_hplloff_wm = {
> >     .guard_size = PINEVIEW_CURSOR_GUARD_WM,
> >     .cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
> >  };
> > -static const struct intel_watermark_params g4x_wm_info = {
> > -   .fifo_size = G4X_FIFO_SIZE,
> > -   .max_wm = G4X_MAX_WM,
> > -   .default_wm = G4X_MAX_WM,
> > -   .guard_size = 2,
> > -   .cacheline_size = G4X_FIFO_LINE_SIZE,
> > -};
> > -static const struct intel_watermark_params g4x_cursor_wm_info = {
> > -   .fifo_size = I965_CURSOR_FIFO,
> > -   .max_wm = I965_CURSOR_MAX_WM,
> > -   .default_wm = I965_CURSOR_DFT_WM,
> > -   .guard_size = 2,
> > -   .cacheline_size = G4X_FIFO_LINE_SIZE,
> > -};
> >  static const struct intel_watermark_params i965_cursor_wm_info = {
> >     .fifo_size = I965_CURSOR_FIFO,
> >     .max_wm = I965_CURSOR_MAX_WM,
> > @@ -780,6 +769,16 @@ static unsigned int intel_calculate_wm(int pixel_rate,
> >     return wm_size;
> >  }
> >  
> > +static bool is_disabling(int old, int new, int threshold)
> > +{
> > +   return old >= threshold && new < threshold;
> > +}
> > +
> > +static bool is_enabling(int old, int new, int threshold)
> > +{
> > +   return old < threshold && new >= threshold;
> > +}
> > +
> >  static int intel_wm_num_levels(struct drm_i915_private *dev_priv)
> >  {
> >     return dev_priv->wm.max_level + 1;
> > @@ -911,138 +910,28 @@ static int g4x_tlb_miss_wa(int fifo_size, int width, 
> > int cpp)
> >     return max(0, tlb_miss);
> >  }
> >  
> > -static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
> > -                       int plane,
> > -                       const struct intel_watermark_params *display,
> > -                       int display_latency_ns,
> > -                       const struct intel_watermark_params *cursor,
> > -                       int cursor_latency_ns,
> > -                       int *plane_wm,
> > -                       int *cursor_wm)
> > +static void g4x_write_wm_values(struct drm_i915_private *dev_priv,
> > +                           const struct g4x_wm_values *wm)
> >  {
> > -   struct intel_crtc *crtc;
> > -   const struct drm_display_mode *adjusted_mode;
> > -   const struct drm_framebuffer *fb;
> > -   int htotal, plane_width, cursor_width, clock, cpp;
> > -   int entries;
> > -
> > -   crtc = intel_get_crtc_for_plane(dev_priv, plane);
> > -   if (!intel_crtc_active(crtc)) {
> > -           *cursor_wm = cursor->guard_size;
> > -           *plane_wm = display->guard_size;
> > -           return false;
> > -   }
> > -
> > -   adjusted_mode = &crtc->config->base.adjusted_mode;
> > -   fb = crtc->base.primary->state->fb;
> > -   clock = adjusted_mode->crtc_clock;
> > -   htotal = adjusted_mode->crtc_htotal;
> > -   plane_width = crtc->config->pipe_src_w;
> > -   cursor_width = crtc->base.cursor->state->crtc_w;
> > -   cpp = fb->format->cpp[0];
> > -
> > -   /* Use the small buffer method to calculate plane watermark */
> > -   entries = intel_wm_method1(clock, cpp, display_latency_ns / 100);
> > -   entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> > -   entries = DIV_ROUND_UP(entries, display->cacheline_size);
> > -   *plane_wm = entries + display->guard_size;
> > -   if (*plane_wm > (int)display->max_wm)
> > -           *plane_wm = display->max_wm;
> > -
> > -   /* Use the large buffer method to calculate cursor watermark */
> > -   entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> > -                              cursor_latency_ns / 100);
> > -   entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> > -   entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> > -   *cursor_wm = entries + cursor->guard_size;
> > -   if (*cursor_wm > (int)cursor->max_wm)
> > -           *cursor_wm = (int)cursor->max_wm;
> > -
> > -   return true;
> > -}
> > -
> > -/*
> > - * 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 g4x_check_srwm(struct drm_i915_private *dev_priv,
> > -                      int display_wm, int cursor_wm,
> > -                      const struct intel_watermark_params *display,
> > -                      const struct intel_watermark_params *cursor)
> > -{
> > -   DRM_DEBUG_KMS("SR watermark: display plane %d, cursor %d\n",
> > -                 display_wm, cursor_wm);
> > -
> > -   if (display_wm > display->max_wm) {
> > -           DRM_DEBUG_KMS("display watermark is too large(%d/%u), 
> > disabling\n",
> > -                         display_wm, display->max_wm);
> > -           return false;
> > -   }
> > -
> > -   if (cursor_wm > cursor->max_wm) {
> > -           DRM_DEBUG_KMS("cursor watermark is too large(%d/%u), 
> > disabling\n",
> > -                         cursor_wm, cursor->max_wm);
> > -           return false;
> > -   }
> > -
> > -   if (!(display_wm || cursor_wm)) {
> > -           DRM_DEBUG_KMS("SR latency is 0, disabling\n");
> > -           return false;
> > -   }
> > -
> > -   return true;
> > -}
> > -
> > -static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
> > -                        int plane,
> > -                        int latency_ns,
> > -                        const struct intel_watermark_params *display,
> > -                        const struct intel_watermark_params *cursor,
> > -                        int *display_wm, int *cursor_wm)
> > -{
> > -   struct intel_crtc *crtc;
> > -   const struct drm_display_mode *adjusted_mode;
> > -   const struct drm_framebuffer *fb;
> > -   int plane_width, cursor_width, htotal, cpp, clock;
> > -   int small, large;
> > -   int entries;
> > -
> > -   if (!latency_ns) {
> > -           *display_wm = *cursor_wm = 0;
> > -           return false;
> > -   }
> > -
> > -   crtc = intel_get_crtc_for_plane(dev_priv, plane);
> > -   adjusted_mode = &crtc->config->base.adjusted_mode;
> > -   fb = crtc->base.primary->state->fb;
> > -   clock = adjusted_mode->crtc_clock;
> > -   htotal = adjusted_mode->crtc_htotal;
> > -   plane_width = crtc->config->pipe_src_w;
> > -   cursor_width = crtc->base.cursor->state->crtc_w;
> > -   cpp = fb->format->cpp[0];
> > -
> > -   /* Use the minimum of the small and large buffer method for primary */
> > -   small = intel_wm_method1(clock, cpp, latency_ns / 100);
> > -   large = intel_wm_method2(clock, htotal, plane_width, cpp,
> > -                            latency_ns / 100);
> > -   entries = min(small, large);
> > -   entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> > -   entries = DIV_ROUND_UP(entries, display->cacheline_size);
> > -   *display_wm = entries + display->guard_size;
> > -
> > -   /* calculate the self-refresh watermark for display cursor */
> > -   entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> > -                              latency_ns / 100);
> > -   entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> > -   entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> > -   *cursor_wm = entries + cursor->guard_size;
> > -
> > -   return g4x_check_srwm(dev_priv,
> > -                         *display_wm, *cursor_wm,
> > -                         display, cursor);
> > +   I915_WRITE(DSPFW1,
> > +              FW_WM(wm->sr.plane, SR) |
> > +              FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) |
> > +              FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) |
> > +              FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA));
> > +   I915_WRITE(DSPFW2,
> > +              (wm->fbc_en ? DSPFW_FBC_SR_EN : 0) |
> > +              FW_WM(wm->sr.fbc, FBC_SR) |
> > +              FW_WM(wm->hpll.fbc, FBC_HPLL_SR) |
> > +              FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEB) |
> > +              FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) |
> > +              FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA));
> > +   I915_WRITE(DSPFW3,
> > +              (wm->hpll_en ? DSPFW_HPLL_SR_EN : 0) |
> > +              FW_WM(wm->sr.cursor, CURSOR_SR) |
> > +              FW_WM(wm->hpll.cursor, HPLL_CURSOR) |
> > +              FW_WM(wm->hpll.plane, HPLL_SR));
> > +
> > +   POSTING_READ(DSPFW1);
> >  }
> >  
> >  #define FW_WM_VLV(value, plane) \
> > @@ -1126,6 +1015,523 @@ static void vlv_write_wm_values(struct 
> > drm_i915_private *dev_priv,
> >  
> >  #undef FW_WM_VLV
> >  
> > +static void g4x_setup_wm_latency(struct drm_i915_private *dev_priv)
> > +{
> > +   /* all latencies in usec */
> > +   dev_priv->wm.pri_latency[G4X_WM_LEVEL_NORMAL] = 5;
> > +   dev_priv->wm.pri_latency[G4X_WM_LEVEL_SR] = 12;
> > +
> > +   dev_priv->wm.max_level = G4X_WM_LEVEL_SR;
> > +}
> > +
> > +static int g4x_plane_fifo_size(enum plane_id plane_id, int level)
> > +{
> > +   /*
> > +    * DSPCNTR[13] supposedly controls whether the
> > +    * primary plane can use the FIFO space otherwise
> > +    * reserved for the sprite plane. It's not 100% clear
> > +    * what the actual FIFO size is, but it looks like we
> > +    * can happily set both primary and sprite watermarks
> > +    * up to 127 cachelines. So that would seem to mean
> > +    * that either DSPCNTR[13] doesn't do anything, or that
> > +    * the total FIFO is >= 256 cachelines in size. Either
> > +    * way, we don't seem to have to worry about this
> > +    * repartitioning as the maximum watermark value the
> > +    * register can hold for each plane is lower than the
> > +    * minimum FIFO size.
> > +    */
> > +   switch (plane_id) {
> > +   case PLANE_CURSOR:
> > +           return 63;
> > +   case PLANE_PRIMARY:
> > +           return level == G4X_WM_LEVEL_NORMAL ? 127 : 511;
> > +   case PLANE_SPRITE0:
> > +           return level == G4X_WM_LEVEL_NORMAL ? 127 : 0;
> > +   default:
> > +           MISSING_CASE(plane_id);
> > +           return 0;
> > +   }
> > +}
> > +
> > +static int g4x_fbc_fifo_size(int level)
> > +{
> > +   switch (level) {
> > +   case G4X_WM_LEVEL_SR:
> > +           return 7;
> > +   case G4X_WM_LEVEL_HPLL:
> > +           return 15;
> > +   default:
> > +           MISSING_CASE(level);
> > +           return 0;
> > +   }
> > +}
> > +
> > +static uint16_t g4x_compute_wm(const struct intel_crtc_state *crtc_state,
> > +                          const struct intel_plane_state *plane_state,
> > +                          int level)
> > +{
> > +   struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> > +   struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
> > +   const struct drm_display_mode *adjusted_mode =
> > +           &crtc_state->base.adjusted_mode;
> > +   int clock, htotal, cpp, width, wm;
> > +   int latency = dev_priv->wm.pri_latency[level] * 10;
> > +
> > +   if (latency == 0)
> > +           return USHRT_MAX;
> > +
> > +   if (!intel_wm_plane_visible(crtc_state, plane_state))
> > +           return 0;
> > +
> > +   /*
> > +    * Not 100% sure which way ELK should go here as the
> > +    * spec only says CL/CTG should assume 32bpp and BW
> > +    * doesn't need to. But as these things followed the
> > +    * mobile vs. desktop lines on gen3 as well, let's
> > +    * assume ELK doesn't need this.
> > +    *
> > +    * The spec also fails to list such a restriction for
> > +    * the HPLL watermark, which seems a little strange.
> > +    * Let's use 32bpp for the HPLL watermark as well.
> > +    */
> > +   if (IS_GM45(dev_priv) && plane->id == PLANE_PRIMARY &&
> > +       level != G4X_WM_LEVEL_NORMAL)
> > +           cpp = 4;
> > +   else
> > +           cpp = plane_state->base.fb->format->cpp[0];
> > +
> > +   clock = adjusted_mode->crtc_clock;
> > +   htotal = adjusted_mode->crtc_htotal;
> > +
> > +   if (plane->id == PLANE_CURSOR)
> > +           width = plane_state->base.crtc_w;
> > +   else
> > +           width = drm_rect_width(&plane_state->base.dst);
> > +
> > +   if (plane->id == PLANE_CURSOR) {
> > +           wm = intel_wm_method2(clock, htotal, width, cpp, latency);
> > +   } else if (plane->id == PLANE_PRIMARY &&
> > +              level == G4X_WM_LEVEL_NORMAL) {
> > +           wm = intel_wm_method1(clock, cpp, latency);
> > +   } else {
> > +           int small, large;
> > +
> > +           small = intel_wm_method1(clock, cpp, latency);
> > +           large = intel_wm_method2(clock, htotal, width, cpp, latency);
> > +
> > +           wm = min(small, large);
> > +   }
> > +
> > +   wm += g4x_tlb_miss_wa(g4x_plane_fifo_size(plane->id, level),
> > +                         width, cpp);
> > +
> > +   wm = DIV_ROUND_UP(wm, 64) + 2;
> > +
> > +   return min_t(int, wm, USHRT_MAX);
> > +}
> > +
> > +static bool g4x_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
> > +                            int level, enum plane_id plane_id, u16 value)
> > +{
> > +   struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +   bool dirty = false;
> > +
> > +   for (; level < intel_wm_num_levels(dev_priv); level++) {
> > +           struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +           dirty |= raw->plane[plane_id] != value;
> > +           raw->plane[plane_id] = value;
> > +   }
> > +
> > +   return dirty;
> > +}
> > +
> > +static bool g4x_raw_fbc_wm_set(struct intel_crtc_state *crtc_state,
> > +                          int level, u16 value)
> > +{
> > +   struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +   bool dirty = false;
> > +
> > +   /* NORMAL level doesn't have an FBC watermark */
> > +   level = max(level, G4X_WM_LEVEL_SR);
> > +
> > +   for (; level < intel_wm_num_levels(dev_priv); level++) {
> > +           struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +           dirty |= raw->fbc != value;
> > +           raw->fbc = value;
> > +   }
> > +
> > +   return dirty;
> > +}
> > +
> > +static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate,
> > +                              const struct intel_plane_state *pstate,
> > +                              uint32_t pri_val);
> > +
> > +static bool g4x_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
> > +                                const struct intel_plane_state 
> > *plane_state)
> > +{
> > +   struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> > +   int num_levels = intel_wm_num_levels(to_i915(plane->base.dev));
> > +   enum plane_id plane_id = plane->id;
> > +   bool dirty = false;
> > +   int level;
> > +
> > +   if (!intel_wm_plane_visible(crtc_state, plane_state)) {
> > +           dirty |= g4x_raw_plane_wm_set(crtc_state, 0, plane_id, 0);
> > +           if (plane_id == PLANE_PRIMARY)
> > +                   dirty |= g4x_raw_fbc_wm_set(crtc_state, 0, 0);
> > +           goto out;
> > +   }
> > +
> > +   for (level = 0; level < num_levels; level++) {
> > +           struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +           int wm, max_wm;
> > +
> > +           wm = g4x_compute_wm(crtc_state, plane_state, level);
> > +           max_wm = g4x_plane_fifo_size(plane_id, level);
> > +
> > +           if (wm > max_wm)
> > +                   break;
> > +
> > +           dirty |= raw->plane[plane_id] != wm;
> > +           raw->plane[plane_id] = wm;
> > +
> > +           if (plane_id != PLANE_PRIMARY ||
> > +               level == G4X_WM_LEVEL_NORMAL)
> > +                   continue;
> > +
> > +           wm = ilk_compute_fbc_wm(crtc_state, plane_state,
> > +                                   raw->plane[plane_id]);
> > +           max_wm = g4x_fbc_fifo_size(level);
> > +
> > +           /*
> > +            * FBC wm is not mandatory as we
> > +            * can always just disable its use.
> > +            */
> > +           if (wm > max_wm)
> > +                   wm = USHRT_MAX;
> > +
> > +           dirty |= raw->fbc != wm;
> > +           raw->fbc = wm;
> > +   }
> > +
> > +   /* mark watermarks as invalid */
> > +   dirty |= g4x_raw_plane_wm_set(crtc_state, level, plane_id, USHRT_MAX);
> > +
> > +   if (plane_id == PLANE_PRIMARY)
> > +           dirty |= g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> > +
> > + out:
> > +   if (dirty) {
> > +           DRM_DEBUG_KMS("%s watermarks: normal=%d, SR=%d, HPLL=%d\n",
> > +                         plane->base.name,
> > +                         
> > crtc_state->wm.g4x.raw[G4X_WM_LEVEL_NORMAL].plane[plane_id],
> > +                         
> > crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].plane[plane_id],
> > +                         
> > crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].plane[plane_id]);
> > +
> > +           if (plane_id == PLANE_PRIMARY)
> > +                   DRM_DEBUG_KMS("FBC watermarks: SR=%d, HPLL=%d\n",
> > +                                 
> > crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].fbc,
> > +                                 
> > crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].fbc);
> > +   }
> > +
> > +   return dirty;
> > +}
> > +
> > +static bool g4x_raw_plane_wm_is_valid(const struct intel_crtc_state 
> > *crtc_state,
> > +                                 enum plane_id plane_id, int level)
> > +{
> > +   const struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +   return raw->plane[plane_id] <= g4x_plane_fifo_size(plane_id, level);
> > +}
> > +
> > +static bool g4x_raw_crtc_wm_is_valid(const struct intel_crtc_state 
> > *crtc_state,
> > +                                int level)
> > +{
> > +   struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +
> > +   if (level > dev_priv->wm.max_level)
> > +           return false;
> > +
> > +   return g4x_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) &&
> > +           g4x_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) &&
> > +           g4x_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level);
> > +}
> > +
> > +/* mark all levels starting from 'level' as invalid */
> > +static void g4x_invalidate_wms(struct intel_crtc *crtc,
> > +                          struct g4x_wm_state *wm_state, int level)
> > +{
> > +   if (level <= G4X_WM_LEVEL_NORMAL) {
> > +           enum plane_id plane_id;
> > +
> > +           for_each_plane_id_on_crtc(crtc, plane_id)
> > +                   wm_state->wm.plane[plane_id] = USHRT_MAX;
> > +   }
> > +
> > +   if (level <= G4X_WM_LEVEL_SR) {
> > +           wm_state->cxsr = false;
> > +           wm_state->sr.cursor = USHRT_MAX;
> > +           wm_state->sr.plane = USHRT_MAX;
> > +           wm_state->sr.fbc = USHRT_MAX;
> > +   }
> > +
> > +   if (level <= G4X_WM_LEVEL_HPLL) {
> > +           wm_state->hpll_en = false;
> > +           wm_state->hpll.cursor = USHRT_MAX;
> > +           wm_state->hpll.plane = USHRT_MAX;
> > +           wm_state->hpll.fbc = USHRT_MAX;
> > +   }
> > +}
> > +
> > +static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state)
> > +{
> > +   struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> > +   struct intel_atomic_state *state =
> > +           to_intel_atomic_state(crtc_state->base.state);
> > +   struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> > +   int num_active_planes = hweight32(crtc_state->active_planes &
> > +                                     ~BIT(PLANE_CURSOR));
> > +   const struct g4x_pipe_wm *raw;
> > +   struct intel_plane_state *plane_state;
> > +   struct intel_plane *plane;
> > +   enum plane_id plane_id;
> > +   int i, level;
> > +   unsigned int dirty = 0;
> > +
> > +   for_each_intel_plane_in_state(state, plane, plane_state, i) {
> > +           const struct intel_plane_state *old_plane_state =
> > +                   to_intel_plane_state(plane->base.state);
> > +
> > +           if (plane_state->base.crtc != &crtc->base &&
> > +               old_plane_state->base.crtc != &crtc->base)
> > +                   continue;
> > +
> > +           if (g4x_raw_plane_wm_compute(crtc_state, plane_state))
> > +                   dirty |= BIT(plane->id);
> > +   }
> > +
> > +   if (!dirty)
> > +           return 0;
> > +
> > +   level = G4X_WM_LEVEL_NORMAL;
> > +   if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +           goto out;
> > +
> > +   raw = &crtc_state->wm.g4x.raw[level];
> > +   for_each_plane_id_on_crtc(crtc, plane_id)
> > +           wm_state->wm.plane[plane_id] = raw->plane[plane_id];
> > +
> > +   level = G4X_WM_LEVEL_SR;
> > +
> > +   if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +           goto out;
> > +
> > +   raw = &crtc_state->wm.g4x.raw[level];
> > +   wm_state->sr.plane = raw->plane[PLANE_PRIMARY];
> > +   wm_state->sr.cursor = raw->plane[PLANE_CURSOR];
> > +   wm_state->sr.fbc = raw->fbc;
> > +
> > +   wm_state->cxsr = num_active_planes == BIT(PLANE_PRIMARY);
> > +
> > +   level = G4X_WM_LEVEL_HPLL;
> > +
> > +   if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +           goto out;
> > +
> > +   raw = &crtc_state->wm.g4x.raw[level];
> > +   wm_state->hpll.plane = raw->plane[PLANE_PRIMARY];
> > +   wm_state->hpll.cursor = raw->plane[PLANE_CURSOR];
> > +   wm_state->hpll.fbc = raw->fbc;
> > +
> > +   wm_state->hpll_en = wm_state->cxsr;
> > +
> > +   level++;
> > +
> > + out:
> > +   if (level == G4X_WM_LEVEL_NORMAL)
> > +           return -EINVAL;
> > +
> > +   /* invalidate the higher levels */
> > +   g4x_invalidate_wms(crtc, wm_state, level);
> > +
> > +   /*
> > +    * Determine if the FBC watermark(s) can be used. IF
> > +    * this isn't the case we prefer to disable the FBC
> > +    ( watermark(s) rather than disable the SR/HPLL
> > +    * level(s) entirely.
> > +    */
> > +   wm_state->fbc_en = level > G4X_WM_LEVEL_NORMAL;
> > +
> > +   if (level >= G4X_WM_LEVEL_SR &&
> > +       wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR))
> > +           wm_state->fbc_en = false;
> > +   else if (level >= G4X_WM_LEVEL_HPLL &&
> > +            wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL))
> > +           wm_state->fbc_en = false;
> > +
> > +   return 0;
> > +}
> > +
> > +static int g4x_compute_intermediate_wm(struct drm_device *dev,
> > +                                  struct intel_crtc *crtc,
> > +                                  struct intel_crtc_state *crtc_state)
> > +{
> > +   struct g4x_wm_state *intermediate = &crtc_state->wm.g4x.intermediate;
> > +   const struct g4x_wm_state *optimal = &crtc_state->wm.g4x.optimal;
> > +   const struct g4x_wm_state *active = &crtc->wm.active.g4x;
> > +   enum plane_id plane_id;
> 
> I would add a provision for modeset, no need to calculate intermediate 
> watermarks with the pipe enabled, something like this?
> 
>       if (!newstate->base.active || 
> drm_atomic_crtc_needs_modeset(&newstate->base)) {
>               *intermediate = *optimal;
>               return 0;
>       }

Hmm. Yeah, I suppose we should be doing somethig like that. Though I'd
still much more prefer if we had the crtc disable as a totally separate
atomic commit.

> 
> The active watermarks are wrongly assigned here, if there's a pending update 
> it could point to old old optimal state, or old intermediate state, it should 
> be something like this:
> old_crtc_state = drm_atomic_get_old_crtc_state(crtc_state->base.state, 
> &crtc->base);
> active = &to_intel_crtc_state(old_crtc_state)->wm.g4x.optimal

Well, it should really be considering both active+new_optimal and
old_optimal+new_optimal. That's assuming we eventually want to allow
the fps>vrefresh thing. That's what I had in my original ILK code BTW,
just no one bothered to bring it over when the current code was merged.

But yeah, with the way things are currently I suppose we should be
only considering the old_optimal+new_optimal case.

> 
> I know, vlv does this wrong too, should be fixed as well..
> With that fixed all patches look good to me, so feel free to put this on them:
> 
> Reviewed-by: Maarten Lankhorst <maarten.lankho...@linux.intel.com>
> 
> > +   intermediate->cxsr = optimal->cxsr && active->cxsr &&
> > +           !crtc_state->disable_cxsr;
> > +   intermediate->hpll_en = optimal->hpll_en && active->hpll_en &&
> > +           !crtc_state->disable_cxsr;
> > +   intermediate->fbc_en = optimal->fbc_en && active->fbc_en;
> > +
> > +   for_each_plane_id_on_crtc(crtc, plane_id) {
> > +           intermediate->wm.plane[plane_id] =
> > +                   max(optimal->wm.plane[plane_id],
> > +                       active->wm.plane[plane_id]);
> > +
> > +           WARN_ON(intermediate->wm.plane[plane_id] >
> > +                   g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL));
> > +   }
> > +
> > +   intermediate->sr.plane = max(optimal->sr.plane,
> > +                                active->sr.plane);
> > +   intermediate->sr.cursor = max(optimal->sr.cursor,
> > +                                 active->sr.cursor);
> > +   intermediate->sr.fbc = max(optimal->sr.fbc,
> > +                              active->sr.fbc);
> > +
> > +   intermediate->hpll.plane = max(optimal->hpll.plane,
> > +                                  active->hpll.plane);
> > +   intermediate->hpll.cursor = max(optimal->hpll.cursor,
> > +                                   active->hpll.cursor);
> > +   intermediate->hpll.fbc = max(optimal->hpll.fbc,
> > +                                active->hpll.fbc);
> > +
> > +   WARN_ON((intermediate->sr.plane >
> > +            g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) ||
> > +            intermediate->sr.cursor >
> > +            g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) &&
> > +           intermediate->cxsr);
> > +   WARN_ON((intermediate->sr.plane >
> > +            g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) ||
> > +            intermediate->sr.cursor >
> > +            g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) &&
> > +           intermediate->hpll_en);
> > +
> > +   WARN_ON(intermediate->sr.fbc > g4x_fbc_fifo_size(1) &&
> > +           intermediate->fbc_en && intermediate->cxsr);
> > +   WARN_ON(intermediate->hpll.fbc > g4x_fbc_fifo_size(2) &&
> > +           intermediate->fbc_en && intermediate->hpll_en);
> > +
> > +   /*
> > +    * If our intermediate WM are identical to the final WM, then we can
> > +    * omit the post-vblank programming; only update if it's different.
> > +    */
> > +   if (memcmp(intermediate, optimal, sizeof(*intermediate)) != 0)
> > +           crtc_state->wm.need_postvbl_update = true;
> > +
> > +   return 0;
> > +}
> > +
> > +static void g4x_merge_wm(struct drm_i915_private *dev_priv,
> > +                    struct g4x_wm_values *wm)
> > +{
> > +   struct intel_crtc *crtc;
> > +   int num_active_crtcs = 0;
> > +
> > +   wm->cxsr = true;
> > +   wm->hpll_en = true;
> > +   wm->fbc_en = true;
> > +
> > +   for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +           const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> > +
> > +           if (!crtc->active)
> > +                   continue;
> > +
> > +           if (!wm_state->cxsr)
> > +                   wm->cxsr = false;
> > +           if (!wm_state->hpll_en)
> > +                   wm->hpll_en = false;
> > +           if (!wm_state->fbc_en)
> > +                   wm->fbc_en = false;
> > +
> > +           num_active_crtcs++;
> > +   }
> > +
> > +   if (num_active_crtcs != 1) {
> > +           wm->cxsr = false;
> > +           wm->hpll_en = false;
> > +           wm->fbc_en = false;
> > +   }
> > +
> > +   for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +           const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> > +           enum pipe pipe = crtc->pipe;
> > +
> > +           wm->pipe[pipe] = wm_state->wm;
> > +           if (crtc->active && wm->cxsr)
> > +                   wm->sr = wm_state->sr;
> > +           if (crtc->active && wm->hpll_en)
> > +                   wm->hpll = wm_state->hpll;
> > +   }
> > +}
> > +
> > +static void g4x_program_watermarks(struct drm_i915_private *dev_priv)
> > +{
> > +   struct g4x_wm_values *old_wm = &dev_priv->wm.g4x;
> > +   struct g4x_wm_values new_wm = {};
> > +
> > +   g4x_merge_wm(dev_priv, &new_wm);
> > +
> > +   if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0)
> > +           return;
> > +
> > +   if (is_disabling(old_wm->cxsr, new_wm.cxsr, true))
> > +           _intel_set_memory_cxsr(dev_priv, false);
> > +
> > +   g4x_write_wm_values(dev_priv, &new_wm);
> > +
> > +   if (is_enabling(old_wm->cxsr, new_wm.cxsr, true))
> > +           _intel_set_memory_cxsr(dev_priv, true);
> > +
> > +   *old_wm = new_wm;
> > +}
> > +
> > +static void g4x_initial_watermarks(struct intel_atomic_state *state,
> > +                              struct intel_crtc_state *crtc_state)
> > +{
> > +   struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +   struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> > +
> > +   mutex_lock(&dev_priv->wm.wm_mutex);
> > +   crtc->wm.active.g4x = crtc_state->wm.g4x.intermediate;
> > +   g4x_program_watermarks(dev_priv);
> > +   mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> > +static void g4x_optimize_watermarks(struct intel_atomic_state *state,
> > +                               struct intel_crtc_state *crtc_state)
> > +{
> > +   struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +   struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
> > +
> > +   if (!crtc_state->wm.need_postvbl_update)
> > +           return;
> > +
> > +   mutex_lock(&dev_priv->wm.wm_mutex);
> > +   intel_crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> > +   g4x_program_watermarks(dev_priv);
> > +   mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> >  /* latency must be in 0.1us units. */
> >  static unsigned int vlv_wm_method2(unsigned int pixel_rate,
> >                                unsigned int htotal,
> > @@ -1673,16 +2079,6 @@ static void vlv_merge_wm(struct drm_i915_private 
> > *dev_priv,
> >     }
> >  }
> >  
> > -static bool is_disabling(int old, int new, int threshold)
> > -{
> > -   return old >= threshold && new < threshold;
> > -}
> > -
> > -static bool is_enabling(int old, int new, int threshold)
> > -{
> > -   return old < threshold && new >= threshold;
> > -}
> > -
> >  static void vlv_program_watermarks(struct drm_i915_private *dev_priv)
> >  {
> >     struct vlv_wm_values *old_wm = &dev_priv->wm.vlv;
> > @@ -1743,65 +2139,6 @@ static void vlv_optimize_watermarks(struct 
> > intel_atomic_state *state,
> >     mutex_unlock(&dev_priv->wm.wm_mutex);
> >  }
> >  
> > -#define single_plane_enabled(mask) is_power_of_2(mask)
> > -
> > -static void g4x_update_wm(struct intel_crtc *crtc)
> > -{
> > -   struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
> > -   static const int sr_latency_ns = 12000;
> > -   int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
> > -   int plane_sr, cursor_sr;
> > -   unsigned int enabled = 0;
> > -   bool cxsr_enabled;
> > -
> > -   if (g4x_compute_wm0(dev_priv, PIPE_A,
> > -                       &g4x_wm_info, pessimal_latency_ns,
> > -                       &g4x_cursor_wm_info, pessimal_latency_ns,
> > -                       &planea_wm, &cursora_wm))
> > -           enabled |= 1 << PIPE_A;
> > -
> > -   if (g4x_compute_wm0(dev_priv, PIPE_B,
> > -                       &g4x_wm_info, pessimal_latency_ns,
> > -                       &g4x_cursor_wm_info, pessimal_latency_ns,
> > -                       &planeb_wm, &cursorb_wm))
> > -           enabled |= 1 << PIPE_B;
> > -
> > -   if (single_plane_enabled(enabled) &&
> > -       g4x_compute_srwm(dev_priv, ffs(enabled) - 1,
> > -                        sr_latency_ns,
> > -                        &g4x_wm_info,
> > -                        &g4x_cursor_wm_info,
> > -                        &plane_sr, &cursor_sr)) {
> > -           cxsr_enabled = true;
> > -   } else {
> > -           cxsr_enabled = false;
> > -           intel_set_memory_cxsr(dev_priv, false);
> > -           plane_sr = cursor_sr = 0;
> > -   }
> > -
> > -   DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
> > -                 "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
> > -                 planea_wm, cursora_wm,
> > -                 planeb_wm, cursorb_wm,
> > -                 plane_sr, cursor_sr);
> > -
> > -   I915_WRITE(DSPFW1,
> > -              FW_WM(plane_sr, SR) |
> > -              FW_WM(cursorb_wm, CURSORB) |
> > -              FW_WM(planeb_wm, PLANEB) |
> > -              FW_WM(planea_wm, PLANEA));
> > -   I915_WRITE(DSPFW2,
> > -              (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
> > -              FW_WM(cursora_wm, CURSORA));
> > -   /* HPLL off in SR has some issues on G4x... disable it */
> > -   I915_WRITE(DSPFW3,
> > -              (I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | 
> > DSPFW_CURSOR_SR_MASK)) |
> > -              FW_WM(cursor_sr, CURSOR_SR));
> > -
> > -   if (cxsr_enabled)
> > -           intel_set_memory_cxsr(dev_priv, true);
> > -}
> > -
> >  static void i965_update_wm(struct intel_crtc *unused_crtc)
> >  {
> >     struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev);
> > @@ -4778,6 +5115,32 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc 
> > *crtc)
> >  #define _FW_WM_VLV(value, plane) \
> >     (((value) & DSPFW_ ## plane ## _MASK_VLV) >> DSPFW_ ## plane ## _SHIFT)
> >  
> > +static void g4x_read_wm_values(struct drm_i915_private *dev_priv,
> > +                          struct g4x_wm_values *wm)
> > +{
> > +   uint32_t tmp;
> > +
> > +   tmp = I915_READ(DSPFW1);
> > +   wm->sr.plane = _FW_WM(tmp, SR);
> > +   wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB);
> > +   wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEB);
> > +   wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEA);
> > +
> > +   tmp = I915_READ(DSPFW2);
> > +   wm->fbc_en = tmp & DSPFW_FBC_SR_EN;
> > +   wm->sr.fbc = _FW_WM(tmp, FBC_SR);
> > +   wm->hpll.fbc = _FW_WM(tmp, FBC_HPLL_SR);
> > +   wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEB);
> > +   wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA);
> > +   wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEA);
> > +
> > +   tmp = I915_READ(DSPFW3);
> > +   wm->hpll_en = tmp & DSPFW_HPLL_SR_EN;
> > +   wm->sr.cursor = _FW_WM(tmp, CURSOR_SR);
> > +   wm->hpll.cursor = _FW_WM(tmp, HPLL_CURSOR);
> > +   wm->hpll.plane = _FW_WM(tmp, HPLL_SR);
> > +}
> > +
> >  static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
> >                            struct vlv_wm_values *wm)
> >  {
> > @@ -4854,6 +5217,147 @@ static void vlv_read_wm_values(struct 
> > drm_i915_private *dev_priv,
> >  #undef _FW_WM
> >  #undef _FW_WM_VLV
> >  
> > +void g4x_wm_get_hw_state(struct drm_device *dev)
> > +{
> > +   struct drm_i915_private *dev_priv = to_i915(dev);
> > +   struct g4x_wm_values *wm = &dev_priv->wm.g4x;
> > +   struct intel_crtc *crtc;
> > +
> > +   g4x_read_wm_values(dev_priv, wm);
> > +
> > +   wm->cxsr = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
> > +
> > +   for_each_intel_crtc(dev, crtc) {
> > +           struct intel_crtc_state *crtc_state =
> > +                   to_intel_crtc_state(crtc->base.state);
> > +           struct g4x_wm_state *active = &crtc->wm.active.g4x;
> > +           struct g4x_pipe_wm *raw;
> > +           enum pipe pipe = crtc->pipe;
> > +           enum plane_id plane_id;
> > +           int level, max_level;
> > +
> > +           active->cxsr = wm->cxsr;
> > +           active->hpll_en = wm->hpll_en;
> > +           active->fbc_en = wm->fbc_en;
> > +
> > +           active->sr = wm->sr;
> > +           active->hpll = wm->hpll;
> > +
> > +           for_each_plane_id_on_crtc(crtc, plane_id) {
> > +                   active->wm.plane[plane_id] =
> > +                           wm->pipe[pipe].plane[plane_id];
> > +           }
> > +
> > +           if (wm->cxsr && wm->hpll_en)
> > +                   max_level = G4X_WM_LEVEL_HPLL;
> > +           else if (wm->cxsr)
> > +                   max_level = G4X_WM_LEVEL_SR;
> > +           else
> > +                   max_level = G4X_WM_LEVEL_NORMAL;
> > +
> > +           level = G4X_WM_LEVEL_NORMAL;
> > +           raw = &crtc_state->wm.g4x.raw[level];
> > +           for_each_plane_id_on_crtc(crtc, plane_id)
> > +                   raw->plane[plane_id] = active->wm.plane[plane_id];
> > +
> > +           if (++level > max_level)
> > +                   goto out;
> > +
> > +           raw = &crtc_state->wm.g4x.raw[level];
> > +           raw->plane[PLANE_PRIMARY] = active->sr.plane;
> > +           raw->plane[PLANE_CURSOR] = active->sr.cursor;
> > +           raw->plane[PLANE_SPRITE0] = 0;
> > +           raw->fbc = active->sr.fbc;
> > +
> > +           if (++level > max_level)
> > +                   goto out;
> > +
> > +           raw = &crtc_state->wm.g4x.raw[level];
> > +           raw->plane[PLANE_PRIMARY] = active->hpll.plane;
> > +           raw->plane[PLANE_CURSOR] = active->hpll.cursor;
> > +           raw->plane[PLANE_SPRITE0] = 0;
> > +           raw->fbc = active->hpll.fbc;
> > +
> > +   out:
> > +           for_each_plane_id_on_crtc(crtc, plane_id)
> > +                   g4x_raw_plane_wm_set(crtc_state, level,
> > +                                        plane_id, USHRT_MAX);
> > +           g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> > +
> > +           crtc_state->wm.g4x.optimal = *active;
> > +           crtc_state->wm.g4x.intermediate = *active;
> > +
> > +           DRM_DEBUG_KMS("Initial watermarks: pipe %c, plane=%d, 
> > cursor=%d, sprite=%d\n",
> > +                         pipe_name(pipe),
> > +                         wm->pipe[pipe].plane[PLANE_PRIMARY],
> > +                         wm->pipe[pipe].plane[PLANE_CURSOR],
> > +                         wm->pipe[pipe].plane[PLANE_SPRITE0]);
> > +   }
> > +
> > +   DRM_DEBUG_KMS("Initial SR watermarks: plane=%d, cursor=%d fbc=%d\n",
> > +                 wm->sr.plane, wm->sr.cursor, wm->sr.fbc);
> > +   DRM_DEBUG_KMS("Initial HPLL watermarks: plane=%d, SR cursor=%d 
> > fbc=%d\n",
> > +                 wm->hpll.plane, wm->hpll.cursor, wm->hpll.fbc);
> > +   DRM_DEBUG_KMS("Initial SR=%s HPLL=%s FBC=%s\n",
> > +                 yesno(wm->cxsr), yesno(wm->hpll_en), yesno(wm->fbc_en));
> > +}
> > +
> > +void g4x_wm_sanitize(struct drm_i915_private *dev_priv)
> > +{
> > +   struct intel_plane *plane;
> > +   struct intel_crtc *crtc;
> > +
> > +   mutex_lock(&dev_priv->wm.wm_mutex);
> > +
> > +   for_each_intel_plane(&dev_priv->drm, plane) {
> > +           struct intel_crtc *crtc =
> > +                   intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> > +           struct intel_crtc_state *crtc_state =
> > +                   to_intel_crtc_state(crtc->base.state);
> > +           struct intel_plane_state *plane_state =
> > +                   to_intel_plane_state(plane->base.state);
> > +           struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> > +           enum plane_id plane_id = plane->id;
> > +           int level;
> > +
> > +           if (plane_state->base.visible)
> > +                   continue;
> > +
> > +           for (level = 0; level < 3; level++) {
> > +                   struct g4x_pipe_wm *raw =
> > +                           &crtc_state->wm.g4x.raw[level];
> > +
> > +                   raw->plane[plane_id] = 0;
> > +                   wm_state->wm.plane[plane_id] = 0;
> > +           }
> > +
> > +           if (plane_id == PLANE_PRIMARY) {
> > +                   for (level = 0; level < 3; level++) {
> > +                           struct g4x_pipe_wm *raw =
> > +                                   &crtc_state->wm.g4x.raw[level];
> > +                           raw->fbc = 0;
> > +                   }
> > +
> > +                   wm_state->sr.fbc = 0;
> > +                   wm_state->hpll.fbc = 0;
> > +                   wm_state->fbc_en = false;
> > +           }
> > +   }
> > +
> > +   for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +           struct intel_crtc_state *crtc_state =
> > +                   to_intel_crtc_state(crtc->base.state);
> > +
> > +           crtc_state->wm.g4x.intermediate =
> > +                   crtc_state->wm.g4x.optimal;
> > +           crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> > +   }
> > +
> > +   g4x_program_watermarks(dev_priv);
> > +
> > +   mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> >  void vlv_wm_get_hw_state(struct drm_device *dev)
> >  {
> >     struct drm_i915_private *dev_priv = to_i915(dev);
> > @@ -8160,6 +8664,12 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
> >             dev_priv->display.initial_watermarks = vlv_initial_watermarks;
> >             dev_priv->display.optimize_watermarks = vlv_optimize_watermarks;
> >             dev_priv->display.atomic_update_watermarks = 
> > vlv_atomic_update_fifo;
> > +   } else if (IS_G4X(dev_priv)) {
> > +           g4x_setup_wm_latency(dev_priv);
> > +           dev_priv->display.compute_pipe_wm = g4x_compute_pipe_wm;
> > +           dev_priv->display.compute_intermediate_wm = 
> > g4x_compute_intermediate_wm;
> > +           dev_priv->display.initial_watermarks = g4x_initial_watermarks;
> > +           dev_priv->display.optimize_watermarks = g4x_optimize_watermarks;
> >     } else if (IS_PINEVIEW(dev_priv)) {
> >             if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev_priv),
> >                                         dev_priv->is_ddr3,
> > @@ -8175,8 +8685,6 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
> >                     dev_priv->display.update_wm = NULL;
> >             } else
> >                     dev_priv->display.update_wm = pineview_update_wm;
> > -   } else if (IS_G4X(dev_priv)) {
> > -           dev_priv->display.update_wm = g4x_update_wm;
> >     } else if (IS_GEN4(dev_priv)) {
> >             dev_priv->display.update_wm = i965_update_wm;
> >     } else if (IS_GEN3(dev_priv)) {
> 

-- 
Ville Syrjälä
Intel OTC
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to