|
@@ -2252,6 +2252,8 @@ static void ilk_merge_wm_level(struct drm_device *dev,
|
|
|
{
|
|
|
const struct intel_crtc *intel_crtc;
|
|
|
|
|
|
+ ret_wm->enable = true;
|
|
|
+
|
|
|
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
|
|
|
const struct intel_pipe_wm *active = &intel_crtc->wm.active;
|
|
|
const struct intel_wm_level *wm = &active->wm[level];
|
|
@@ -2259,16 +2261,19 @@ static void ilk_merge_wm_level(struct drm_device *dev,
|
|
|
if (!active->pipe_enabled)
|
|
|
continue;
|
|
|
|
|
|
+ /*
|
|
|
+ * The watermark values may have been used in the past,
|
|
|
+ * so we must maintain them in the registers for some
|
|
|
+ * time even if the level is now disabled.
|
|
|
+ */
|
|
|
if (!wm->enable)
|
|
|
- return;
|
|
|
+ ret_wm->enable = false;
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
-
|
|
|
- ret_wm->enable = true;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2280,6 +2285,7 @@ static void ilk_wm_merge(struct drm_device *dev,
|
|
|
struct intel_pipe_wm *merged)
|
|
|
{
|
|
|
int level, max_level = ilk_wm_max_level(dev);
|
|
|
+ int last_enabled_level = max_level;
|
|
|
|
|
|
/* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */
|
|
|
if ((INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)) &&
|
|
@@ -2295,15 +2301,19 @@ static void ilk_wm_merge(struct drm_device *dev,
|
|
|
|
|
|
ilk_merge_wm_level(dev, level, wm);
|
|
|
|
|
|
- if (!ilk_validate_wm_level(level, max, wm))
|
|
|
- break;
|
|
|
+ if (level > last_enabled_level)
|
|
|
+ wm->enable = false;
|
|
|
+ else if (!ilk_validate_wm_level(level, max, wm))
|
|
|
+ /* make sure all following levels get disabled */
|
|
|
+ last_enabled_level = level - 1;
|
|
|
|
|
|
/*
|
|
|
* The spec says it is preferred to disable
|
|
|
* FBC WMs instead of disabling a WM level.
|
|
|
*/
|
|
|
if (wm->fbc_val > max->fbc) {
|
|
|
- merged->fbc_wm_enabled = false;
|
|
|
+ if (wm->enable)
|
|
|
+ merged->fbc_wm_enabled = false;
|
|
|
wm->fbc_val = 0;
|
|
|
}
|
|
|
}
|
|
@@ -2358,14 +2368,19 @@ static void ilk_compute_wm_results(struct drm_device *dev,
|
|
|
level = ilk_wm_lp_to_level(wm_lp, merged);
|
|
|
|
|
|
r = &merged->wm[level];
|
|
|
- if (!r->enable)
|
|
|
- break;
|
|
|
|
|
|
- results->wm_lp[wm_lp - 1] = WM3_LP_EN |
|
|
|
+ /*
|
|
|
+ * Maintain the watermark values even if the level is
|
|
|
+ * disabled. Doing otherwise could cause underruns.
|
|
|
+ */
|
|
|
+ results->wm_lp[wm_lp - 1] =
|
|
|
(ilk_wm_lp_latency(dev, level) << WM1_LP_LATENCY_SHIFT) |
|
|
|
(r->pri_val << WM1_LP_SR_SHIFT) |
|
|
|
r->cur_val;
|
|
|
|
|
|
+ if (r->enable)
|
|
|
+ results->wm_lp[wm_lp - 1] |= WM1_LP_SR_EN;
|
|
|
+
|
|
|
if (INTEL_INFO(dev)->gen >= 8)
|
|
|
results->wm_lp[wm_lp - 1] |=
|
|
|
r->fbc_val << WM1_LP_FBC_SHIFT_BDW;
|
|
@@ -2373,6 +2388,10 @@ static void ilk_compute_wm_results(struct drm_device *dev,
|
|
|
results->wm_lp[wm_lp - 1] |=
|
|
|
r->fbc_val << WM1_LP_FBC_SHIFT;
|
|
|
|
|
|
+ /*
|
|
|
+ * Always set WM1S_LP_EN when spr_val != 0, even if the
|
|
|
+ * level is disabled. Doing otherwise could cause underruns.
|
|
|
+ */
|
|
|
if (INTEL_INFO(dev)->gen <= 6 && r->spr_val) {
|
|
|
WARN_ON(wm_lp != 1);
|
|
|
results->wm_lp_spr[wm_lp - 1] = WM1S_LP_EN | r->spr_val;
|