Skip to content

Commit

Permalink
drm/i915: Add two-stage ILK-style watermark programming (v11)
Browse files Browse the repository at this point in the history
In addition to calculating final watermarks, let's also pre-calculate a
set of intermediate watermark values at atomic check time.  These
intermediate watermarks are a combination of the watermarks for the old
state and the new state; they should satisfy the requirements of both
states which means they can be programmed immediately when we commit the
atomic state (without waiting for a vblank).  Once the vblank does
happen, we can then re-program watermarks to the more optimal final
value.

v2: Significant rebasing/rewriting.

v3:
 - Move 'need_postvbl_update' flag to CRTC state (Daniel)
 - Don't forget to check intermediate watermark values for validity
   (Maarten)
 - Don't due async watermark optimization; just do it at the end of the
   atomic transaction, after waiting for vblanks.  We do want it to be
   async eventually, but adding that now will cause more trouble for
   Maarten's in-progress work.  (Maarten)
 - Don't allocate space in crtc_state for intermediate watermarks on
   platforms that don't need it (gen9+).
 - Move WaCxSRDisabledForSpriteScaling:ivb into intel_begin_crtc_commit
   now that ilk_update_wm is gone.

v4:
 - Add a wm_mutex to cover updates to intel_crtc->active and the
   need_postvbl_update flag.  Since we don't have async yet it isn't
   terribly important yet, but might as well add it now.
 - Change interface to program watermarks.  Platforms will now expose
   .initial_watermarks() and .optimize_watermarks() functions to do
   watermark programming.  These should lock wm_mutex, copy the
   appropriate state values into intel_crtc->active, and then call
   the internal program watermarks function.

v5:
 - Skip intermediate watermark calculation/check during initial hardware
   readout since we don't trust the existing HW values (and don't have
   valid values of our own yet).
 - Don't try to call .optimize_watermarks() on platforms that don't have
   atomic watermarks yet.  (Maarten)

v6:
 - Rebase

v7:
 - Further rebase

v8:
 - A few minor indentation and line length fixes

v9:
 - Yet another rebase since Maarten's patches reworked a bunch of the
   code (wm_pre, wm_post, etc.) that this was previously based on.

v10:
 - Move wm_mutex to dev_priv to protect against racing commits against
   disjoint CRTC sets. (Maarten)
 - Drop unnecessary clearing of cstate->wm.need_postvbl_update (Maarten)

v11:
 - Now that we've moved to atomic watermark updates, make sure we call
   the proper function to program watermarks in
   {ironlake,haswell}_crtc_enable(); the failure to do so on the
   previous patch iteration led to us not actually programming the
   watermarks before turning on the CRTC, which was the cause of the
   underruns that the CI system was seeing.
 - Fix inverted logic for determining when to optimize watermarks.  We
   were needlessly optimizing when the intermediate/optimal values were
   the same (harmless), but not actually optimizing when they differed
   (also harmless, but wasteful from a power/bandwidth perspective).

Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
Reviewed-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1456276813-5689-1-git-send-email-matthew.d.roper@intel.com
  • Loading branch information
Matt Roper committed Feb 29, 2016
1 parent 5790ff7 commit ed4a6a7
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 58 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/i915_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
mutex_init(&dev_priv->sb_lock);
mutex_init(&dev_priv->modeset_restore_lock);
mutex_init(&dev_priv->av_mutex);
mutex_init(&dev_priv->wm.wm_mutex);

ret = i915_workqueues_init(dev_priv);
if (ret < 0)
Expand Down
13 changes: 12 additions & 1 deletion drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,11 @@ struct drm_i915_display_funcs {
struct dpll *best_clock);
int (*compute_pipe_wm)(struct intel_crtc *crtc,
struct drm_atomic_state *state);
void (*program_watermarks)(struct intel_crtc_state *cstate);
int (*compute_intermediate_wm)(struct drm_device *dev,
struct intel_crtc *intel_crtc,
struct intel_crtc_state *newstate);
void (*initial_watermarks)(struct intel_crtc_state *cstate);
void (*optimize_watermarks)(struct intel_crtc_state *cstate);
void (*update_wm)(struct drm_crtc *crtc);
int (*modeset_calc_cdclk)(struct drm_atomic_state *state);
void (*modeset_commit_cdclk)(struct drm_atomic_state *state);
Expand Down Expand Up @@ -1980,6 +1984,13 @@ struct drm_i915_private {
};

uint8_t max_level;

/*
* Should be held around atomic WM register writing; also
* protects * intel_crtc->wm.active and
* cstate->wm.need_postvbl_update.
*/
struct mutex wm_mutex;
} wm;

struct i915_runtime_pm pm;
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/intel_atomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc)
crtc_state->disable_cxsr = false;
crtc_state->wm_changed = false;
crtc_state->fb_changed = false;
crtc_state->wm.need_postvbl_update = false;

return &crtc_state->base;
}
Expand Down
97 changes: 91 additions & 6 deletions drivers/gpu/drm/i915/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -4843,7 +4843,42 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state)
intel_set_memory_cxsr(dev_priv, false);
}

if (!needs_modeset(&pipe_config->base) && pipe_config->wm_changed)
/*
* IVB workaround: must disable low power watermarks for at least
* one frame before enabling scaling. LP watermarks can be re-enabled
* when scaling is disabled.
*
* WaCxSRDisabledForSpriteScaling:ivb
*/
if (pipe_config->disable_lp_wm) {
ilk_disable_lp_wm(dev);
intel_wait_for_vblank(dev, crtc->pipe);
}

/*
* If we're doing a modeset, we're done. No need to do any pre-vblank
* watermark programming here.
*/
if (needs_modeset(&pipe_config->base))
return;

/*
* For platforms that support atomic watermarks, program the
* 'intermediate' watermarks immediately. On pre-gen9 platforms, these
* will be the intermediate values that are safe for both pre- and
* post- vblank; when vblank happens, the 'active' values will be set
* to the final 'target' values and we'll do this again to get the
* optimal watermarks. For gen9+ platforms, the values we program here
* will be the final target values which will get automatically latched
* at vblank time; no further programming will be necessary.
*
* If a platform hasn't been transitioned to atomic watermarks yet,
* we'll continue to update watermarks the old way, if flags tell
* us to.
*/
if (dev_priv->display.initial_watermarks != NULL)
dev_priv->display.initial_watermarks(pipe_config);
else if (pipe_config->wm_changed)
intel_update_watermarks(&crtc->base);
}

Expand Down Expand Up @@ -4922,7 +4957,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
*/
intel_crtc_load_lut(crtc);

intel_update_watermarks(crtc);
dev_priv->display.initial_watermarks(intel_crtc->config);
intel_enable_pipe(intel_crtc);

if (intel_crtc->config->has_pch_encoder)
Expand Down Expand Up @@ -5021,7 +5056,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
if (!intel_crtc->config->has_dsi_encoder)
intel_ddi_enable_transcoder_func(crtc);

intel_update_watermarks(crtc);
dev_priv->display.initial_watermarks(pipe_config);
intel_enable_pipe(intel_crtc);

if (intel_crtc->config->has_pch_encoder)
Expand Down Expand Up @@ -11785,6 +11820,7 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_plane *plane = plane_state->plane;
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane_state *old_plane_state =
to_intel_plane_state(plane->state);
int idx = intel_crtc->base.base.id, ret;
Expand Down Expand Up @@ -11843,6 +11879,11 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
pipe_config->wm_changed = true;
}

/* Pre-gen9 platforms need two-step watermark updates */
if (pipe_config->wm_changed && INTEL_INFO(dev)->gen < 9 &&
dev_priv->display.optimize_watermarks)
to_intel_crtc_state(crtc_state)->wm.need_postvbl_update = true;

if (visible || was_visible)
intel_crtc->atomic.fb_bits |=
to_intel_plane(plane)->frontbuffer_bit;
Expand Down Expand Up @@ -11954,8 +11995,29 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc,
ret = 0;
if (dev_priv->display.compute_pipe_wm) {
ret = dev_priv->display.compute_pipe_wm(intel_crtc, state);
if (ret)
if (ret) {
DRM_DEBUG_KMS("Target pipe watermarks are invalid\n");
return ret;
}
}

if (dev_priv->display.compute_intermediate_wm &&
!to_intel_atomic_state(state)->skip_intermediate_wm) {
if (WARN_ON(!dev_priv->display.compute_pipe_wm))
return 0;

/*
* Calculate 'intermediate' watermarks that satisfy both the
* old state and the new state. We can program these
* immediately.
*/
ret = dev_priv->display.compute_intermediate_wm(crtc->dev,
intel_crtc,
pipe_config);
if (ret) {
DRM_DEBUG_KMS("No valid intermediate pipe watermarks are possible\n");
return ret;
}
}

if (INTEL_INFO(dev)->gen >= 9) {
Expand Down Expand Up @@ -13488,6 +13550,7 @@ static int intel_atomic_commit(struct drm_device *dev,
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc_state *crtc_state;
struct drm_crtc *crtc;
struct intel_crtc_state *intel_cstate;
int ret = 0, i;
bool hw_check = intel_state->modeset;
unsigned long put_domains[I915_MAX_PIPES] = {};
Expand Down Expand Up @@ -13603,6 +13666,20 @@ static int intel_atomic_commit(struct drm_device *dev,
if (intel_state->modeset)
intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET);

/*
* Now that the vblank has passed, we can go ahead and program the
* optimal watermarks on platforms that need two-step watermark
* programming.
*
* TODO: Move this (and other cleanup) to an async worker eventually.
*/
for_each_crtc_in_state(state, crtc, crtc_state, i) {
intel_cstate = to_intel_crtc_state(crtc->state);

if (dev_priv->display.optimize_watermarks)
dev_priv->display.optimize_watermarks(intel_cstate);
}

mutex_lock(&dev->struct_mutex);
drm_atomic_helper_cleanup_planes(dev, state);
mutex_unlock(&dev->struct_mutex);
Expand Down Expand Up @@ -15273,7 +15350,7 @@ static void sanitize_watermarks(struct drm_device *dev)
int i;

/* Only supported on platforms that use atomic watermark design */
if (!dev_priv->display.program_watermarks)
if (!dev_priv->display.optimize_watermarks)
return;

/*
Expand All @@ -15294,6 +15371,13 @@ static void sanitize_watermarks(struct drm_device *dev)
if (WARN_ON(IS_ERR(state)))
goto fail;

/*
* Hardware readout is the only time we don't want to calculate
* intermediate watermarks (since we don't trust the current
* watermarks).
*/
to_intel_atomic_state(state)->skip_intermediate_wm = true;

ret = intel_atomic_check(dev, state);
if (ret) {
/*
Expand All @@ -15316,7 +15400,8 @@ static void sanitize_watermarks(struct drm_device *dev)
for_each_crtc_in_state(state, crtc, cstate, i) {
struct intel_crtc_state *cs = to_intel_crtc_state(cstate);

dev_priv->display.program_watermarks(cs);
cs->wm.need_postvbl_update = true;
dev_priv->display.optimize_watermarks(cs);
}

drm_atomic_state_free(state);
Expand Down
28 changes: 26 additions & 2 deletions drivers/gpu/drm/i915/intel_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@ struct intel_atomic_state {

struct intel_shared_dpll_config shared_dpll[I915_NUM_PLLS];
struct intel_wm_config wm_config;

/*
* Current watermarks can't be trusted during hardware readout, so
* don't bother calculating intermediate watermarks.
*/
bool skip_intermediate_wm;
};

struct intel_plane_state {
Expand Down Expand Up @@ -510,13 +516,29 @@ struct intel_crtc_state {

struct {
/*
* optimal watermarks, programmed post-vblank when this state
* is committed
* Optimal watermarks, programmed post-vblank when this state
* is committed.
*/
union {
struct intel_pipe_wm ilk;
struct skl_pipe_wm skl;
} optimal;

/*
* Intermediate watermarks; these can be programmed immediately
* since they satisfy both the current configuration we're
* switching away from and the new configuration we're switching
* to.
*/
struct intel_pipe_wm intermediate;

/*
* Platforms with two-step watermark programming will need to
* update watermark programming post-vblank to switch from the
* safe intermediate watermarks to the optimal final
* watermarks.
*/
bool need_postvbl_update;
} wm;
};

Expand Down Expand Up @@ -600,6 +622,7 @@ struct intel_crtc {
struct intel_pipe_wm ilk;
struct skl_pipe_wm skl;
} active;

/* allow CxSR on this pipe */
bool cxsr_allowed;
} wm;
Expand Down Expand Up @@ -1565,6 +1588,7 @@ void skl_wm_get_hw_state(struct drm_device *dev);
void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
struct skl_ddb_allocation *ddb /* out */);
uint32_t ilk_pipe_pixel_rate(const struct intel_crtc_state *pipe_config);
bool ilk_disable_lp_wm(struct drm_device *dev);
int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6);

/* intel_sdvo.c */
Expand Down
Loading

0 comments on commit ed4a6a7

Please sign in to comment.