drm/i915/cx0: Verify C10/C20 pll dividers

Add verification for pll table dividers. The port clock
is computed based on pll tables and, for hdmi case, the
algorithmic model is applied to calculate pll dividers.
If port clock differs more than +-1 kHz from expected value
an drm_warn() is thrown and pll divider differences are
printed out for debugging purposes.

v2:
- Move clock derivation from dividers in intel_cx0pll_enable()
  earlier in the patchset.
- Keep intel_cx0_pll_power_save_wa() in intel_dpll_sanitize_state()
- Use tables[i].name != NULL as a terminating condition.
- Drop duplicate intel_cx0pll_clock_matches() declaration in header.
- Use state vs. params term consistently in intel_c10pll_verify_clock()
  and intel_c20pll_verify_clock().

Signed-off-by: Mika Kahola <mika.kahola@intel.com>
Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
Link: https://patch.msgid.link/20260119093757.2850233-12-mika.kahola@intel.com
This commit is contained in:
Mika Kahola 2026-01-19 09:37:52 +00:00
parent 50ad932880
commit 58213c1d78
3 changed files with 130 additions and 1 deletions

View File

@ -3838,3 +3838,124 @@ void intel_cx0_pll_power_save_wa(struct intel_display *display)
intel_cx0pll_disable(encoder);
}
}
static void intel_c10pll_verify_clock(struct intel_display *display,
int precomputed_clock,
const char *pll_state_name,
const struct intel_c10pll_state *pll_state,
bool is_precomputed_state)
{
struct drm_printer p;
int clock;
clock = intel_c10pll_calc_port_clock(pll_state);
if (intel_dpll_clock_matches(clock, precomputed_clock))
return;
drm_warn(display->drm,
"PLL state %s (%s): clock difference too high: computed %d, pre-computed %d\n",
pll_state_name,
is_precomputed_state ? "precomputed" : "computed",
clock, precomputed_clock);
if (!drm_debug_enabled(DRM_UT_KMS))
return;
p = drm_dbg_printer(display->drm, DRM_UT_KMS, NULL);
drm_printf(&p, "PLL state %s (%s):\n",
pll_state_name,
is_precomputed_state ? "precomputed" : "computed");
intel_c10pll_dump_hw_state(&p, pll_state);
}
static void intel_c10pll_verify_params(struct intel_display *display,
const struct intel_cx0pll_params *pll_params)
{
struct intel_c10pll_state pll_state;
intel_c10pll_verify_clock(display, pll_params->clock_rate, pll_params->name, pll_params->c10, true);
if (!pll_params->is_hdmi)
return;
intel_snps_hdmi_pll_compute_c10pll(&pll_state, pll_params->clock_rate);
intel_c10pll_verify_clock(display, pll_params->clock_rate, pll_params->name, &pll_state, false);
}
static void intel_c20pll_verify_clock(struct intel_display *display,
int precomputed_clock,
const char *pll_state_name,
const struct intel_c20pll_state *pll_state,
bool is_precomputed_state)
{
struct drm_printer p;
int clock;
clock = intel_c20pll_calc_port_clock(pll_state);
if (intel_dpll_clock_matches(clock, precomputed_clock))
return;
drm_warn(display->drm,
"PLL state %s (%s): clock difference too high: computed %d, pre-computed %d\n",
pll_state_name,
is_precomputed_state ? "precomputed" : "computed",
clock, precomputed_clock);
if (!drm_debug_enabled(DRM_UT_KMS))
return;
p = drm_dbg_printer(display->drm, DRM_UT_KMS, NULL);
drm_printf(&p, "PLL state %s (%s):\n",
pll_state_name,
is_precomputed_state ? "precomputed" : "computed");
intel_c20pll_dump_hw_state(&p, pll_state);
}
static void intel_c20pll_verify_params(struct intel_display *display,
const struct intel_cx0pll_params *pll_params)
{
struct intel_c20pll_state pll_state;
intel_c20pll_verify_clock(display, pll_params->clock_rate, pll_params->name, pll_params->c20, true);
if (!pll_params->is_hdmi)
return;
if (intel_c20_compute_hdmi_tmds_pll(display, pll_params->clock_rate, &pll_state) != 0)
return;
intel_c20pll_verify_clock(display, pll_params->clock_rate, pll_params->name, &pll_state, false);
}
static void intel_cx0pll_verify_tables(struct intel_display *display,
const struct intel_cx0pll_params *tables)
{
int i;
for (i = 0; tables[i].name; i++) {
if (tables[i].is_c10)
intel_c10pll_verify_params(display, &tables[i]);
else
intel_c20pll_verify_params(display, &tables[i]);
}
}
void intel_cx0pll_verify_plls(struct intel_display *display)
{
/* C10 */
intel_cx0pll_verify_tables(display, mtl_c10_edp_tables);
intel_cx0pll_verify_tables(display, mtl_c10_dp_tables);
intel_cx0pll_verify_tables(display, mtl_c10_hdmi_tables);
/* C20 */
intel_cx0pll_verify_tables(display, xe2hpd_c20_edp_tables);
intel_cx0pll_verify_tables(display, mtl_c20_dp_tables);
intel_cx0pll_verify_tables(display, xe2hpd_c20_dp_tables);
intel_cx0pll_verify_tables(display, xe3lpd_c20_dp_edp_tables);
intel_cx0pll_verify_tables(display, mtl_c20_hdmi_tables);
}

View File

@ -77,6 +77,7 @@ bool intel_mtl_tbt_pll_readout_hw_state(struct intel_display *display,
struct intel_dpll_hw_state *hw_state);
int intel_mtl_tbt_calc_port_clock(struct intel_encoder *encoder);
void intel_cx0pll_verify_plls(struct intel_display *display);
void intel_cx0_pll_power_save_wa(struct intel_display *display);
void intel_lnl_mac_transmit_lfps(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);

View File

@ -4613,7 +4613,7 @@ void intel_dpll_init(struct intel_display *display)
dpll_mgr = &pch_pll_mgr;
if (!dpll_mgr)
return;
goto out_verify;
dpll_info = dpll_mgr->dpll_info;
@ -4632,6 +4632,13 @@ void intel_dpll_init(struct intel_display *display)
display->dpll.mgr = dpll_mgr;
display->dpll.num_dpll = i;
out_verify:
/*
* TODO: Convert these to a KUnit test or dependent on a kconfig
* debug option.
*/
intel_cx0pll_verify_plls(display);
}
/**