On platforms with both cdclk squash and crawl, bxt_modeset_calc_cdclk()
can pick a target cdclk whose VCO differs from the current one. The
resulting transition causes pipe FIFO underruns:
- Up-crawl from VCO 614400: intermediate frequencies fall below
min_cdclk.
- Down-crawl from VCO 1382400: DBUF ratio changes mid-commit before
watermarks for the new ratio are programmed.
On a VCO-changing transition, prefer the lowest cdclk_table entry that
satisfies min_cdclk at the current VCO (pure squash, no DBUF ratio
change). If none exists, fall back to max_cdclk_freq on the up-crawl
path and stay at the current cdclk on the down-crawl path.
Assisted-by: Claude:claude-sonnet-4.6
Signed-off-by: Nemesa Garg <[email protected]>
---
drivers/gpu/drm/i915/display/intel_cdclk.c | 47 ++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c
b/drivers/gpu/drm/i915/display/intel_cdclk.c
index 189ae2d3cfc9..ead8e59e44a4 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -1598,6 +1598,26 @@ static int bxt_calc_cdclk(struct intel_display *display,
int min_cdclk)
return display->cdclk.max_cdclk_freq;
}
+/*
+ * Lowest cdclk_table entry that satisfies min_cdclk AND keeps the
+ * supplied VCO. Returns 0 if no such entry exists.
+ */
+static int bxt_calc_cdclk_for_vco(struct intel_display *display,
+ int min_cdclk, int vco)
+{
+ const struct intel_cdclk_vals *table = display->cdclk.table;
+ int i;
+
+ for (i = 0; table[i].refclk; i++) {
+ if (table[i].refclk == display->cdclk.hw.ref &&
+ table[i].cdclk >= min_cdclk &&
+ display->cdclk.hw.ref * table[i].ratio == vco)
+ return table[i].cdclk;
+ }
+
+ return 0;
+}
+
static int bxt_calc_cdclk_pll_vco(struct intel_display *display, int cdclk)
{
const struct intel_cdclk_vals *table = display->cdclk.table;
@@ -3300,6 +3320,33 @@ static int bxt_modeset_calc_cdclk(struct
intel_atomic_state *state)
cdclk = bxt_calc_cdclk(display, min_cdclk);
vco = bxt_calc_cdclk_pll_vco(display, cdclk);
+ /*
+ * Guard against VCO-changing CDCLK transitions that cause pipe FIFO
+ * underruns. When crawling up from VCO 614400 the intermediate
+ * frequencies are below min_cdclk; when crawling down from VCO
+ * 1382400 the DBUF ratio changes mid-modeset before watermarks are
+ * reprogrammed. Prefer a same-VCO cdclk_table entry (pure squash,
+ * no DBUF ratio change); only fall back to max_cdclk_freq when no
+ * such entry can satisfy min_cdclk.
+ */
+ if (HAS_CDCLK_SQUASH(display) && HAS_CDCLK_CRAWL(display) &&
+ display->cdclk.hw.vco > 0 && vco > 0 &&
+ display->cdclk.hw.vco != vco) {
+ if (cdclk > display->cdclk.hw.cdclk) {
+ int same_vco_cdclk;
+
+ same_vco_cdclk = bxt_calc_cdclk_for_vco(display,
min_cdclk,
+
display->cdclk.hw.vco);
+ if (same_vco_cdclk)
+ cdclk = same_vco_cdclk;
+ else
+ cdclk = display->cdclk.max_cdclk_freq;
+ } else {
+ cdclk = display->cdclk.hw.cdclk;
+ }
+ vco = bxt_calc_cdclk_pll_vco(display, cdclk);
+ }
+
cdclk_state->logical.vco = vco;
cdclk_state->logical.cdclk = cdclk;
cdclk_state->logical.voltage_level =
--
2.25.1