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

Reply via email to