From 7aa40043efc9c6f3d6262567489d3c65892098f0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 28 Jul 2026 21:00:53 +0300 Subject: [PATCH 01/72] drm/xe/display: remove unnecessary includes xe_display_rpm.c no longer needs the display includes, remove them. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/20260728180053.624215-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display_rpm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display_rpm.c b/drivers/gpu/drm/xe/display/xe_display_rpm.c index b3db40035499..548b503aa024 100644 --- a/drivers/gpu/drm/xe/display/xe_display_rpm.c +++ b/drivers/gpu/drm/xe/display/xe_display_rpm.c @@ -3,8 +3,6 @@ #include -#include "intel_display_core.h" -#include "intel_display_rpm.h" #include "xe_device.h" #include "xe_device_types.h" #include "xe_pm.h" From 7a862851e429b4f8b3a3a675258812e99de873b0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 28 Jul 2026 20:59:59 +0300 Subject: [PATCH 02/72] drm/i915/dpt: remove unused include i915_dpt.c no longer needs intel_display_core.h, remove. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/20260728175959.622050-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_dpt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_dpt.c b/drivers/gpu/drm/i915/i915_dpt.c index fcd7cced771d..e01dc4de1788 100644 --- a/drivers/gpu/drm/i915/i915_dpt.c +++ b/drivers/gpu/drm/i915/i915_dpt.c @@ -6,7 +6,6 @@ #include #include -#include "display/intel_display_core.h" #include "gem/i915_gem_domain.h" #include "gem/i915_gem_internal.h" #include "gem/i915_gem_lmem.h" From 8565e6cfb8f19e066896efbc4cc126adb92cb38a Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:08 +0530 Subject: [PATCH 03/72] drm/i915/psr: Do not enable PSR2 when CMRR is enabled CMRR is mutually exclusive to PSR2, do not enable PSR2 when CMRR is enabled. --v2: - Restrict selective update config check (Ankit) - Commit message change --v3: - Commit changes --v4: - Add check to psr2_config_valid (Jouni) --v5: - Correct debug message --v6: - Commit message update Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-2-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 40e3d7095996..410778e73a2d 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1491,6 +1491,12 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, int crtc_vdisplay = crtc_state->hw.adjusted_mode.crtc_vdisplay; int psr_max_h = 0, psr_max_v = 0, max_bpp = 0; + if (crtc_state->cmrr.enable) { + drm_dbg_kms(display->drm, + "PSR2 cannot be enabled when CMRR is enabled\n"); + return false; + } + if (!connector->dp.psr_caps.su_support || display->params.enable_psr == 1) return false; From dfd1495664ef26662fa77ec829df88e41b6fd1fc Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:09 +0530 Subject: [PATCH 04/72] drm/i915/vrr: Restrict CMRR enable condition to VRR-TG-default platforms CMRR is currently advertised on all HAS_CMRR platforms, but it depends on the VRR timing generator being the default timing generator, which currently only applies to DISPLAY_VER >= 30. Gate the CMRR enable condition and readout accordingly so CMRR is only reported on supported platforms. No functional change. --v2: - Create a separate function to check if CMRR possible --v3: - Simplify commit message (Chaitanya) - Remove redundant outer parenthesis (Chaitanya) Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-3-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_vrr.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 51e4f3309b8b..b0c9101e6ed3 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -187,15 +187,21 @@ int intel_vrr_vmax_vblank_start(const struct intel_crtc_state *crtc_state) return intel_vrr_vmax_vtotal(crtc_state) - crtc_state->vrr.guardband; } +static bool intel_vrr_cmrr_possible(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + + return HAS_CMRR(display) && intel_vrr_always_use_vrr_tg(display); +} + static bool is_cmrr_frac_required(struct intel_crtc_state *crtc_state) { - struct intel_display *display = to_intel_display(crtc_state); int calculated_refresh_k, actual_refresh_k, pixel_clock_per_line; struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; /* Avoid CMRR for now till we have VRR with fixed timings working */ - if (!HAS_CMRR(display) || true) + if (!intel_vrr_cmrr_possible(crtc_state) || true) return false; actual_refresh_k = @@ -1066,7 +1072,7 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) trans_vrr_ctl = intel_de_read(display, TRANS_VRR_CTL(display, cpu_transcoder)); - if (HAS_CMRR(display)) + if (intel_vrr_cmrr_possible(crtc_state)) crtc_state->cmrr.enable = (trans_vrr_ctl & VRR_CTL_CMRR_ENABLE); if (crtc_state->cmrr.enable) { From 9edafeb040998a3bf9947044a620e88041ed9747 Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:10 +0530 Subject: [PATCH 05/72] drm/i915/vrr: Add per-CRTC vrr/cmrr debugfs control Add a per-CRTC debugfs file 'intel_vrr_target_refresh_rate' that lets the user force a CMRR target refresh rate and video-mode requirement. The file uses a "numerator/denominator" format: - numerator: requested refresh rate in milli-Hz (refresh rate in Hz * 1000, e.g. 60000 for 60 Hz) - denominator: 1000 for a 1:1 ratio (no video timing) or 1001 for the 1000/1001 video timing Reading the file reports the currently stored values; writing updates them. The file is created only on platforms with VRR and CMRR support. --v2: - Drop the "vrr" debugfs subdirectory and expose a single flat, intel_-prefixed "intel_vrr_cmrr" file (Jani, Nikula) - Rename struct intel_crtc.cmrr to force_cmrr to make its purpose explicit (Chaitanya) - Fix parse comment: numerator unit is milli-Hz, not KHz (Chaitanya) - Add debugfs/intel_ prefixes to the debugfs handler functions (Chaitanya) - Expand commit message with debugfs entry semantics (Chaitanya) --v3: - Rename debugfs function and file name (Chaitanya) - Remove railing new line at EOF (Chaitanya) - Update CMRR guard conditions (Chaitanya) - Add 0/0 default case. (Ramanaidu) --v4: - Correct CMRR guard check while creating debugfs --v5: - Commit message update (Chaitanya) - Add support for 0/0 writing (Validation) Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-4-mitulkumar.ajitkumar.golani@intel.com --- .../drm/i915/display/intel_display_debugfs.c | 2 + .../drm/i915/display/intel_display_types.h | 5 + drivers/gpu/drm/i915/display/intel_vrr.c | 112 ++++++++++++++++++ drivers/gpu/drm/i915/display/intel_vrr.h | 2 + 4 files changed, 121 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 3f02868ef105..2bbf4760dc30 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -49,6 +49,7 @@ #include "intel_psr.h" #include "intel_psr_regs.h" #include "intel_vdsc.h" +#include "intel_vrr.h" #include "intel_wm.h" #include "intel_tc.h" @@ -1395,6 +1396,7 @@ void intel_crtc_debugfs_add(struct intel_crtc *crtc) intel_drrs_crtc_debugfs_add(crtc); intel_fbc_crtc_debugfs_add(crtc); hsw_ips_crtc_debugfs_add(crtc); + intel_vrr_crtc_debugfs_add(crtc); debugfs_create_file("i915_current_bpc", 0444, root, crtc, &i915_current_bpc_fops); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b7cc361fd955..46dd980b315a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1553,6 +1553,11 @@ struct intel_crtc { u64 flip_count; } dc_balance; + struct { + u32 numerator; + u32 denominator; + } force_cmrr; + int scanline_offset; struct { diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index b0c9101e6ed3..615c8fa8c063 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -4,6 +4,10 @@ * */ +#include +#include +#include + #include #include @@ -1237,3 +1241,111 @@ int intel_vrr_dcb_vmax_vblank_start_final(const struct intel_crtc_state *crtc_st return intel_vrr_vblank_start(crtc_state, VRR_DCB_VMAX(tmp) + 1); } + +static +int intel_vrr_cmrr_parse_ratio(char *str, u32 *numerator, u32 *denominator) +{ + char *sep; + int ret; + + /* + * Parse a "numerator/denominator" CMRR ratio string. The numerator + * is the requested refresh rate in milli-Hz (refresh rate in Hz * 1000) + * and the denominator selects the timing: 1000 for a 1:1 ratio + * (no video timing) or 1001 for the 1000/1001 video timing. + */ + + sep = strchr(str, '/'); + if (!sep) + return -EINVAL; + + *sep = '\0'; + + ret = kstrtou32(strim(str), 10, numerator); + if (ret) + return ret; + + ret = kstrtou32(strim(sep + 1), 10, denominator); + if (ret) + return ret; + /* + * "0/0" clears any previously configured CMRR override. + * A zero numerator already means "CMRR not requested" in + * intel_vrr_cmrr_compute_config(), so just let it through. + */ + if (*numerator == 0 && *denominator == 0) + return 0; + + if (*numerator == 0) + return -EINVAL; + + if (*denominator != 1000 && *denominator != 1001) + return -EINVAL; + + return 0; +} + +static int intel_vrr_debugfs_target_rr_show(struct seq_file *m, void *data) +{ + struct intel_crtc *crtc = m->private; + + seq_printf(m, "%u/%u\n", crtc->force_cmrr.numerator, crtc->force_cmrr.denominator); + + return 0; +} + +static int intel_vrr_debugfs_target_rr_open(struct inode *inode, struct file *file) +{ + return single_open(file, intel_vrr_debugfs_target_rr_show, inode->i_private); +} + +static ssize_t intel_vrr_debugfs_target_rr_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct intel_crtc *crtc = m->private; + u32 numerator, denominator; + char kbuf[32]; + int ret; + + if (len >= sizeof(kbuf)) + return -EINVAL; + + if (copy_from_user(kbuf, ubuf, len)) + return -EFAULT; + + kbuf[len] = '\0'; + + ret = intel_vrr_cmrr_parse_ratio(kbuf, &numerator, &denominator); + if (ret) + return ret; + + if (crtc->force_cmrr.numerator == numerator && + crtc->force_cmrr.denominator == denominator) + return len; + + crtc->force_cmrr.numerator = numerator; + crtc->force_cmrr.denominator = denominator; + + return len; +} + +static const struct file_operations intel_vrr_debugfs_target_rr_fops = { + .owner = THIS_MODULE, + .open = intel_vrr_debugfs_target_rr_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = intel_vrr_debugfs_target_rr_write, +}; + +void intel_vrr_crtc_debugfs_add(struct intel_crtc *crtc) +{ + struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); + + if (!intel_vrr_cmrr_possible(crtc_state)) + return; + + debugfs_create_file("intel_vrr_target_refresh_rate", 0600, crtc->base.debugfs_entry, + crtc, &intel_vrr_debugfs_target_rr_fops); +} diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h index 55e9c429f579..19c7990be1b2 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.h +++ b/drivers/gpu/drm/i915/display/intel_vrr.h @@ -56,4 +56,6 @@ int intel_vrr_dcb_vmax_vblank_start_next(const struct intel_crtc_state *crtc_sta int intel_vrr_dcb_vmin_vblank_start_final(const struct intel_crtc_state *crtc_state); int intel_vrr_dcb_vmax_vblank_start_final(const struct intel_crtc_state *crtc_state); +void intel_vrr_crtc_debugfs_add(struct intel_crtc *crtc); + #endif /* __INTEL_VRR_H__ */ From 77ce0fb5f7453cf795b8ec87064a6f0f712d537a Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:11 +0530 Subject: [PATCH 06/72] drm/i915/vrr: Update AS_SDP target_rr_divider based on CMRR config request Compute AS_SDP target_rr_divider based on actual CMRR request requirement. Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-5-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 0b84f6fb257d..a6edfd4c86b2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3086,6 +3086,7 @@ static void intel_dp_compute_as_sdp(struct intel_dp *intel_dp, struct drm_dp_as_sdp *as_sdp = &crtc_state->infoframes.as_sdp; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); /* * #FIXME: SDP/infoframe updates aren’t truly atomic, and with the new @@ -3106,7 +3107,11 @@ static void intel_dp_compute_as_sdp(struct intel_dp *intel_dp, if (crtc_state->cmrr.enable) { as_sdp->mode = DP_AS_SDP_FAVT_TRR_REACHED; as_sdp->target_rr = drm_mode_vrefresh(adjusted_mode); - as_sdp->target_rr_divider = true; + + if (crtc->force_cmrr.denominator == 1001) + as_sdp->target_rr_divider = true; + else + as_sdp->target_rr_divider = false; } else if (crtc_state->vrr.enable) { as_sdp->mode = DP_AS_SDP_AVT_DYNAMIC_VTOTAL; } else { From 6f80bda1d088b9df6d3e91c5b5ad48406de4e7e4 Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:12 +0530 Subject: [PATCH 07/72] drm/i915/display: Move CMRR crtc_state members under VRR Move CMRR crtc state members under VRR infrastructure as it is enabled during fixed-refresh-rate VRR timing generator is enabled. --v2: - Move cmrr structure under vrr umbrella. - Remove dups. (Jani) --v3: - Update commit message Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-6-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 2 +- drivers/gpu/drm/i915/display/intel_display.c | 10 +++---- .../drm/i915/display/intel_display_types.h | 12 ++++---- drivers/gpu/drm/i915/display/intel_dp.c | 2 +- drivers/gpu/drm/i915/display/intel_psr.c | 2 +- drivers/gpu/drm/i915/display/intel_vrr.c | 30 +++++++++---------- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index c8e0f90af910..3c0f5b2fb2e7 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -326,7 +326,7 @@ void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state) XELPD_VRR_CTL_VRR_GUARDBAND(crtc_state->vrr.guardband); /* TODO: The code below may need to be revisited once CMRR is enabled */ - if (crtc_state->cmrr.enable) + if (crtc_state->vrr.cmrr.enable) vrr_ctl |= VRR_CTL_CMRR_ENABLE; intel_de_write(display, TRANS_VRR_CTL(display, cmtg_transcoder), vrr_ctl); diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 9dfd095f325f..8ce762188665 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -950,8 +950,8 @@ static bool vrr_params_changed(const struct intel_crtc_state *old_crtc_state, static bool cmrr_params_changed(const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *new_crtc_state) { - return old_crtc_state->cmrr.cmrr_m != new_crtc_state->cmrr.cmrr_m || - old_crtc_state->cmrr.cmrr_n != new_crtc_state->cmrr.cmrr_n; + return old_crtc_state->vrr.cmrr.cmrr_m != new_crtc_state->vrr.cmrr.cmrr_m || + old_crtc_state->vrr.cmrr.cmrr_n != new_crtc_state->vrr.cmrr.cmrr_n; } static bool intel_crtc_vrr_enabling(struct intel_atomic_state *state, @@ -5536,9 +5536,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(vrr.flipline); PIPE_CONF_CHECK_I(vrr.vsync_start); PIPE_CONF_CHECK_I(vrr.vsync_end); - PIPE_CONF_CHECK_LLI(cmrr.cmrr_m); - PIPE_CONF_CHECK_LLI(cmrr.cmrr_n); - PIPE_CONF_CHECK_BOOL(cmrr.enable); + PIPE_CONF_CHECK_LLI(vrr.cmrr.cmrr_m); + PIPE_CONF_CHECK_LLI(vrr.cmrr.cmrr_n); + PIPE_CONF_CHECK_BOOL(vrr.cmrr.enable); PIPE_CONF_CHECK_I(vrr.dc_balance.vmin); PIPE_CONF_CHECK_I(vrr.dc_balance.vmax); PIPE_CONF_CHECK_I(vrr.dc_balance.guardband); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 46dd980b315a..20a07ea06b5e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1408,13 +1408,13 @@ struct intel_crtc_state { u16 max_increase, max_decrease; u16 vblank_target; } dc_balance; - } vrr; - /* Content Match Refresh Rate state */ - struct { - bool enable; - u64 cmrr_n, cmrr_m; - } cmrr; + /* Content Match Refresh Rate state */ + struct { + bool enable; + u64 cmrr_n, cmrr_m; + } cmrr; + } vrr; /* Stream Splitter for eDP MSO */ struct { diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index a6edfd4c86b2..0e2003f3d6cb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3104,7 +3104,7 @@ static void intel_dp_compute_as_sdp(struct intel_dp *intel_dp, as_sdp->revision = 0x2; as_sdp->vtotal = intel_vrr_vmin_vtotal(crtc_state); - if (crtc_state->cmrr.enable) { + if (crtc_state->vrr.cmrr.enable) { as_sdp->mode = DP_AS_SDP_FAVT_TRR_REACHED; as_sdp->target_rr = drm_mode_vrefresh(adjusted_mode); diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 410778e73a2d..52836496040f 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1491,7 +1491,7 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, int crtc_vdisplay = crtc_state->hw.adjusted_mode.crtc_vdisplay; int psr_max_h = 0, psr_max_v = 0, max_bpp = 0; - if (crtc_state->cmrr.enable) { + if (crtc_state->vrr.cmrr.enable) { drm_dbg_kms(display->drm, "PSR2 cannot be enabled when CMRR is enabled\n"); return false; diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 615c8fa8c063..6d3333fd5281 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -235,12 +235,12 @@ cmrr_get_vtotal(struct intel_crtc_state *crtc_state, bool video_mode_required) multiplier_n = 1000; } - crtc_state->cmrr.cmrr_n = mul_u32_u32(desired_refresh_rate * adjusted_mode->crtc_htotal, - multiplier_n); + crtc_state->vrr.cmrr.cmrr_n = mul_u32_u32(desired_refresh_rate * adjusted_mode->crtc_htotal, + multiplier_n); vtotal = DIV_ROUND_UP_ULL(mul_u32_u32(adjusted_mode->crtc_clock * 1000, multiplier_n), - crtc_state->cmrr.cmrr_n); + crtc_state->vrr.cmrr.cmrr_n); adjusted_pixel_rate = mul_u32_u32(adjusted_mode->crtc_clock * 1000, multiplier_m); - crtc_state->cmrr.cmrr_m = do_div(adjusted_pixel_rate, crtc_state->cmrr.cmrr_n); + crtc_state->vrr.cmrr.cmrr_m = do_div(adjusted_pixel_rate, crtc_state->vrr.cmrr.cmrr_n); return vtotal; } @@ -258,7 +258,7 @@ void intel_vrr_compute_cmrr_timings(struct intel_crtc_state *crtc_state) crtc_state->vrr.vmin = crtc_state->vrr.vmax; crtc_state->vrr.flipline = crtc_state->vrr.vmin; - crtc_state->cmrr.enable = true; + crtc_state->vrr.cmrr.enable = true; crtc_state->mode_flags |= I915_MODE_FLAG_VRR; } @@ -651,15 +651,15 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) return; } - if (crtc_state->cmrr.enable) { + if (crtc_state->vrr.cmrr.enable) { intel_de_write(display, TRANS_CMRR_M_HI(display, cpu_transcoder), - upper_32_bits(crtc_state->cmrr.cmrr_m)); + upper_32_bits(crtc_state->vrr.cmrr.cmrr_m)); intel_de_write(display, TRANS_CMRR_M_LO(display, cpu_transcoder), - lower_32_bits(crtc_state->cmrr.cmrr_m)); + lower_32_bits(crtc_state->vrr.cmrr.cmrr_m)); intel_de_write(display, TRANS_CMRR_N_HI(display, cpu_transcoder), - upper_32_bits(crtc_state->cmrr.cmrr_n)); + upper_32_bits(crtc_state->vrr.cmrr.cmrr_n)); intel_de_write(display, TRANS_CMRR_N_LO(display, cpu_transcoder), - lower_32_bits(crtc_state->cmrr.cmrr_n)); + lower_32_bits(crtc_state->vrr.cmrr.cmrr_n)); } intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder); @@ -980,7 +980,7 @@ void intel_vrr_enable(const struct intel_crtc_state *crtc_state) intel_vrr_enable_dc_balancing(crtc_state); if (!intel_vrr_always_use_vrr_tg(display)) - intel_vrr_tg_enable(crtc_state, crtc_state->cmrr.enable); + intel_vrr_tg_enable(crtc_state, crtc_state->vrr.cmrr.enable); } void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) @@ -1077,12 +1077,12 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) TRANS_VRR_CTL(display, cpu_transcoder)); if (intel_vrr_cmrr_possible(crtc_state)) - crtc_state->cmrr.enable = (trans_vrr_ctl & VRR_CTL_CMRR_ENABLE); + crtc_state->vrr.cmrr.enable = (trans_vrr_ctl & VRR_CTL_CMRR_ENABLE); - if (crtc_state->cmrr.enable) { - crtc_state->cmrr.cmrr_n = + if (crtc_state->vrr.cmrr.enable) { + crtc_state->vrr.cmrr.cmrr_n = intel_de_read64_2x32(display, TRANS_CMRR_N_LO(display, cpu_transcoder)); - crtc_state->cmrr.cmrr_m = + crtc_state->vrr.cmrr.cmrr_m = intel_de_read64_2x32(display, TRANS_CMRR_M_LO(display, cpu_transcoder)); } From 39ca58943e4666c49eb568cf9c5c6b19e5042eb8 Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:13 +0530 Subject: [PATCH 08/72] drm/i915/vrr: Compute CMRR fractional timings generically Rework the fractional CMRR computation into a generic, transcoder-agnostic helper driven by an explicit per-CRTC debugfs target, replacing the previous disabled, eDP-only code path. Compute CMRR_M and CMRR_N timings based on the video mode requirements. A 1:1 ratio (denominator == 1000) means no video timing is required. Note that the CMRR enable path is wired up separately; this patch only lays down the generic computation. Remove computation of mode_flags, I915_MODE_FLAG_VRR, as CMRR is being moved to the fixed refresh rate path. Also return early from the CMRR computation if the existing computation is already sufficient. --v2: - Derive video_mode locally instead of caching it in the persistent struct intel_crtc state (Jani, Chaitanya) - Fix numerator unit in comment: milli-Hz, not kHz (Chaitanya) - Fix "requirement" typo and clarify that CMRR is not yet enabled in the commit message (Chaitanya) - Fix precision issue while computing the M/N ratio (Chaitanya) - Rename multiplier_m and multiplier_n to improve readability (Chaitanya) - Compute vtotal as it is required for dithering as per the algorithm implementation (Chaitanya) - Replace the misleading adjusted_pixel_rate with dividend, which is more descriptive --v3: - Mention the rationale for not computing I915_MODE_FLAG_VRR (Chaitanya) - Remove the redundant return statement at the end of the CMRR computation (Chaitanya) - Correct cmrr_n calculation (Chaitanya) - Add an early return if the current computation is already sufficient to drive the mode without CMRR (Chaitanya) - Round up requested_refresh_rate (Validation) - Fix integer overflow --v4: - Update cmrr_n calculation to avoid aggressive dithering - Use div64_u64 to truncate to the floor value instead of DIV_ROUND_UP_ULL, which rounds up to the ceiling value - Add a TODO comment to the cmrr_n calculation - Add a condition for custom refresh rate requests - Update the CMRR computation comment block in compute_config --v5: - Simplify computation - Correct typo in commit message (Chaitanya) - Compute early return condition (Chaitanya) --v6: - Simplify commit message - Simplify video timing requirement compute function (Chaitanya) Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-7-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_vrr.c | 135 ++++++++++++----------- 1 file changed, 70 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 6d3333fd5281..f406f34b816e 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -27,9 +27,6 @@ #include "skl_prefill.h" #include "skl_watermark.h" -#define FIXED_POINT_PRECISION 100 -#define CMRR_PRECISION_TOLERANCE 10 - /* * Tunable parameters for DC Balance correction. * These are captured based on experimentations. @@ -198,68 +195,78 @@ static bool intel_vrr_cmrr_possible(const struct intel_crtc_state *crtc_state) return HAS_CMRR(display) && intel_vrr_always_use_vrr_tg(display); } -static bool -is_cmrr_frac_required(struct intel_crtc_state *crtc_state) +static void +intel_vrr_cmrr_compute_config(struct intel_crtc_state *crtc_state) { - int calculated_refresh_k, actual_refresh_k, pixel_clock_per_line; + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + u64 dividend; + u32 mode_rate_mhz; + u32 requested_rate_mhz = crtc->force_cmrr.numerator; + bool video_mode = crtc->force_cmrr.denominator != 1000; + int rr_multiplier = video_mode ? 1000 : 1; + int rr_divider = video_mode ? 1001 : 1; - /* Avoid CMRR for now till we have VRR with fixed timings working */ - if (!intel_vrr_cmrr_possible(crtc_state) || true) - return false; + if (!intel_vrr_cmrr_possible(crtc_state)) + return; - actual_refresh_k = - drm_mode_vrefresh(adjusted_mode) * FIXED_POINT_PRECISION; - pixel_clock_per_line = - adjusted_mode->crtc_clock * 1000 / adjusted_mode->crtc_htotal; - calculated_refresh_k = - pixel_clock_per_line * FIXED_POINT_PRECISION / adjusted_mode->crtc_vtotal; + /* No CMRR ratio configured through debugfs */ + if (!requested_rate_mhz) + return; - if ((actual_refresh_k - calculated_refresh_k) < CMRR_PRECISION_TOLERANCE) - return false; - - return true; -} - -static unsigned int -cmrr_get_vtotal(struct intel_crtc_state *crtc_state, bool video_mode_required) -{ - int multiplier_m = 1, multiplier_n = 1, vtotal, desired_refresh_rate; - u64 adjusted_pixel_rate; - struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - - desired_refresh_rate = drm_mode_vrefresh(adjusted_mode); - - if (video_mode_required) { - multiplier_m = 1001; - multiplier_n = 1000; + /* Requested rate must match the mode's nominal (integer) refresh rate */ + if (DIV_ROUND_CLOSEST(requested_rate_mhz, 1000) != + drm_mode_vrefresh(adjusted_mode)) { + drm_dbg_kms(display->drm, + "[CRTC:%d:%s] CMRR requested %u.%03u Hz doesn't match mode %d Hz\n", + crtc->base.base.id, crtc->base.name, + requested_rate_mhz / 1000, requested_rate_mhz % 1000, + drm_mode_vrefresh(adjusted_mode)); + return; } - crtc_state->vrr.cmrr.cmrr_n = mul_u32_u32(desired_refresh_rate * adjusted_mode->crtc_htotal, - multiplier_n); - vtotal = DIV_ROUND_UP_ULL(mul_u32_u32(adjusted_mode->crtc_clock * 1000, multiplier_n), - crtc_state->vrr.cmrr.cmrr_n); - adjusted_pixel_rate = mul_u32_u32(adjusted_mode->crtc_clock * 1000, multiplier_m); - crtc_state->vrr.cmrr.cmrr_m = do_div(adjusted_pixel_rate, crtc_state->vrr.cmrr.cmrr_n); + /* Actual rate produced by the current timings, in milli-Hz */ + mode_rate_mhz = + DIV_ROUND_CLOSEST_ULL((u64)adjusted_mode->crtc_clock * 1000 * 1000, + adjusted_mode->crtc_vtotal * + adjusted_mode->crtc_htotal); - return vtotal; -} - -static -void intel_vrr_compute_cmrr_timings(struct intel_crtc_state *crtc_state) -{ /* - * TODO: Compute precise target refresh rate to determine - * if video_mode_required should be true. Currently set to - * false due to uncertainty about the precise target - * refresh Rate. + * 1:1 request already satisfied by the mode -> CMRR not needed. */ - crtc_state->vrr.vmax = cmrr_get_vtotal(crtc_state, false); - crtc_state->vrr.vmin = crtc_state->vrr.vmax; - crtc_state->vrr.flipline = crtc_state->vrr.vmin; + if (!video_mode && DIV_ROUND_CLOSEST(mode_rate_mhz, 10) == + DIV_ROUND_CLOSEST(requested_rate_mhz, 10)) { + drm_dbg_kms(display->drm, + "[CRTC:%d:%s] %u.%03u Hz can be driven without CMRR\n", + crtc->base.base.id, crtc->base.name, + requested_rate_mhz / 1000, requested_rate_mhz % 1000); + return; + } - crtc_state->vrr.cmrr.enable = true; - crtc_state->mode_flags |= I915_MODE_FLAG_VRR; + /* + * Let pixel_clock_hz = adjusted_mode->crtc_clock * 1000. + * + * cmrr_n = (requested_rate_mhz x htotal x rr_multiplier) / 1000 + * cmrr_m = (pixel_clock_hz x rr_divider) % cmrr_n + * + * where requested_rate_mhz is the requested refresh rate in milli-Hz + * and rr_multiplier/rr_divider = 1000/1001 when the video timing + * is required, else 1/1. The integer vtotal term is tracked in SW + * (it is the programmed mode vtotal) while the fractional part + * represented by cmrr_m/cmrr_n is tracked in HW. + * + * TODO: Using the actual desired rate for cmrr_n in video + * mode produces more aggressive vtotal dithering than + * expected; revisit once the Bspec algorithm is clarified. + */ + crtc_state->vrr.cmrr.cmrr_n = + div64_u64((u64)requested_rate_mhz * + adjusted_mode->crtc_htotal * rr_multiplier, 1000); + dividend = (u64)adjusted_mode->crtc_clock * rr_divider * 1000; + adjusted_mode->crtc_vtotal = div64_u64_rem(dividend, + crtc_state->vrr.cmrr.cmrr_n, + &crtc_state->vrr.cmrr.cmrr_m); } static @@ -435,8 +442,6 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state, struct intel_display *display = to_intel_display(crtc_state); struct intel_connector *connector = to_intel_connector(conn_state->connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - bool is_edp = intel_dp_is_edp(intel_dp); struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; int vmin, vmax; @@ -470,12 +475,17 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state, vmax = vmin; } - if (crtc_state->uapi.vrr_enabled && vmin < vmax) + if (crtc_state->uapi.vrr_enabled && vmin < vmax) { intel_vrr_compute_vrr_timings(crtc_state, vmin, vmax); - else if (is_cmrr_frac_required(crtc_state) && is_edp) - intel_vrr_compute_cmrr_timings(crtc_state); - else + } else { + /* + * CMRR is a fixed average Vtotal mode and is only computed on + * the fixed refresh rate path. It is generic across transcoders + * and gated on platform support and a valid debugfs ratio. + */ + intel_vrr_cmrr_compute_config(crtc_state); intel_vrr_compute_fixed_rr_timings(crtc_state); + } if (HAS_AS_SDP(display)) { crtc_state->vrr.vsync_start = @@ -1142,11 +1152,6 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) intel_vrr_get_dc_balance_config(crtc_state); - /* - * #TODO: For Both VRR and CMRR the flag I915_MODE_FLAG_VRR is set for mode_flags. - * Since CMRR is currently disabled, set this flag for VRR for now. - * Need to keep this in mind while re-enabling CMRR. - */ if (crtc_state->vrr.enable) crtc_state->mode_flags |= I915_MODE_FLAG_VRR; From 9083322aa83d680daca956c5c63a5912f8e11f9c Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:14 +0530 Subject: [PATCH 09/72] drm/i915/vrr: Latch CMRR ratio via atomic commit on debugfs write Writing the per-CRTC "intel_vrr_target_refresh_rate" debugfs file only updates crtc->force_cmrr, a side channel that is not tracked by the atomic state. The requested ratio therefore does not reach the hardware until some unrelated commit recomputes the pipe config. Kick an internal atomic commit for the CRTC from the debugfs write and mark the CRTC mode as changed so the pipe config is recomputed. Note that this commit may or may not be downgraded to a fastset, depending on whether the computed vblank value (with LRR disabled) changes, so it is not characterised as a fastset. If the commit fails, restore the previous numerator/denominator so that force_cmrr does not hold on to bad values, which would otherwise make all subsequent commits fail. --v2: - Commit message and code comment update (Chaitanya) - Commit header update - Restore to old known debugfs values when commit fails (Chaitanya) Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-8-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_vrr.c | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index f406f34b816e..3463533b1ea1 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -8,10 +8,12 @@ #include #include +#include #include #include #include "intel_alpm.h" +#include "intel_atomic.h" #include "intel_cmtg.h" #include "intel_crtc.h" #include "intel_de.h" @@ -1304,12 +1306,67 @@ static int intel_vrr_debugfs_target_rr_open(struct inode *inode, struct file *fi return single_open(file, intel_vrr_debugfs_target_rr_show, inode->i_private); } +/* + * Force an internal commit on @crtc so that a CMRR ratio programmed + * via debugfs gets recomputed and latched into hardware. + * + * CMRR only alters the (average) vtotal. Depending on whether the + * computed vblank value (with LRR disabled) changes, this commit may or + * may not be downgraded to a fastset. + */ +static int intel_vrr_cmrr_commit_force(struct intel_crtc *crtc) +{ + struct intel_display *display = to_intel_display(crtc); + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_commit *state; + struct intel_crtc_state *crtc_state; + int ret = 0; + + state = drm_atomic_commit_alloc(display->drm); + if (!state) + return -ENOMEM; + + drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); + + state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; + +retry: + crtc_state = intel_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto out; + } + + if (!crtc_state->hw.active) + goto out; + + /* Mark mode as changed to trigger a pipe recompute + update() */ + crtc_state->uapi.mode_changed = true; + + ret = drm_atomic_commit(state); +out: + if (ret == -EDEADLK) { + drm_atomic_commit_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + drm_atomic_commit_put(state); + + return ret; +} + static ssize_t intel_vrr_debugfs_target_rr_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { struct seq_file *m = file->private_data; struct intel_crtc *crtc = m->private; u32 numerator, denominator; + u32 old_numerator, old_denominator; char kbuf[32]; int ret; @@ -1329,9 +1386,29 @@ static ssize_t intel_vrr_debugfs_target_rr_write(struct file *file, const char _ crtc->force_cmrr.denominator == denominator) return len; + old_numerator = crtc->force_cmrr.numerator; + old_denominator = crtc->force_cmrr.denominator; + crtc->force_cmrr.numerator = numerator; crtc->force_cmrr.denominator = denominator; + /* + * The debugfs value is a side channel that is not tracked by the atomic + * state, so kick an internal commit to recompute and latch the + * new CMRR parameters. + */ + ret = intel_vrr_cmrr_commit_force(crtc); + if (ret) { + /* + * Restore the last known good ratio so that force_cmrr does not + * hold on to bad values, which would make all subsequent commits + * fail. + */ + crtc->force_cmrr.numerator = old_numerator; + crtc->force_cmrr.denominator = old_denominator; + return ret; + } + return len; } From 6301b17d606db492c1c3f99b3aa6bfaaedcd5315 Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:15 +0530 Subject: [PATCH 10/72] drm/i915/vrr: Program CMRR enable/disable from transcoder timings Move the CMRR M/N register writes into a new intel_vrr_set_cmrr_timings() helper called from intel_vrr_set_fixed_rr_timings(), so CMRR is programmed together with the fixed rr timings for both the CMTG and CPU transcoder, instead of being open-coded in intel_vrr_set_transcoder_timings(). Drop the now-unused cmrr_enable argument to intel_vrr_tg_enable(). No functional change intended for non-CMRR configurations. --v2: - Commit message changes. - Added Simplified enable/disable sequence. (Chaitanya) --v3: - Guard CMRR enable condition (Chaitanya) - Comment changes updated (Chaitanya) - Squash register write commits together and avoid double writing issue. (Chaitanya) --v4: - Guard CMRR enable/disable with platform check --v5: - Update CMRR in enable/disable path --v6: - Simplify enable/disable calls (Chaitanya) - Make function usable for common cmtg and cpu transcoder (Chaitanya) --v7: - Update commit message (Chaitanya) Signed-off-by: Mitul Golani Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-9-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_vrr.c | 59 ++++++++++++++++-------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 3463533b1ea1..653581ed4e90 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -344,6 +344,36 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state) return intel_vrr_fixed_rr_hw_vtotal(crtc_state); } +static void +intel_vrr_set_cmrr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) +{ + struct intel_display *display = to_intel_display(crtc_state); + + if (!intel_vrr_cmrr_possible(crtc_state)) + return; + + intel_de_write(display, TRANS_CMRR_M_HI(display, transcoder), + upper_32_bits(crtc_state->vrr.cmrr.cmrr_m)); + intel_de_write(display, TRANS_CMRR_M_LO(display, transcoder), + lower_32_bits(crtc_state->vrr.cmrr.cmrr_m)); + intel_de_write(display, TRANS_CMRR_N_LO(display, transcoder), + lower_32_bits(crtc_state->vrr.cmrr.cmrr_n)); + intel_de_write(display, TRANS_CMRR_N_HI(display, transcoder), + upper_32_bits(crtc_state->vrr.cmrr.cmrr_n)); + + /* + * On always-VRR-TG platforms the fastset path does not rewrite the + * whole TRANS_VRR_CTL, so RMW only the CMRR enable bit here. On a + * modeset the authoritative writes (intel_vrr_tg_enable(), + * intel_cmtg_set_vrr_ctl()) carry the same bit, so this stays + * consistent. + */ + intel_de_rmw(display, TRANS_VRR_CTL(display, transcoder), + VRR_CTL_CMRR_ENABLE, + crtc_state->vrr.cmrr.enable ? VRR_CTL_CMRR_ENABLE : 0); +} + void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, enum transcoder transcoder) { @@ -352,6 +382,8 @@ void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, if (!intel_vrr_possible(crtc_state)) return; + intel_vrr_set_cmrr_timings(crtc_state, transcoder); + intel_de_write(display, TRANS_VRR_VMIN(display, transcoder), intel_vrr_fixed_rr_hw_vmin(crtc_state) - 1); intel_de_write(display, TRANS_VRR_VMAX(display, transcoder), @@ -663,17 +695,6 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) return; } - if (crtc_state->vrr.cmrr.enable) { - intel_de_write(display, TRANS_CMRR_M_HI(display, cpu_transcoder), - upper_32_bits(crtc_state->vrr.cmrr.cmrr_m)); - intel_de_write(display, TRANS_CMRR_M_LO(display, cpu_transcoder), - lower_32_bits(crtc_state->vrr.cmrr.cmrr_m)); - intel_de_write(display, TRANS_CMRR_N_HI(display, cpu_transcoder), - upper_32_bits(crtc_state->vrr.cmrr.cmrr_n)); - intel_de_write(display, TRANS_CMRR_N_LO(display, cpu_transcoder), - lower_32_bits(crtc_state->vrr.cmrr.cmrr_n)); - } - intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder); intel_cmtg_set_vrr_timings(crtc_state); @@ -939,8 +960,7 @@ intel_vrr_disable_dc_balancing(const struct intel_crtc_state *old_crtc_state) intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl); } -static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state, - bool cmrr_enable) +static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; @@ -952,11 +972,12 @@ static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state, vrr_ctl = VRR_CTL_VRR_ENABLE | trans_vrr_ctl(crtc_state); /* - * FIXME this might be broken as bspec seems to imply that - * even VRR_CTL_CMRR_ENABLE is armed by TRANS_CMRR_N_HI - * when enabling CMRR (but not when disabling CMRR?). + * This full TRANS_VRR_CTL write is the authoritative one, so it must + * carry VRR_CTL_CMRR_ENABLE when CMRR is in use. Writing TRANS_CMRR_N_HI + * arms the bit in hardware, but this later write would otherwise clear + * it again. */ - if (cmrr_enable) + if (crtc_state->vrr.cmrr.enable) vrr_ctl |= VRR_CTL_CMRR_ENABLE; intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl); @@ -992,7 +1013,7 @@ void intel_vrr_enable(const struct intel_crtc_state *crtc_state) intel_vrr_enable_dc_balancing(crtc_state); if (!intel_vrr_always_use_vrr_tg(display)) - intel_vrr_tg_enable(crtc_state, crtc_state->vrr.cmrr.enable); + intel_vrr_tg_enable(crtc_state); } void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) @@ -1019,7 +1040,7 @@ void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state) return; if (intel_vrr_always_use_vrr_tg(display)) - intel_vrr_tg_enable(crtc_state, false); + intel_vrr_tg_enable(crtc_state); } void intel_vrr_transcoder_disable(const struct intel_crtc_state *old_crtc_state) From 59f87450fb2d7f0a38e0f5c7ca18a91dc7b6f8d2 Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:16 +0530 Subject: [PATCH 11/72] drm/i915/vrr: Dump CMRR state in the crtc state dump Add crtc state dump for CMRR. --v2: - Remove video mode state checker. Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-10-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_crtc_state_dump.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c index 4493483f10a9..ad4f362e0c09 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c +++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c @@ -311,6 +311,9 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config, pipe_config->vrr.dc_balance.max_increase, pipe_config->vrr.dc_balance.max_decrease, pipe_config->vrr.dc_balance.vblank_target); + drm_printf(&p, "cmrr: %s, cmrr_m: %llu, cmrr_n: %llu\n", + str_yes_no(pipe_config->vrr.cmrr.enable), + pipe_config->vrr.cmrr.cmrr_m, pipe_config->vrr.cmrr.cmrr_n); drm_printf(&p, "requested mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&pipe_config->hw.mode)); From 533b8c78f10e5de213b4d85bfc365292ce767fe0 Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Thu, 30 Jul 2026 16:29:17 +0530 Subject: [PATCH 12/72] drm/i915/vrr: Enable CMRR Set crtc_state->vrr.cmrr.enable at the end of intel_vrr_cmrr_compute_config() so that CMRR is actually enabled once the M/N parameters have been computed. This is the final step that turns on CMRR, with all the parameter computation handled in the earlier patches. --v2: - Everything else except enable compute to handle before this patch (Chaitanya) --v3: - Remove redundant return from compute config --v4: - Update CMRR commit message (Chaitanya) Signed-off-by: Mitul Golani Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260730105919.3913412-11-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_vrr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 653581ed4e90..e36db1174440 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -269,6 +269,8 @@ intel_vrr_cmrr_compute_config(struct intel_crtc_state *crtc_state) adjusted_mode->crtc_vtotal = div64_u64_rem(dividend, crtc_state->vrr.cmrr.cmrr_n, &crtc_state->vrr.cmrr.cmrr_m); + + crtc_state->vrr.cmrr.enable = true; } static From 75eef2123c1d51ced45737fabec5b7e4cb65211a Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 30 Jun 2026 11:53:33 +0000 Subject: [PATCH 13/72] drm/i915/display: Split out DG2 MPLLB enable helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the DG2 MPLLB enable path by splitting out a helper that programs the PHY directly from an intel_mpllb_state. This is preparatory work for moving DG2 MPLLB handling under the DPLL framework, where callbacks operate on dpll_hw_state rather than the full crtc_state. Assisted-by: Copilot:claude-sonnet-4-6 Signed-off-by: Mika Kahola Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260630115336.1059976-2-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_snps_phy.c | 11 ++++++++--- drivers/gpu/drm/i915/display/intel_snps_phy.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_snps_phy.c b/drivers/gpu/drm/i915/display/intel_snps_phy.c index bf9df566630f..dfb3a5c35c85 100644 --- a/drivers/gpu/drm/i915/display/intel_snps_phy.c +++ b/drivers/gpu/drm/i915/display/intel_snps_phy.c @@ -1816,11 +1816,10 @@ int intel_mpllb_calc_state(struct intel_crtc_state *crtc_state, return -EINVAL; } -void intel_mpllb_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +void intel_mpllb_enable_phy(struct intel_encoder *encoder, + const struct intel_mpllb_state *pll_state) { struct intel_display *display = to_intel_display(encoder); - const struct intel_mpllb_state *pll_state = &crtc_state->dpll_hw_state.mpllb; enum phy phy = intel_encoder_to_phy(encoder); intel_reg_t enable_reg = (phy <= PHY_D ? DG2_PLL_ENABLE(phy) : MG_PLL_ENABLE(0)); @@ -1875,6 +1874,12 @@ void intel_mpllb_enable(struct intel_encoder *encoder, */ } +void intel_mpllb_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + intel_mpllb_enable_phy(encoder, &crtc_state->dpll_hw_state.mpllb); +} + void intel_mpllb_disable(struct intel_encoder *encoder) { struct intel_display *display = to_intel_display(encoder); diff --git a/drivers/gpu/drm/i915/display/intel_snps_phy.h b/drivers/gpu/drm/i915/display/intel_snps_phy.h index 7f96da22d028..2c7a5f2040f1 100644 --- a/drivers/gpu/drm/i915/display/intel_snps_phy.h +++ b/drivers/gpu/drm/i915/display/intel_snps_phy.h @@ -22,6 +22,8 @@ void intel_snps_phy_update_psr_power_state(struct intel_encoder *encoder, int intel_mpllb_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder); +void intel_mpllb_enable_phy(struct intel_encoder *encoder, + const struct intel_mpllb_state *pll_state); void intel_mpllb_enable(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_mpllb_disable(struct intel_encoder *encoder); From a97ec4236fee524461ce2bb1c5524472898c092b Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 30 Jun 2026 11:53:34 +0000 Subject: [PATCH 14/72] drm/i915/display: Add DG2 MPLLB DPLL manager support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add DG2-specific DPLL manager infrastructure for MPLLB-backed port PLLs. Introduce PLL id mapping, DG2-specific DPLL callbacks for enable/disable/readout/frequency calculation, and manager callbacks for compute/get/dump/compare. The platform is not switched over yet. DG2's PLL-to-encoder lookup prefers the active encoder using the PLL, but fall back to a matching encoder for the fixed port-to-PLL mapping when no active user can be identified. This keeps the active-user fix for operational paths while allowing readout and verification paths to resolve the PHY even when pll->active_mask does not identify an active user. v2: Use intel_encoder_is_dig_port() to filter out MST virtual encoders (Imre) Assisted-by: Copilot:claude-sonnet-4-6 Signed-off-by: Mika Kahola Reviewed-by: Michał Grzelak (v1) Reviewed-by: Vinod Govindapillai Link: https://patch.msgid.link/20260630115336.1059976-3-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 210 ++++++++++++++++++ drivers/gpu/drm/i915/display/intel_dpll_mgr.h | 22 ++ 2 files changed, 232 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 0f933101b7dd..3f9ed7f63eb2 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -42,6 +42,7 @@ #include "intel_lt_phy.h" #include "intel_mg_phy_regs.h" #include "intel_pch_refclk.h" +#include "intel_snps_phy.h" #include "intel_tc.h" /** @@ -4735,6 +4736,215 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .compare_hw_state = xe3plpd_compare_hw_state, }; +enum intel_dpll_id dg2_port_to_pll_id(enum port port) +{ + switch (port) { + case PORT_A: + return DPLL_ID_DG2_DPLL_A; + case PORT_B: + return DPLL_ID_DG2_DPLL_B; + case PORT_C: + return DPLL_ID_DG2_DPLL_C; + case PORT_D_XELPD: + return DPLL_ID_DG2_DPLL_D; + case PORT_TC1: + return DPLL_ID_DG2_DPLL_E; + default: + MISSING_CASE(port); + return DPLL_ID_DG2_DPLL_A; + } +} + +static struct intel_encoder *dg2_get_intel_encoder(struct intel_display *display, + const struct intel_dpll *pll) +{ + struct intel_encoder *encoder; + + for_each_intel_encoder(display->drm, encoder) { + if (dg2_port_to_pll_id(encoder->port) == pll->info->id && + intel_encoder_is_dig_port(encoder)) + return encoder; + } + + return NULL; +} + +static void dg2_mpllb_enable(struct intel_display *display, + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_mpllb_enable_phy(encoder, &dpll_hw_state->mpllb); +} + +static void dg2_mpllb_disable(struct intel_display *display, + struct intel_dpll *pll) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_mpllb_disable(encoder); +} + +static bool dg2_mpllb_get_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + enum phy phy; + i915_reg_t enable_reg; + struct ref_tracker *wakeref; + bool ret = false; + u32 val; + + if (!encoder) + return false; + + wakeref = intel_display_power_get_if_enabled(display, + POWER_DOMAIN_DISPLAY_CORE); + if (!wakeref) + return false; + + phy = intel_encoder_to_phy(encoder); + enable_reg = (phy <= PHY_D ? DG2_PLL_ENABLE(phy) : MG_PLL_ENABLE(0)); + + val = intel_de_read(display, enable_reg); + if (!(val & PLL_ENABLE)) + goto out; + + intel_mpllb_readout_hw_state(encoder, &dpll_hw_state->mpllb); + ret = true; + +out: + intel_display_power_put(display, POWER_DOMAIN_DISPLAY_CORE, wakeref); + return ret; +} + +static int dg2_mpllb_get_freq(struct intel_display *display, + const struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return 0; + + return intel_mpllb_calc_port_clock(encoder, &dpll_hw_state->mpllb); +} + +static const struct intel_dpll_funcs mpllb_pll_funcs = { + .enable = dg2_mpllb_enable, + .disable = dg2_mpllb_disable, + .get_hw_state = dg2_mpllb_get_hw_state, + .get_freq = dg2_mpllb_get_freq, +}; + +static const struct dpll_info dg2_plls[] = { + { .name = "MPLLB A", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_A, }, + { .name = "MPLLB B", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_B, }, + { .name = "MPLLB C", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_C, }, + { .name = "MPLLB D", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_D, }, + { .name = "MPLLB E", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_E, }, + {} +}; + +static int dg2_compute_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct icl_port_dpll *port_dpll = + &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + int ret; + + ret = intel_mpllb_calc_state(crtc_state, encoder); + if (ret) + return ret; + + port_dpll->hw_state = crtc_state->dpll_hw_state; + + /* this is mainly for the fastset check */ + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + + crtc_state->port_clock = intel_mpllb_calc_port_clock(encoder, + &port_dpll->hw_state.mpllb); + + return 0; +} + +static int dg2_get_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct icl_port_dpll *port_dpll = + &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + enum intel_dpll_id dpll_id = dg2_port_to_pll_id(encoder->port); + + port_dpll->pll = intel_find_dpll(state, crtc, + &port_dpll->hw_state, + BIT(dpll_id)); + if (!port_dpll->pll) + return -EINVAL; + + intel_reference_dpll(state, crtc, + port_dpll->pll, &port_dpll->hw_state); + + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + + return 0; +} + +static void dg2_dump_hw_state(struct drm_printer *p, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + const struct intel_mpllb_state *hw_state = &dpll_hw_state->mpllb; + + drm_printf(p, "dpll_hw_state: mpllb_cp: 0x%x, mpllb_div: 0x%x, " + "mpllb_div2: 0x%x, mpllb_fracn1: 0x%x, " + "mpllb_fracn2: 0x%x, mpllb_sscen: 0x%x, " + "mpllb_sscstep: 0x%x, ref_control: 0x%x\n", + hw_state->mpllb_cp, hw_state->mpllb_div, + hw_state->mpllb_div2, hw_state->mpllb_fracn1, + hw_state->mpllb_fracn2, hw_state->mpllb_sscen, + hw_state->mpllb_sscstep, hw_state->ref_control); +} + +static bool dg2_compare_hw_state(const struct intel_dpll_hw_state *_a, + const struct intel_dpll_hw_state *_b) +{ + const struct intel_mpllb_state *a = &_a->mpllb; + const struct intel_mpllb_state *b = &_b->mpllb; + + return a->mpllb_cp == b->mpllb_cp && + a->mpllb_div == b->mpllb_div && + a->mpllb_div2 == b->mpllb_div2 && + a->mpllb_fracn1 == b->mpllb_fracn1 && + a->mpllb_fracn2 == b->mpllb_fracn2 && + a->mpllb_sscen == b->mpllb_sscen && + a->mpllb_sscstep == b->mpllb_sscstep; +} + +__maybe_unused +static const struct intel_dpll_mgr dg2_pll_mgr = { + .dpll_info = dg2_plls, + .compute_dplls = dg2_compute_dplls, + .get_dplls = dg2_get_dplls, + .put_dplls = icl_put_dplls, + .update_active_dpll = icl_update_active_dpll, + .update_ref_clks = icl_update_dpll_ref_clks, + .dump_hw_state = dg2_dump_hw_state, + .compare_hw_state = dg2_compare_hw_state, +}; + /** * intel_dpll_init - Initialize DPLLs * @display: intel_display device diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index d408ccf6f902..f4b84733a58c 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -169,9 +169,31 @@ enum intel_dpll_id { * @DPLL_ID_DG1_DPLL3: DG1 combo PHY DPLL3 */ DPLL_ID_DG1_DPLL3 = 3, + + /** + * @DPLL_ID_DG2_DPLL_A: DG2 port PLL for PHY A (PORT_A) + */ + DPLL_ID_DG2_DPLL_A = 0, + /** + * @DPLL_ID_DG2_DPLL_B: DG2 port PLL for PHY B (PORT_B) + */ + DPLL_ID_DG2_DPLL_B = 1, + /** + * @DPLL_ID_DG2_DPLL_C: DG2 port PLL for PHY C (PORT_C) + */ + DPLL_ID_DG2_DPLL_C = 2, + /** + * @DPLL_ID_DG2_DPLL_D: DG2 port PLL for PHY D (PORT_D_XELPD) + */ + DPLL_ID_DG2_DPLL_D = 3, + /** + * @DPLL_ID_DG2_DPLL_E: DG2 port PLL for PHY F (PORT_TC1) + */ + DPLL_ID_DG2_DPLL_E = 4, }; #define I915_NUM_PLLS 9 +enum intel_dpll_id dg2_port_to_pll_id(enum port port); enum icl_port_dpll_id { ICL_PORT_DPLL_DEFAULT, From 6cff97dd5d096f0df92d7247a4fb5279252e1c63 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 30 Jun 2026 11:53:35 +0000 Subject: [PATCH 15/72] drm/i915/display: Prepare DG2 DDI and compute paths for DPLL framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare DG2 users to obtain clock state and reserve PLLs through the shared DPLL framework. Replace the DG2-specific compute hook with the generic framework-backed helpers. Assisted-by: Copilot:claude-sonnet-4-6 Signed-off-by: Mika Kahola Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260630115336.1059976-4-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 30 ++++++++++++++++++++--- drivers/gpu/drm/i915/display/intel_dpll.c | 22 ++--------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index b8985e1e0a81..922b91157ecc 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4314,11 +4314,33 @@ static void mtl_ddi_tc_phy_get_config(struct intel_encoder *encoder, mtl_port_to_pll_id(display, encoder->port)); } -static void dg2_ddi_get_config(struct intel_encoder *encoder, - struct intel_crtc_state *crtc_state) +static struct intel_dpll *dg2_ddi_get_pll(struct intel_encoder *encoder) { - intel_mpllb_readout_hw_state(encoder, &crtc_state->dpll_hw_state.mpllb); - crtc_state->port_clock = intel_mpllb_calc_port_clock(encoder, &crtc_state->dpll_hw_state.mpllb); + struct intel_display *display = to_intel_display(encoder); + + return intel_get_dpll_by_id(display, dg2_port_to_pll_id(encoder->port)); +} + +static void dg2_ddi_get_config(struct intel_encoder *encoder, + struct intel_crtc_state *crtc_state) +{ + struct icl_port_dpll *port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + struct intel_dpll *pll = dg2_ddi_get_pll(encoder); + + if (pll) + intel_ddi_get_clock(encoder, crtc_state, pll); + + /* + * Keep the hw readout robust against unexpected NULL PLL lookups, + * so modeset verify always has intel_dpll populated for DG2. + */ + if (!crtc_state->intel_dpll) { + port_dpll->pll = pll; + intel_mpllb_readout_hw_state(encoder, &port_dpll->hw_state.mpllb); + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + crtc_state->port_clock = intel_mpllb_calc_port_clock(encoder, + &port_dpll->hw_state.mpllb); + } intel_ddi_get_config(encoder, crtc_state); } diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index f40807a5566b..20fd091cb9db 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -22,7 +22,6 @@ #include "intel_lvds_regs.h" #include "intel_panel.h" #include "intel_pps.h" -#include "intel_snps_phy.h" #include "vlv_dpio_phy_regs.h" #include "vlv_sideband.h" @@ -1194,24 +1193,6 @@ static int hsw_crtc_get_dpll(struct intel_atomic_state *state, return intel_dpll_reserve(state, crtc, encoder); } -static int dg2_crtc_compute_clock(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct intel_crtc_state *crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_encoder *encoder = - intel_get_crtc_new_encoder(state, crtc_state); - int ret; - - ret = intel_mpllb_calc_state(crtc_state, encoder); - if (ret) - return ret; - - crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state); - - return 0; -} - static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); @@ -1682,7 +1663,8 @@ static const struct intel_dpll_global_funcs mtl_dpll_funcs = { }; static const struct intel_dpll_global_funcs dg2_dpll_funcs = { - .crtc_compute_clock = dg2_crtc_compute_clock, + .crtc_compute_clock = hsw_crtc_compute_clock, + .crtc_get_dpll = hsw_crtc_get_dpll, }; static const struct intel_dpll_global_funcs hsw_dpll_funcs = { From c39a65033b398cf244546f084a505d9864d5ee19 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 30 Jun 2026 11:53:36 +0000 Subject: [PATCH 16/72] drm/i915/display: Switch DG2 to use DPLL framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that DG2 has a DPLL manager and its users are prepared to obtain clock state through the framework, switch the platform over to use the shared DPLL manager. Remove specific MPLLB state verification and use .compare_hw function hook to compare HW and SW PLL states. Assisted-by: Copilot:claude-sonnet-4-6 Signed-off-by: Mika Kahola Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260630115336.1059976-5-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 - drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 +++-- .../drm/i915/display/intel_modeset_verify.c | 1 - drivers/gpu/drm/i915/display/intel_snps_phy.c | 49 ------------------- 4 files changed, 8 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 922b91157ecc..bf5788c5bbd3 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -5354,8 +5354,6 @@ void intel_ddi_init(struct intel_display *display, else encoder->get_config = mtl_ddi_non_tc_phy_get_config; } else if (display->platform.dg2) { - encoder->enable_clock = intel_mpllb_enable; - encoder->disable_clock = intel_mpllb_disable; encoder->get_config = dg2_ddi_get_config; } else if (display->platform.alderlake_s) { encoder->enable_clock = adls_ddi_enable_clock; diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 3f9ed7f63eb2..bfd6f0a19725 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4933,7 +4933,6 @@ static bool dg2_compare_hw_state(const struct intel_dpll_hw_state *_a, a->mpllb_sscstep == b->mpllb_sscstep; } -__maybe_unused static const struct intel_dpll_mgr dg2_pll_mgr = { .dpll_info = dg2_plls, .compute_dplls = dg2_compute_dplls, @@ -4960,8 +4959,7 @@ void intel_dpll_init(struct intel_display *display) mutex_init(&display->dpll.lock); if (display->platform.dg2) - /* No shared DPLLs on DG2; port PLLs are part of the PHY */ - dpll_mgr = NULL; + dpll_mgr = &dg2_pll_mgr; else if (DISPLAY_VER(display) >= 35) dpll_mgr = &xe3plpd_pll_mgr; else if (DISPLAY_VER(display) >= 14) @@ -5330,8 +5328,13 @@ verify_single_dpll_state(struct intel_display *display, if (pll->on) { const struct intel_dpll_mgr *dpll_mgr = display->dpll.mgr; - - if (HAS_LT_PHY(display)) + /* + * Avoid direct struct comparison here. Some hw state fields, such + * as DG2 MPLLB ref_control or LT PHY config[1], are written by + * firmware and may differ from the software state without indicating + * a real mismatch. + */ + if (HAS_LT_PHY(display) || display->platform.dg2) pll_mismatch = !dpll_mgr->compare_hw_state(&pll->state.hw_state, &dpll_hw_state); else diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c index 2ec17c2bfe0f..f0761d8a7437 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c @@ -245,7 +245,6 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state, verify_connector_state(state, crtc); verify_crtc_state(state, crtc); intel_dpll_state_verify(state, crtc); - intel_mpllb_state_verify(state, crtc); } void intel_modeset_verify_disabled(struct intel_atomic_state *state) diff --git a/drivers/gpu/drm/i915/display/intel_snps_phy.c b/drivers/gpu/drm/i915/display/intel_snps_phy.c index dfb3a5c35c85..ce94ac689064 100644 --- a/drivers/gpu/drm/i915/display/intel_snps_phy.c +++ b/drivers/gpu/drm/i915/display/intel_snps_phy.c @@ -1982,52 +1982,3 @@ void intel_mpllb_readout_hw_state(struct intel_encoder *encoder, */ pll_state->mpllb_div &= ~SNPS_PHY_MPLLB_FORCE_EN; } - -void intel_mpllb_state_verify(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct intel_display *display = to_intel_display(state); - const struct intel_crtc_state *new_crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_mpllb_state mpllb_hw_state = {}; - const struct intel_mpllb_state *mpllb_sw_state = &new_crtc_state->dpll_hw_state.mpllb; - struct intel_encoder *encoder; - - if (!display->platform.dg2) - return; - - if (!new_crtc_state->hw.active) - return; - - /* intel_get_crtc_new_encoder() only works for modeset/fastset commits */ - if (!intel_crtc_needs_modeset(new_crtc_state) && - !intel_crtc_needs_fastset(new_crtc_state)) - return; - - encoder = intel_get_crtc_new_encoder(state, new_crtc_state); - intel_mpllb_readout_hw_state(encoder, &mpllb_hw_state); - -#define MPLLB_CHECK(__name) \ - INTEL_DISPLAY_STATE_WARN(display, mpllb_sw_state->__name != mpllb_hw_state.__name, \ - "[CRTC:%d:%s] mismatch in MPLLB: %s (expected 0x%08x, found 0x%08x)", \ - crtc->base.base.id, crtc->base.name, \ - __stringify(__name), \ - mpllb_sw_state->__name, mpllb_hw_state.__name) - - MPLLB_CHECK(mpllb_cp); - MPLLB_CHECK(mpllb_div); - MPLLB_CHECK(mpllb_div2); - MPLLB_CHECK(mpllb_fracn1); - MPLLB_CHECK(mpllb_fracn2); - MPLLB_CHECK(mpllb_sscen); - MPLLB_CHECK(mpllb_sscstep); - - /* - * ref_control is handled by the hardware/firemware and never - * programmed by the software, but the proper values are supplied - * in the bspec for verification purposes. - */ - MPLLB_CHECK(ref_control); - -#undef MPLLB_CHECK -} From 1572e2c5ef0a1a21487c34626b0f695537d818ce Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 28 Jul 2026 20:00:29 +0300 Subject: [PATCH 17/72] drm/{i915,xe}/display: move transient data flush call to display parent interface Move the core driver TDF call to display parent interface to avoid direct calls from display to xe driver. Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260728170029.608289-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display.c | 4 +-- .../gpu/drm/i915/display/intel_frontbuffer.c | 3 +-- drivers/gpu/drm/i915/display/intel_parent.c | 6 +++++ drivers/gpu/drm/i915/display/intel_parent.h | 1 + drivers/gpu/drm/i915/display/intel_tdf.h | 25 ------------------- drivers/gpu/drm/xe/Makefile | 3 +-- drivers/gpu/drm/xe/display/xe_display.c | 15 +++++++++++ drivers/gpu/drm/xe/display/xe_tdf.c | 15 ----------- include/drm/intel/display_parent_interface.h | 3 +++ 9 files changed, 29 insertions(+), 46 deletions(-) delete mode 100644 drivers/gpu/drm/i915/display/intel_tdf.h delete mode 100644 drivers/gpu/drm/xe/display/xe_tdf.c diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 8ce762188665..829d7a411dcc 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -107,6 +107,7 @@ #include "intel_modeset_verify.h" #include "intel_overlay.h" #include "intel_panel.h" +#include "intel_parent.h" #include "intel_pch_display.h" #include "intel_pch_refclk.h" #include "intel_pfit.h" @@ -119,7 +120,6 @@ #include "intel_sdvo.h" #include "intel_snps_phy.h" #include "intel_tc.h" -#include "intel_tdf.h" #include "intel_tv.h" #include "intel_vblank.h" #include "intel_vdsc.h" @@ -7506,7 +7506,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) intel_atomic_commit_fence_wait(state); - intel_td_flush(display); + intel_parent_transient_data_flush(display); intel_atomic_prepare_plane_clear_colors(state); diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c index 705742e117ca..70aae6ffa3fe 100644 --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -66,7 +66,6 @@ #include "intel_frontbuffer.h" #include "intel_parent.h" #include "intel_psr.h" -#include "intel_tdf.h" /** * frontbuffer_flush - flush frontbuffer @@ -95,7 +94,7 @@ static void frontbuffer_flush(struct intel_display *display, trace_intel_frontbuffer_flush(display, frontbuffer_bits, origin); might_sleep(); - intel_td_flush(display); + intel_parent_transient_data_flush(display); intel_drrs_flush(display, frontbuffer_bits); intel_psr_flush(display, frontbuffer_bits, origin); intel_fbc_flush(display, frontbuffer_bits, origin); diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index a5e41ea66921..ffc1f5e0518c 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -451,6 +451,12 @@ bool intel_parent_has_fenced_regions(struct intel_display *display) return display->parent->has_fenced_regions && display->parent->has_fenced_regions(display->drm); } +void intel_parent_transient_data_flush(struct intel_display *display) +{ + if (display->parent->transient_data_flush) + display->parent->transient_data_flush(display->drm); +} + bool intel_parent_vgpu_active(struct intel_display *display) { return display->parent->vgpu_active && display->parent->vgpu_active(display->drm); diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 595d4148b8eb..c817a826169c 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -155,6 +155,7 @@ int intel_parent_vlv_iosf_write(struct intel_display *display, enum vlv_iosf_sb_ /* generic */ bool intel_parent_has_auxccs(struct intel_display *display); bool intel_parent_has_fenced_regions(struct intel_display *display); +void intel_parent_transient_data_flush(struct intel_display *display); bool intel_parent_vgpu_active(struct intel_display *display); void intel_parent_fence_priority_display(struct intel_display *display, struct dma_fence *fence); diff --git a/drivers/gpu/drm/i915/display/intel_tdf.h b/drivers/gpu/drm/i915/display/intel_tdf.h deleted file mode 100644 index 0862c2bfd9cd..000000000000 --- a/drivers/gpu/drm/i915/display/intel_tdf.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2024 Intel Corporation - */ - -#ifndef __INTEL_TDF_H__ -#define __INTEL_TDF_H__ - -/* - * TDF (Transient-Data-Flush) is needed for Xe2+ where special L3:XD caching can - * be enabled through various PAT index modes. Idea is to use this caching mode - * when for example rendering onto the display surface, with the promise that - * KMD will ensure transient cache entries are always flushed by the time we do - * the display flip, since display engine is never coherent with CPU/GPU caches. - */ - -struct intel_display; - -#ifdef I915 -static inline void intel_td_flush(struct intel_display *display) {} -#else -void intel_td_flush(struct intel_display *display); -#endif - -#endif diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 3ff58186c767..5a6b4f494d67 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -227,8 +227,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ display/xe_hdcp_gsc.o \ display/xe_initial_plane.o \ display/xe_panic.o \ - display/xe_stolen.o \ - display/xe_tdf.o + display/xe_stolen.o # Display code shared with i915 xe-$(CONFIG_DRM_XE_DISPLAY) += \ diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 8da4c457a4a0..5f5e79505b3d 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -446,6 +446,20 @@ static bool has_auxccs(struct drm_device *drm) return xe->info.platform == XE_ALDERLAKE_P; } +/* + * TDF (Transient-Data-Flush) is needed for Xe2+ where special L3:XD caching can + * be enabled through various PAT index modes. Idea is to use this caching mode + * when for example rendering onto the display surface, with the promise that + * KMD will ensure transient cache entries are always flushed by the time we do + * the display flip, since display engine is never coherent with CPU/GPU caches. + */ +static void transient_data_flush(struct drm_device *drm) +{ + struct xe_device *xe = to_xe_device(drm); + + xe_device_td_flush(xe); +} + static const struct intel_display_parent_interface parent = { .bo = &xe_display_bo_interface, .dsb = &xe_display_dsb_interface, @@ -459,6 +473,7 @@ static const struct intel_display_parent_interface parent = { .rpm = &xe_display_rpm_interface, .stolen = &xe_display_stolen_interface, .has_auxccs = has_auxccs, + .transient_data_flush = transient_data_flush, }; /** diff --git a/drivers/gpu/drm/xe/display/xe_tdf.c b/drivers/gpu/drm/xe/display/xe_tdf.c deleted file mode 100644 index 78bda4c47874..000000000000 --- a/drivers/gpu/drm/xe/display/xe_tdf.c +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * Copyright © 2024 Intel Corporation - */ - -#include "intel_display_core.h" -#include "intel_tdf.h" -#include "xe_device.h" - -void intel_td_flush(struct intel_display *display) -{ - struct xe_device *xe = to_xe_device(display->drm); - - xe_device_td_flush(xe); -} diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index de395df9ca30..8b25107615ea 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -302,6 +302,9 @@ struct intel_display_parent_interface { /** @has_fenced_regions: Support legacy fencing? Optional. */ bool (*has_fenced_regions)(struct drm_device *drm); + /** @transient_data_flush: Transient data flush. Optional. */ + void (*transient_data_flush)(struct drm_device *drm); + /** @vgpu_active: Is vGPU active? Optional. */ bool (*vgpu_active)(struct drm_device *drm); }; From 71e9afe275b0e496b9377e572d3f24b531c3c4d8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 28 Jul 2026 17:50:19 +0300 Subject: [PATCH 18/72] drm/xe/display: drop redundant intel_hpd_poll_fini() call intel_hpd_poll_fini() is already being called via xe_display_fini_early() -> intel_display_driver_remove_noirq(). Remove the redundant call. Reviewed-by: Uma Shankar Link: https://patch.msgid.link/6c3a537d69af2bca21e9f04cd678aaf9d72f7c8e.1785250188.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 5f5e79505b3d..4ea1d37d7730 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -129,7 +129,6 @@ static void xe_display_fini(void *arg) struct xe_device *xe = arg; struct intel_display *display = xe->display; - intel_hpd_poll_fini(display); intel_hdcp_component_fini(display); intel_audio_deinit(display); intel_display_driver_remove(display); From 1f4555296f122e5223a78435c7db57bae4ee731d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 28 Jul 2026 17:50:20 +0300 Subject: [PATCH 19/72] drm/xe/display: drop redundant intel_hdcp_component_fini() call intel_hdcp_component_fini() is already being called via xe_display_fini_early() -> intel_display_driver_remove_noirq(). Remove the redundant call. Reviewed-by: Uma Shankar Link: https://patch.msgid.link/08355094a47cbf63f2092dae000780567636c967.1785250188.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 4ea1d37d7730..aeba0e919f50 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -29,7 +29,6 @@ #include "intel_dmc_wl.h" #include "intel_dp.h" #include "intel_fbdev.h" -#include "intel_hdcp.h" #include "intel_hotplug.h" #include "intel_opregion.h" #include "skl_watermark.h" @@ -129,7 +128,6 @@ static void xe_display_fini(void *arg) struct xe_device *xe = arg; struct intel_display *display = xe->display; - intel_hdcp_component_fini(display); intel_audio_deinit(display); intel_display_driver_remove(display); } From a2db38e7db00d515c424157f7bc4b129381cacaa Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 28 Jul 2026 17:50:21 +0300 Subject: [PATCH 20/72] drm/xe/display: drop redundant intel_audio_deinit() call intel_audio_deinit() is already being called via xe_display_unregister() -> intel_display_driver_unregister(). Remove the redundant call. Reviewed-by: Uma Shankar Link: https://patch.msgid.link/b2b376f5d6f7ecb48bf9028ab5d0b9c95dbe9859.1785250188.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index aeba0e919f50..5ccf7959e3dd 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -18,7 +18,6 @@ #include #include "intel_acpi.h" -#include "intel_audio.h" #include "intel_display.h" #include "intel_display_core.h" #include "intel_display_device.h" @@ -128,7 +127,6 @@ static void xe_display_fini(void *arg) struct xe_device *xe = arg; struct intel_display *display = xe->display; - intel_audio_deinit(display); intel_display_driver_remove(display); } From 9d87cadf676223f3d8c440a61a072e55c416e30d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 3 Aug 2026 14:54:17 +0300 Subject: [PATCH 21/72] drm/{i915,xe}/display: add core workaround query to display parent interface There are workarounds display needs to apply depending on information only available to the core driver. Add a display parent interface for the query. For starters, there's only one workaround like this, so keep it simple instead of over-engineering. This lets us drop an #ifdef I915 as well as xe dependency on some display headers. v2: git add xe_display_wa.h v3: Add a comments about usage (Suraj) Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260803115417.1760484-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_wa.c | 4 +++- drivers/gpu/drm/i915/display/intel_display_wa.h | 9 --------- drivers/gpu/drm/xe/display/xe_display.c | 2 ++ drivers/gpu/drm/xe/display/xe_display_wa.c | 13 +++++++++---- drivers/gpu/drm/xe/display/xe_display_wa.h | 9 +++++++++ include/drm/intel/display_parent_interface.h | 10 ++++++++++ 6 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 drivers/gpu/drm/xe/display/xe_display_wa.h diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.c b/drivers/gpu/drm/i915/display/intel_display_wa.c index 3662e0f17c69..888676693f5e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_wa.c +++ b/drivers/gpu/drm/i915/display/intel_display_wa.c @@ -4,6 +4,7 @@ */ #include +#include #include #include "intel_de.h" @@ -131,7 +132,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa, case INTEL_DISPLAY_WA_16011863758: return DISPLAY_VER(display) >= 11; case INTEL_DISPLAY_WA_16023588340: - return intel_display_needs_wa_16023588340(display); + /* This is a GT workaround. */ + return display->parent->wa && display->parent->wa->wa_16023588340(display->drm); case INTEL_DISPLAY_WA_16025573575: return intel_display_needs_wa_16025573575(display); case INTEL_DISPLAY_WA_16025596647: diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.h b/drivers/gpu/drm/i915/display/intel_display_wa.h index 338b32e4162d..425a300840aa 100644 --- a/drivers/gpu/drm/i915/display/intel_display_wa.h +++ b/drivers/gpu/drm/i915/display/intel_display_wa.h @@ -12,15 +12,6 @@ struct intel_display; void intel_display_wa_apply(struct intel_display *display); -#ifdef I915 -static inline bool intel_display_needs_wa_16023588340(struct intel_display *display) -{ - return false; -} -#else -bool intel_display_needs_wa_16023588340(struct intel_display *display); -#endif - /* * This enum lists display workarounds; each entry here must have a * corresponding case in __intel_display_wa(). Keep both sorted by lineage diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 5ccf7959e3dd..7b25c0814674 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -35,6 +35,7 @@ #include "xe_display_bo.h" #include "xe_display_pcode.h" #include "xe_display_rpm.h" +#include "xe_display_wa.h" #include "xe_dsb_buffer.h" #include "xe_fb_pin.h" #include "xe_frontbuffer.h" @@ -467,6 +468,7 @@ static const struct intel_display_parent_interface parent = { .pcode = &xe_display_pcode_interface, .rpm = &xe_display_rpm_interface, .stolen = &xe_display_stolen_interface, + .wa = &xe_display_wa_interface, .has_auxccs = has_auxccs, .transient_data_flush = transient_data_flush, }; diff --git a/drivers/gpu/drm/xe/display/xe_display_wa.c b/drivers/gpu/drm/xe/display/xe_display_wa.c index 2aa1b8c03411..46e41e8a3304 100644 --- a/drivers/gpu/drm/xe/display/xe_display_wa.c +++ b/drivers/gpu/drm/xe/display/xe_display_wa.c @@ -3,17 +3,22 @@ * Copyright © 2024 Intel Corporation */ -#include "intel_display_core.h" -#include "intel_display_wa.h" +#include + #include "xe_device.h" +#include "xe_display_wa.h" #include "xe_wa.h" #include -bool intel_display_needs_wa_16023588340(struct intel_display *display) +static bool intel_display_needs_wa_16023588340(struct drm_device *drm) { - struct xe_device *xe = to_xe_device(display->drm); + struct xe_device *xe = to_xe_device(drm); struct xe_gt *wa_gt = xe_root_mmio_gt(xe); return wa_gt && XE_GT_WA(wa_gt, 16023588340); } + +const struct intel_display_wa_interface xe_display_wa_interface = { + .wa_16023588340 = intel_display_needs_wa_16023588340, +}; diff --git a/drivers/gpu/drm/xe/display/xe_display_wa.h b/drivers/gpu/drm/xe/display/xe_display_wa.h new file mode 100644 index 000000000000..0262b463916d --- /dev/null +++ b/drivers/gpu/drm/xe/display/xe_display_wa.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __XE_DISPLAY_WA_H__ +#define __XE_DISPLAY_WA_H__ + +extern const struct intel_display_wa_interface xe_display_wa_interface; + +#endif diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index 8b25107615ea..d7d06dcaa699 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -230,6 +230,10 @@ struct intel_display_vlv_iosf_interface { int (*write)(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr, u32 val); }; +struct intel_display_wa_interface { + bool (*wa_16023588340)(struct drm_device *drm); +}; + /** * struct intel_display_parent_interface - services parent driver provides to display * @@ -291,6 +295,12 @@ struct intel_display_parent_interface { /** @vlv_iosf: VLV IOSF sideband. Optional. */ const struct intel_display_vlv_iosf_interface *vlv_iosf; + /** + * @wa: Display workarounds query. Use only for workarounds that require + * information only available to the parent driver. Optional. + */ + const struct intel_display_wa_interface *wa; + /* Generic independent functions */ struct { /** @fence_priority_display: Set display priority. Optional. */ From 54ce53fd24a99f000666694f99967b47c2ca6c00 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Wed, 5 Aug 2026 09:20:23 +0530 Subject: [PATCH 22/72] drm/i915/hdmi: Use correct type for output_format parameter Correct the data-type of the argument output_format to enum, for the function intel_hdmi_dsc_get_bpp(). Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260805035027.3473398-2-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_hdmi.c | 4 ++-- drivers/gpu/drm/i915/display/intel_hdmi.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 8a019d3574df..9af0f3aba0fc 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -3338,8 +3338,8 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, */ int intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, - int output_format, bool hdmi_all_bpp, - int hdmi_max_chunk_bytes) + enum intel_output_format output_format, + bool hdmi_all_bpp, int hdmi_max_chunk_bytes) { int max_dsc_bpp, min_dsc_bpp; int target_bytes; diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index 0fa3661568e8..fb950fdd0e7a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -54,8 +54,8 @@ bool intel_hdmi_bpc_possible(const struct intel_crtc_state *crtc_state, int bpc, bool has_hdmi_sink); int intel_hdmi_tmds_clock(int clock, int bpc, enum intel_output_format sink_format); int intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, - int num_slices, int output_format, bool hdmi_all_bpp, - int hdmi_max_chunk_bytes); + int num_slices, enum intel_output_format output_format, + bool hdmi_all_bpp, int hdmi_max_chunk_bytes); int intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, int src_max_slices, int src_max_slice_width, int hdmi_max_slices, int hdmi_throughput); From bbae35c9f58460056d7063180bd2809ed3813166 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Wed, 5 Aug 2026 09:20:24 +0530 Subject: [PATCH 23/72] drm/i915/display: Generalize intel_hdmi_dsc_get_num_slices helper Refactor intel_hdmi_dsc_get_num_slices to remove dependency on intel_crtc_state structure by accepting display mode and output format as parameters instead. This makes the function reusable for mode_valid cases where crtc_state is not present. Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260805035027.3473398-3-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 4 +++- drivers/gpu/drm/i915/display/intel_hdmi.c | 14 ++++++++------ drivers/gpu/drm/i915/display/intel_hdmi.h | 4 +++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 0e2003f3d6cb..a6001ebffcbe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4282,7 +4282,9 @@ intel_dp_pcon_dsc_enc_slices(struct intel_dp *intel_dp, int pcon_max_slices = drm_dp_pcon_dsc_max_slices(intel_dp->pcon_dsc_dpcd); int pcon_max_slice_width = drm_dp_pcon_dsc_max_slice_width(intel_dp->pcon_dsc_dpcd); - return intel_hdmi_dsc_get_num_slices(crtc_state, pcon_max_slices, + return intel_hdmi_dsc_get_num_slices(&crtc_state->hw.adjusted_mode, + crtc_state->output_format, + pcon_max_slices, pcon_max_slice_width, hdmi_max_slices, hdmi_throughput); } diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 9af0f3aba0fc..228c5e2be164 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -3216,7 +3216,8 @@ int intel_hdmi_dsc_get_slice_height(int vactive) * intel_hdmi_dsc_get_num_slices - get no. of dsc slices based on dsc encoder * and dsc decoder capabilities * - * @crtc_state: intel crtc_state + * @mode: drm_display_mode for which num of slices are needed + * @output_format : pipe output format * @src_max_slices: maximum slices supported by the DSC encoder * @src_max_slice_width: maximum slice width supported by DSC encoder * @hdmi_max_slices: maximum slices supported by sink DSC decoder @@ -3226,7 +3227,8 @@ int intel_hdmi_dsc_get_slice_height(int vactive) * and decoder. */ int -intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, +intel_hdmi_dsc_get_num_slices(const struct drm_display_mode *mode, + enum intel_output_format output_format, int src_max_slices, int src_max_slice_width, int hdmi_max_slices, int hdmi_throughput) { @@ -3248,7 +3250,7 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, int max_throughput; /* max clock freq. in khz per slice */ int max_slice_width; int slice_width; - int pixel_clock = crtc_state->hw.adjusted_mode.crtc_clock; + int pixel_clock = mode->crtc_clock; if (!hdmi_throughput) return 0; @@ -3259,8 +3261,8 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, * for 4:4:4 is 1.0. Multiplying these factors by 10 and later * dividing adjusted clock value by 10. */ - if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 || - crtc_state->output_format == INTEL_OUTPUT_FORMAT_RGB) + if (output_format == INTEL_OUTPUT_FORMAT_YCBCR444 || + output_format == INTEL_OUTPUT_FORMAT_RGB) kslice_adjust = 10; else kslice_adjust = 5; @@ -3315,7 +3317,7 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, else return 0; - slice_width = DIV_ROUND_UP(crtc_state->hw.adjusted_mode.hdisplay, target_slices); + slice_width = DIV_ROUND_UP(mode->hdisplay, target_slices); if (slice_width >= max_slice_width) min_slices = target_slices + 1; } while (slice_width >= max_slice_width); diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index fb950fdd0e7a..4e2228ca0c5b 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -13,6 +13,7 @@ enum intel_output_format; enum port; struct drm_connector; struct drm_connector_state; +struct drm_display_mode; struct drm_encoder; struct intel_connector; struct intel_crtc_state; @@ -56,7 +57,8 @@ int intel_hdmi_tmds_clock(int clock, int bpc, enum intel_output_format sink_form int intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, enum intel_output_format output_format, bool hdmi_all_bpp, int hdmi_max_chunk_bytes); -int intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, +int intel_hdmi_dsc_get_num_slices(const struct drm_display_mode *mode, + enum intel_output_format output_format, int src_max_slices, int src_max_slice_width, int hdmi_max_slices, int hdmi_throughput); int intel_hdmi_dsc_get_slice_height(int vactive); From eb204603a451f17a5699c2d550625a0d99e0ad1b Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Wed, 5 Aug 2026 09:20:25 +0530 Subject: [PATCH 24/72] drm/i915/{dp,hdmi}: Pass bpc in intel_hdmi_dsc_get_bpp Generalize intel_hdmi_dsc_get_bpp helper to accept bpc as a parameter instead of assuming hardcoded value. v2: Add {dp,hdmi} in subject prefix. (Suraj) Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260805035027.3473398-4-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 3 ++- drivers/gpu/drm/i915/display/intel_hdmi.c | 10 +++++----- drivers/gpu/drm/i915/display/intel_hdmi.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index a6001ebffcbe..4567afd4aea9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4301,9 +4301,10 @@ intel_dp_pcon_dsc_enc_bpp(struct intel_dp *intel_dp, int pcon_fractional_bpp = drm_dp_pcon_dsc_bpp_incr(intel_dp->pcon_dsc_dpcd); int hdmi_max_chunk_bytes = info->hdmi.dsc_cap.total_chunk_kbytes * 1024; + int bpc = crtc_state->pipe_bpp / 3; return intel_hdmi_dsc_get_bpp(pcon_fractional_bpp, slice_width, - num_slices, output_format, hdmi_all_bpp, + num_slices, output_format, bpc, hdmi_all_bpp, hdmi_max_chunk_bytes); } diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 228c5e2be164..d3efd9e4e86a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -3333,6 +3333,7 @@ intel_hdmi_dsc_get_num_slices(const struct drm_display_mode *mode, * @slice_width: dsc slice width supported by the source and sink * @num_slices: num of slices supported by the source and sink * @output_format: video output format + * @bpc: bits per color * @hdmi_all_bpp: sink supports decoding of 1/16th bpp setting * @hdmi_max_chunk_bytes: max bytes in a line of chunks supported by sink * @@ -3340,7 +3341,7 @@ intel_hdmi_dsc_get_num_slices(const struct drm_display_mode *mode, */ int intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, - enum intel_output_format output_format, + enum intel_output_format output_format, int bpc, bool hdmi_all_bpp, int hdmi_max_chunk_bytes) { int max_dsc_bpp, min_dsc_bpp; @@ -3358,18 +3359,17 @@ intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, * for each bpp we check if no of bytes can be supported by HDMI sink */ - /* Assuming: bpc as 8*/ if (output_format == INTEL_OUTPUT_FORMAT_YCBCR420) { min_dsc_bpp = 6; - max_dsc_bpp = 3 * 4; /* 3*bpc/2 */ + max_dsc_bpp = 3 * bpc / 2; } else if (output_format == INTEL_OUTPUT_FORMAT_YCBCR444 || output_format == INTEL_OUTPUT_FORMAT_RGB) { min_dsc_bpp = 8; - max_dsc_bpp = 3 * 8; /* 3*bpc */ + max_dsc_bpp = 3 * bpc; } else { /* Assuming 4:2:2 encoding */ min_dsc_bpp = 7; - max_dsc_bpp = 2 * 8; /* 2*bpc */ + max_dsc_bpp = 2 * bpc; } /* diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index 4e2228ca0c5b..c3a0e81615e4 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -56,7 +56,7 @@ bool intel_hdmi_bpc_possible(const struct intel_crtc_state *crtc_state, int intel_hdmi_tmds_clock(int clock, int bpc, enum intel_output_format sink_format); int intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, enum intel_output_format output_format, - bool hdmi_all_bpp, int hdmi_max_chunk_bytes); + int bpc, bool hdmi_all_bpp, int hdmi_max_chunk_bytes); int intel_hdmi_dsc_get_num_slices(const struct drm_display_mode *mode, enum intel_output_format output_format, int src_max_slices, int src_max_slice_width, From 3b04e8241b223e7cb70efe87c8297f8b8ff187e2 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Wed, 5 Aug 2026 09:20:26 +0530 Subject: [PATCH 25/72] drm/i915/hdmi: Add helper to get max FRL rate for an HDMI sink Add the helpers for getting the max FRL rate with and without DSC for an HDMI sink. Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260805035027.3473398-5-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_hdmi.c | 19 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_hdmi.h | 3 +++ 2 files changed, 22 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index d3efd9e4e86a..9b637e38a1a5 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -3422,3 +3422,22 @@ intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, return 0; } + +int intel_hdmi_sink_max_frl_rate(struct drm_connector *connector) +{ + int max_lanes = connector->display_info.hdmi.max_lanes; + int rate_per_lane = connector->display_info.hdmi.max_frl_rate_per_lane; + + return max_lanes * rate_per_lane; +} + +int intel_hdmi_sink_dsc_max_frl_rate(struct drm_connector *connector) +{ + int max_lanes = connector->display_info.hdmi.dsc_cap.max_lanes; + int rate_per_lane = connector->display_info.hdmi.dsc_cap.max_frl_rate_per_lane; + + if (!max_lanes || !rate_per_lane) + return 0; + + return max_lanes * rate_per_lane; +} diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index c3a0e81615e4..c95ed37bcc0c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -75,4 +75,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, struct drm_connector *_connector); +int intel_hdmi_sink_max_frl_rate(struct drm_connector *connector); +int intel_hdmi_sink_dsc_max_frl_rate(struct drm_connector *connector); + #endif /* __INTEL_HDMI_H__ */ From 63d8817cfa3bf5e7b7362711bef8af221aa18ad4 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Wed, 5 Aug 2026 09:20:27 +0530 Subject: [PATCH 26/72] drm/i915/dp: Use helpers for getting max FRL rate Use intel_hdmi_sink_max_frl_rate() and intel_hdmi_sink_dsc_max_frl_rate() helpers instead of open-coding the FRL rate calculation. Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260805035027.3473398-6-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 4567afd4aea9..45867f7eb300 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4095,14 +4095,9 @@ static int intel_dp_pcon_set_frl_mask(int max_frl) static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) { struct intel_connector *connector = intel_dp->attached_connector; - const struct drm_display_info *info = &connector->base.display_info; - int max_frl_rate; - int max_lanes, rate_per_lane; - int max_dsc_lanes, dsc_rate_per_lane; - - max_lanes = info->hdmi.max_lanes; - rate_per_lane = info->hdmi.max_frl_rate_per_lane; - max_frl_rate = max_lanes * rate_per_lane; + struct drm_connector *drm_connector = &connector->base; + int max_frl_rate = intel_hdmi_sink_max_frl_rate(drm_connector); + int dsc_max_frl_rate = intel_hdmi_sink_dsc_max_frl_rate(drm_connector); /* * The sink's DSC max FRL rate only applies to compressed video @@ -4111,12 +4106,8 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) * the regular max FRL rate is the limit. */ if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && - info->hdmi.dsc_cap.v_1p2) { - max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; - dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; - if (max_dsc_lanes && dsc_rate_per_lane) - max_frl_rate = min(max_frl_rate, max_dsc_lanes * dsc_rate_per_lane); - } + dsc_max_frl_rate) + return min(max_frl_rate, dsc_max_frl_rate); return max_frl_rate; } From ea9f3470d33602fb776ea55443467baacf66f23a Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Thu, 6 Aug 2026 13:18:19 +0530 Subject: [PATCH 27/72] drm/i915/dp_mst: Remove duplicate intel_pfit_compute_config() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mst_stream_compute_config() called intel_pfit_compute_config() twice in a row. commit 5ce9ac1531b8 ("drm/i915/mst: Call intel_pfit_compute_config() for sharpness filter") was erroneously cherry-picked to the fixes tree while commit ca97f5546f19 ("drm/i915/mst: Call intel_pfit_compute_config() for sharpness filter") was already in there. Drop the redundant duplicate call. Cc: Rodrigo Vivi Cc: Ville Syrjälä Cc: Nemesa Garg Cc: Jani Nikula Fixes: 5ce9ac1531b8 ("drm/i915/mst: Call intel_pfit_compute_config() for sharpness filter") Signed-off-by: Chaitanya Kumar Borah Reviewed-by: Nemesa Garg Link: https://patch.msgid.link/20260806074819.2631970-1-chaitanya.kumar.borah@intel.com Signed-off-by: Rodrigo Vivi [Rodrigo: adjusted commit message] --- drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 3be1643f8d03..57daed0b0b36 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -761,10 +761,6 @@ static int mst_stream_compute_config(struct intel_atomic_state *state, pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - ret = intel_pfit_compute_config(pipe_config, conn_state); - if (ret) - return ret; - ret = intel_pfit_compute_config(pipe_config, conn_state); if (ret) return ret; From 74770d5d9c99cd272722dc601f287eb51141b97a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 7 Aug 2026 15:34:04 +0300 Subject: [PATCH 28/72] drm/i915/display: Limit max joined dotclock on joiner pipes available Multiplying the max dotclock based on joiner availability on the platform alone does not take into account the actual pipes (possibly with some fused) available for joining. Limit the max joined dotclock based on the actual pipes available for joining. Add helpers for figuring out the valid primary pipes, based on having the required joiners and amount of consecutive pipes available. v2: Check for consecutive pipes available (Ankit) Cc: Ankit Nautiyal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/3c615440f018af779a3a522f14637d2993f7af03.1786105831.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display.c | 51 +++++++++++++++++++- drivers/gpu/drm/i915/display/intel_display.h | 1 + 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 829d7a411dcc..c958b2a92cc7 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -193,6 +193,53 @@ is_trans_port_sync_mode(const struct intel_crtc_state *crtc_state) is_trans_port_sync_slave(crtc_state); } +/* + * Return a bitmask of all the start indices of consecutive bitfields of size + * width in mask. + */ +static unsigned long find_consecutive_bits(unsigned long mask, int width) +{ + unsigned long bit, out_mask = 0; + + if (!width) + return 0; + + for_each_set_bit(bit, &mask, BITS_PER_TYPE(mask)) { + /* For each set bit, see if the following bits are set also */ + unsigned long bitfield = GENMASK(bit + width - 1, bit); + + if ((mask & bitfield) == bitfield) + out_mask |= BIT(bit); + } + + return out_mask; +} + +/* + * Return a bitmask of all valid joiner primary pipes for joining + * num_joined_pipes pipes. For completeness, return all valid pipes for + * num_joined_pipes == 1. + * + * Return 0 if the platform doesn't support joining for the requested number of + * pipes, or there are not enough consecutive pipes available. + */ +u8 intel_joiner_valid_primary_pipe_mask(struct intel_display *display, int num_joined_pipes) +{ + if (num_joined_pipes == 1) { + return DISPLAY_RUNTIME_INFO(display)->pipe_mask; + } else if (num_joined_pipes == 2) { + if (!HAS_UNCOMPRESSED_JOINER(display) && !HAS_BIGJOINER(display)) + return 0; + } else if (num_joined_pipes == 4) { + if (!HAS_ULTRAJOINER(display)) + return 0; + } else { + return 0; + } + + return find_consecutive_bits(DISPLAY_RUNTIME_INFO(display)->pipe_mask, num_joined_pipes); +} + static enum pipe joiner_primary_pipe(const struct intel_crtc_state *crtc_state) { return ffs(crtc_state->joiner_pipes) - 1; @@ -8113,9 +8160,9 @@ static int max_dotclock(struct intel_display *display) { int max_dotclock = display->cdclk.max_dotclk_freq; - if (HAS_ULTRAJOINER(display)) + if (intel_joiner_valid_primary_pipe_mask(display, 4)) max_dotclock *= 4; - else if (HAS_UNCOMPRESSED_JOINER(display) || HAS_BIGJOINER(display)) + else if (intel_joiner_valid_primary_pipe_mask(display, 2)) max_dotclock *= 2; return max_dotclock; diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 57ea4f2edf2a..e7ecae9d2d21 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -373,6 +373,7 @@ intel_cpu_transcoder_mode_valid(struct intel_display *display, enum phy intel_port_to_phy(struct intel_display *display, enum port port); bool is_trans_port_sync_mode(const struct intel_crtc_state *state); bool is_trans_port_sync_master(const struct intel_crtc_state *state); +u8 intel_joiner_valid_primary_pipe_mask(struct intel_display *display, int num_joined_pipes); u8 intel_crtc_joined_pipe_mask(const struct intel_crtc_state *crtc_state); bool intel_crtc_is_joiner_secondary(const struct intel_crtc_state *crtc_state); bool intel_crtc_is_joiner_primary(const struct intel_crtc_state *crtc_state); From 1dff85f86b4673f6857c209041e2357e35e3397e Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 7 Aug 2026 15:34:05 +0300 Subject: [PATCH 29/72] drm/i915/dp: Limit for_each_joiner_candidate() on joiner pipes available Make intel_dp_can_join(), and subsequently intel_dp_joiner_candidate_valid() and for_each_joiner_candidate() more strict about pipes actually available for joining by using intel_joiner_valid_primary_pipe_mask(). This rejects some invalid combos early for both mode enumeration and compute config. Cc: Ankit Nautiyal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/efc330687f8299e5725564c3ed90d5d8e6aa0eb2.1786105831.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dp.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 45867f7eb300..833375bd2c0c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1274,17 +1274,7 @@ bool intel_dp_can_join(struct intel_dp *intel_dp, if (num_joined_pipes > 1 && !intel_dp_has_joiner(intel_dp)) return false; - switch (num_joined_pipes) { - case 1: - return true; - case 2: - return HAS_BIGJOINER(display) || - HAS_UNCOMPRESSED_JOINER(display); - case 4: - return HAS_ULTRAJOINER(display); - default: - return false; - } + return intel_joiner_valid_primary_pipe_mask(display, num_joined_pipes); } bool intel_dp_dotclk_valid(struct intel_display *display, From ee77cfd52cd32c236d7bcd41464c824d456331fe Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 7 Aug 2026 15:34:06 +0300 Subject: [PATCH 30/72] drm/i915/dp: Limit compute config joining to pipes that can actually join Skip joining for pipes that can't work as joiner primary pipe for the number of pipes being joined. This limits the invalid combos early, instead of ending up with crtc_state->joiner_pipes that would fail during modeset. Before, crtc_state->joiner pipes could have ended up including pipes that do not exist on the platform. Cc: Ankit Nautiyal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/dc1c7fc45d4da7d3fd1bd8b94c30c78d69aa0742.1786105831.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dp.c | 5 +++++ drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 833375bd2c0c..1e0dfc4333ef 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2871,6 +2871,7 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, struct drm_connector_state *conn_state, bool respect_downstream_limits) { + struct intel_display *display = to_intel_display(encoder); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct intel_connector *connector = to_intel_connector(conn_state->connector); @@ -2885,6 +2886,10 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, return -EINVAL; for_each_joiner_candidate(connector, adjusted_mode, num_joined_pipes) { + /* If the pipe can't be a joiner primary, skip early. */ + if (!(intel_joiner_valid_primary_pipe_mask(display, num_joined_pipes) & BIT(crtc->pipe))) + continue; + /* * NOTE: * The crtc_state->joiner_pipes should have been set at the end diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 57daed0b0b36..6a869d0f6ffc 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -766,6 +766,10 @@ static int mst_stream_compute_config(struct intel_atomic_state *state, return ret; for_each_joiner_candidate(connector, adjusted_mode, num_joined_pipes) { + /* If the pipe can't be a joiner primary, skip early. */ + if (!(intel_joiner_valid_primary_pipe_mask(display, num_joined_pipes) & BIT(crtc->pipe))) + continue; + if (num_joined_pipes > 1) pipe_config->joiner_pipes = GENMASK(crtc->pipe + num_joined_pipes - 1, crtc->pipe); From cffd42c07319e55b9064b544a93381a423427a98 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 7 Aug 2026 15:34:07 +0300 Subject: [PATCH 31/72] drm/i915/debugfs: Reject invalid force_joined_pipes harder Reject invalid joiner forcing more thoroughly, taking into account whether bigjoiner or uncompressed joiner is available, and whether the requested number of pipes can actually be joined. Cc: Ankit Nautiyal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/df39b8f30d27a441465b4d9bd42d4db982e5db2b.1786105831.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_debugfs.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 2bbf4760dc30..3e302f23f247 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -1285,22 +1285,11 @@ static ssize_t i915_joiner_write(struct file *file, if (ret < 0) return ret; - switch (force_joined_pipes) { - case 0: - case 1: - case 2: - connector->force_joined_pipes = force_joined_pipes; - break; - case 4: - if (HAS_ULTRAJOINER(display)) { - connector->force_joined_pipes = force_joined_pipes; - break; - } - - fallthrough; - default: + if (force_joined_pipes && + !intel_joiner_valid_primary_pipe_mask(display, force_joined_pipes)) return -EINVAL; - } + + connector->force_joined_pipes = force_joined_pipes; *offp += len; From 5abc20e39dd074e8696387ca6871d6e432baf0cd Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Tue, 11 Aug 2026 23:28:42 +0530 Subject: [PATCH 32/72] drm/i915/ddi: add helper to compute DDI clock frequency Add intel_ddi_link_symbol_clock() to return the DDI clock frequency for a given port clock: DP 8b/10b : rate DP 128b/132b (UHBR) : (10 / 32) * rate HDMI FRL : (10 / 18) * rate HDMI TMDS : rate The DP case reuses intel_dp_link_symbol_clock(). This will help in upcoming commits to decide value to be written in DDI_CLK_VALFREQ. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260811175844.2613721-2-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 11 +++++++++++ drivers/gpu/drm/i915/display/intel_ddi.h | 1 + 2 files changed, 12 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index bf5788c5bbd3..dd9064592ec9 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -1529,6 +1529,17 @@ int intel_ddi_level(struct intel_encoder *encoder, return level; } +int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock) +{ + if (intel_encoder_is_dp(encoder)) + return intel_dp_link_symbol_clock(clock); + + if (intel_hdmi_is_frl(clock)) + return DIV_ROUND_CLOSEST(clock * 10, 18); + + return clock; +} + static void hsw_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h index 580ecb09b8b6..239d5a403f91 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.h +++ b/drivers/gpu/drm/i915/display/intel_ddi.h @@ -81,6 +81,7 @@ void intel_ddi_sanitize_encoder_pll_mapping(struct intel_encoder *encoder); int intel_ddi_level(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, int lane); +int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock); void intel_ddi_update_active_dpll(struct intel_atomic_state *state, struct intel_encoder *encoder, struct intel_crtc *crtc); From 9ac3ee6c0f92cd09893bd442964fb6b0d6813b5e Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Tue, 11 Aug 2026 23:28:43 +0530 Subject: [PATCH 33/72] drm/i915/cx0: program DDI_CLK_VALFREQ with DDI clock frequency DDI_CLK_VALFREQ is programmed with the port clock, which for DP is the symbol clock computed assuming 8b/10b encoding (link_rate / 10). For DP 128b/132b (UHBR) rates and for HDMI FRL the port clock needs to be modfied. DDI_CLK_VALFREQ does not have any functional impact on H/w, it only records the frequency S/w intends to set. Use intel_ddi_link_symbol_clock() to write the correct DDI clock in kHz. Fixes: 51390cc0e00a ("drm/i915/mtl: Add Support for C10 PHY message bus and pll programming") Fixes: 73fc3abcb797 ("drm/i915/mtl: Enabling/disabling sequence Thunderbolt pll") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260811175844.2613721-3-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 452062417ce9..dbebd7210848 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -3233,7 +3233,8 @@ static void intel_cx0pll_enable(struct intel_encoder *encoder, * 8. Program DDI_CLK_VALFREQ to match intended DDI * clock frequency. */ - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), + intel_ddi_link_symbol_clock(encoder, port_clock)); /* * 9. Set PORT_CLOCK_CTL register PCLK PLL Request @@ -3406,7 +3407,7 @@ void intel_mtl_tbt_pll_enable_clock(struct intel_encoder *encoder, int port_cloc * clock frequency. */ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), - port_clock); + intel_ddi_link_symbol_clock(encoder, port_clock)); } void intel_mtl_pll_enable(struct intel_encoder *encoder, From eaed815ca3483c227e4ec80b86d1b3ce5c2508be Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Tue, 11 Aug 2026 23:28:44 +0530 Subject: [PATCH 34/72] drm/i915/lt_phy: program DDI_CLK_VALFREQ with DDI clock frequency DDI_CLK_VALFREQ is programmed with the port clock, which for DP is the symbol clock computed assuming 8b/10b encoding (link_rate / 10). For DP 128b/132b (UHBR) rates and for HDMI FRL the port clock needs to be modified. DDI_CLK_VALFREQ does not have any functional impact on H/w, it only records the frequency S/w intends to set. Use intel_ddi_link_symbol_clock() to write the correct DDI clock in kHz Fixes: 5ec58d714935 ("drm/i915/lt_phy: Add .enable_clock hook on DDI") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260811175844.2613721-4-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 8fc6d230493f..86492651b01d 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1976,7 +1976,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * Change. We handle this step in bxt_set_cdclk(). */ /* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */ - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), + intel_ddi_link_symbol_clock(encoder, port_clock)); /* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port), @@ -2023,7 +2024,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, lane_phy_pulse_status, lane_phy_pulse_status); } else { - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), + intel_ddi_link_symbol_clock(encoder, port_clock)); } /* From e26c92e712724e46ab707110a1bc379980f8d573 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 9 Jun 2026 12:52:36 +0300 Subject: [PATCH 35/72] drm/i915/display: harden masks in HSW_AUD_PIN_ELD_CP_VLD macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HSW_AUD_PIN_ELD_CP_VLD has a 4-bit field per transcoder for TRANSCODER_A..TRANSCODER_D only (bits 0..15). Any other transcoder value (TRANSCODER_EDP, TRANSCODER_DSI_*, INVALID_TRANSCODER) is not valid here. This is not a problem with the current implementation, because trans is always valid when these macros are called, but it's more robust to mask the index to the low 2 bits so the shift is always well-defined. Reviewed-by: Jani Nikula Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260609095525.570614-2-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_audio_regs.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_audio_regs.h b/drivers/gpu/drm/i915/display/intel_audio_regs.h index 4c31844d21df..f3d2a99c03d0 100644 --- a/drivers/gpu/drm/i915/display/intel_audio_regs.h +++ b/drivers/gpu/drm/i915/display/intel_audio_regs.h @@ -109,12 +109,20 @@ #define _HSW_AUD_EDID_DATA_B 0x65150 #define HSW_AUD_EDID_DATA(trans) _MMIO_TRANS(trans, _HSW_AUD_EDID_DATA_A, _HSW_AUD_EDID_DATA_B) +/* + * HSW_AUD_PIN_ELD_CP_VLD has a 4-bit field per transcoder for + * TRANSCODER_A..TRANSCODER_D only (bits 0..15). Any other transcoder + * value (TRANSCODER_EDP, TRANSCODER_DSI_*, INVALID_TRANSCODER) is not + * valid here. Mask the index to the low 2 bits so the shift is + * always well-defined. + */ #define HSW_AUD_PIPE_CONV_CFG _MMIO(0x6507c) #define HSW_AUD_PIN_ELD_CP_VLD _MMIO(0x650c0) -#define AUDIO_INACTIVE(trans) ((1 << 3) << ((trans) * 4)) -#define AUDIO_OUTPUT_ENABLE(trans) ((1 << 2) << ((trans) * 4)) -#define AUDIO_CP_READY(trans) ((1 << 1) << ((trans) * 4)) -#define AUDIO_ELD_VALID(trans) ((1 << 0) << ((trans) * 4)) +#define __AUDIO_TRANS_SHIFT(trans) (((trans) & 0x3) * 4) +#define AUDIO_INACTIVE(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans) + 3) +#define AUDIO_OUTPUT_ENABLE(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans) + 2) +#define AUDIO_CP_READY(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans) + 1) +#define AUDIO_ELD_VALID(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans)) #define _AUD_TCA_DP_2DOT0_CTRL 0x650bc #define _AUD_TCB_DP_2DOT0_CTRL 0x651bc From f80c812804c3c07b891594cfc3db02326cba36aa Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 9 Jun 2026 12:52:37 +0300 Subject: [PATCH 36/72] drm/i915/display: harden shifts in ICL_DPCLKA_CFGCR0_DDI_CLK_SEL macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ICL_DPCLKA_CFGCR0 has a 2-bit DDI_CLK_SEL field per combo PHY, for PHY_A..PHY_D only. Any other phy value (PHY_NONE, TypeC/SNPS PHYs) is not valid here. This is not a problem with the current implementation, because phy is always valid when these macros are called, but it's more robust to cast to unsigned so the shift is always well-defined. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260609095525.570614-3-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- .../gpu/drm/i915/display/intel_display_regs.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 0f5018482497..f98f76b308de 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -2874,9 +2874,19 @@ enum skl_power_gate { #define ICL_DPCLKA_CFGCR0_TC_CLK_OFF(tc_port) (1 << ((tc_port) < TC_PORT_4 ? \ (tc_port) + 12 : \ (tc_port) - TC_PORT_4 + 21)) -#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) ((phy) * 2) -#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(phy) (3 << ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) -#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL(pll, phy) ((pll) << ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) + +/* + * ICL_DPCLKA_CFGCR0 has a 2-bit DDI_CLK_SEL field per combo PHY, for + * PHY_A..PHY_D only. Any other phy value (PHY_NONE, TypeC/SNPS PHYs) + * is not valid here. + */ +#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) _PICK(phy & 0x3, 0, 2, 4, 6) +#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(phy) \ + REG_GENMASK(ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) + 1, \ + ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) +#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL(pll, phy) \ + ((pll) << ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) + #define RKL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) _PICK(phy, 0, 2, 4, 27) #define RKL_DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(phy) \ (3 << RKL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) From 279b0df165e2ec4f853de03b3ee1c7961ac23513 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 9 Jun 2026 12:52:38 +0300 Subject: [PATCH 37/72] drm/i915/display: harden shift in intel_ddi_compute_config_late() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BIT() takes a non-negative shift amount, but cpu_transcoder is of type enum transcoder, which can in theory be INVALID_TRANSCODER (-1). This is not a problem with the current implementation, because cpu_transcoder is always valid when this code is reached, but it's more robust to cast to unsigned so the shift is always well-defined. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260609095525.570614-4-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_ddi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index dd9064592ec9..9b3b526e5e55 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4679,8 +4679,8 @@ static int intel_ddi_compute_config_late(struct intel_atomic_state *state, if (crtc_state->master_transcoder == crtc_state->cpu_transcoder) { crtc_state->master_transcoder = INVALID_TRANSCODER; - crtc_state->sync_mode_slaves_mask = - port_sync_transcoders & ~BIT(crtc_state->cpu_transcoder); + crtc_state->sync_mode_slaves_mask = port_sync_transcoders & + ~REG_BIT(crtc_state->cpu_transcoder); } return 0; From 5655c87693f6a6006208197a4e3cb29f1253087f Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 9 Jun 2026 12:52:39 +0300 Subject: [PATCH 38/72] drm/i915/display: remove unnecessary PHY_NONE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHY_NONE is not really used, but we define it and, thus, need to check for it in a few places we use phy. The only potential places where phy may become PHY_NONE, is in intel_port_to_phy(), where it derives from port, which can be PORT_NONE. Many of its callers don't check for PHY_NONE, which can cause unknown behavior. Additionally, this can only happen if the encoder used has PORT_NONE, which should not be the case either, without unexpected consequences. Remove the PHY_NONE definition entirely and add a couple of WARNs at the relevant places, just to be sure. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260609095525.570614-5-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_display.c | 10 ++++++---- drivers/gpu/drm/i915/display/intel_display.h | 2 -- .../gpu/drm/i915/display/intel_display_power_well.c | 6 +++++- drivers/gpu/drm/i915/display/intel_hti.c | 3 --- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index c958b2a92cc7..553c60d452dd 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -1864,9 +1864,7 @@ static void hsw_crtc_disable(struct intel_atomic_state *state, /* Prefer intel_encoder_is_combo() */ bool intel_phy_is_combo(struct intel_display *display, enum phy phy) { - if (phy == PHY_NONE) - return false; - else if (display->platform.alderlake_s) + if (display->platform.alderlake_s) return phy <= PHY_E; else if (display->platform.dg1 || display->platform.rocketlake) return phy <= PHY_D; @@ -1920,7 +1918,7 @@ bool intel_phy_is_snps(struct intel_display *display, enum phy phy) * For DG2, and for DG2 only, all four "combo" ports and the TC1 port * (PHY E) use Synopsis PHYs. See intel_phy_is_tc(). */ - return display->platform.dg2 && phy > PHY_NONE && phy <= PHY_E; + return display->platform.dg2 && phy <= PHY_E; } /* Prefer intel_encoder_to_phy() */ @@ -1938,6 +1936,10 @@ enum phy intel_port_to_phy(struct intel_display *display, enum port port) port == PORT_D) return PHY_A; + if (drm_WARN(display->drm, port < 0, + "PHY is invalid if port < 0 (%d), assuming PHY_A\n", port)) + return PHY_A; + return PHY_A + port - PORT_A; } diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index e7ecae9d2d21..eecafff167d3 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -136,8 +136,6 @@ enum tc_port { }; enum phy { - PHY_NONE = -1, - PHY_A = 0, PHY_B, PHY_C, diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 02cb4d800e23..233465a8306e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -325,7 +325,11 @@ static enum phy icl_aux_pw_to_phy(struct intel_display *display, { struct intel_encoder *encoder = icl_aux_pw_to_encoder(display, power_well); - return encoder ? intel_encoder_to_phy(encoder) : PHY_NONE; + if (drm_WARN(display->drm, !encoder, + "PHY is invalid if encoder is NULL, assuming PHY_A\n")) + return PHY_A; + + return intel_encoder_to_phy(encoder); } static bool icl_aux_pw_is_tc_phy(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_hti.c b/drivers/gpu/drm/i915/display/intel_hti.c index dc454420c134..56602240ceff 100644 --- a/drivers/gpu/drm/i915/display/intel_hti.c +++ b/drivers/gpu/drm/i915/display/intel_hti.c @@ -23,9 +23,6 @@ void intel_hti_init(struct intel_display *display) bool intel_hti_uses_phy(struct intel_display *display, enum phy phy) { - if (drm_WARN_ON(display->drm, phy == PHY_NONE)) - return false; - return display->hti.state & HDPORT_ENABLED && display->hti.state & HDPORT_DDI_USED(phy); } From 600a7c9d40e5e0c5544f42d1c9592c8d15224dc0 Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 18 Aug 2026 15:21:49 +0530 Subject: [PATCH 39/72] drm/i915/display: Clear SEL_FETCH_PLANE_CTL on plane disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icl_plane_disable_sel_fetch_arm() wrote SEL_FETCH_PLANE_CTL = 0 only when crtc_state->enable_psr2_sel_fetch was set. If a plane was disabled after selective fetch had been turned off, the guard fired early and left the register's enable bit set in hardware. The bit is harmless until selective fetch is re-enabled. When it is, the hardware resumes fetching for the now-disabled plane and keeps its old DDB range reserved. i9xx_cursor_disable_sel_fetch_arm() has the same guard on SEL_FETCH_CUR_CTL and is fixed the same way. v2: Add same check for cursor also. [sashiko] Cc: stable@vger.kernel.org Fixes: b1f5279b5981 ("drm/i915/psr: Move plane sel fetch configuration into plane source files") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8739 Assisted-by: GitHub-Copilot:claude-opus-4.6 Signed-off-by: Nemesa Garg Reviewed-by: Jouni Högander Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260818095149.2172935-1-nemesa.garg@intel.com --- drivers/gpu/drm/i915/display/intel_cursor.c | 15 ++++++++++----- .../gpu/drm/i915/display/skl_universal_plane.c | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 0673f16f6fd0..86bb96ac449b 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -530,13 +530,18 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state, } static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb, - struct intel_plane *plane, - const struct intel_crtc_state *crtc_state) + struct intel_plane *plane) { struct intel_display *display = to_intel_display(plane); enum pipe pipe = plane->pipe; - if (!crtc_state->enable_psr2_sel_fetch) + /* + * Clear this whenever the hardware has selective fetch, not just when + * the current state uses it. The cursor may have been enabled with + * selective fetch earlier and had its enable bit orphaned when the + * feature was switched off. + */ + if (!HAS_PSR2_SEL_FETCH(display)) return; intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0); @@ -586,7 +591,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb, if (crtc_state->enable_psr2_su_region_et) wa_16021440873(dsb, plane, crtc_state, plane_state); else - i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + i9xx_cursor_disable_sel_fetch_arm(dsb, plane); } } @@ -695,7 +700,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb, if (plane_state) i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state); else - i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + i9xx_cursor_disable_sel_fetch_arm(dsb, plane); if (plane->cursor.base != base || plane->cursor.size != fbc_ctl || diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 07a683293352..5cda1ab90e40 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -879,13 +879,18 @@ skl_plane_disable_arm(struct intel_dsb *dsb, } static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb, - struct intel_plane *plane, - const struct intel_crtc_state *crtc_state) + struct intel_plane *plane) { struct intel_display *display = to_intel_display(plane); enum pipe pipe = plane->pipe; - if (!crtc_state->enable_psr2_sel_fetch) + /* + * Clear this whenever the hardware has selective fetch, not just when + * the current state uses it. The plane may have been enabled with + * selective fetch earlier and had its enable bit orphaned when the + * feature was switched off. + */ + if (!HAS_PSR2_SEL_FETCH(display)) return; intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0); @@ -921,7 +926,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb, skl_write_plane_wm(dsb, plane, crtc_state); - icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); + icl_plane_disable_sel_fetch_arm(dsb, plane); if (plane_has_normalizer(plane)) intel_de_write_dsb(display, dsb, @@ -1641,7 +1646,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb, intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), SEL_FETCH_PLANE_CTL_ENABLE); else - icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); + icl_plane_disable_sel_fetch_arm(dsb, plane); } static void From 7fe0404b027fe478cbcb2e197c9a969a98a191e6 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 24 Jul 2026 15:35:23 -0300 Subject: [PATCH 40/72] drm/i915/xe3p_lpd: Update CURSOR_BUF/WM masks Xe3p onwards CURSOR_BUF/WM masks have been increased. Update them to reflect the following. Bspec: 69847, 69880 Signed-off-by: Suraj Kandpal Reviewed-by: Dnyaneshwar Bhadane Link: https://patch.msgid.link/20260724-xe3p_lpd-cursor-dbuf-increase-v1-1-42f2faae2fb5@intel.com Signed-off-by: Gustavo Sousa --- drivers/gpu/drm/i915/display/intel_cursor_regs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor_regs.h b/drivers/gpu/drm/i915/display/intel_cursor_regs.h index aaa66331063e..da2f9630ae2d 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cursor_regs.h @@ -81,7 +81,7 @@ #define CUR_WM_EN REG_BIT(31) #define CUR_WM_IGNORE_LINES REG_BIT(30) #define CUR_WM_LINES_MASK REG_GENMASK(26, 14) -#define CUR_WM_BLOCKS_MASK REG_GENMASK(11, 0) +#define CUR_WM_BLOCKS_MASK REG_GENMASK(12, 0) #define _CUR_WM_SAGV_A 0x70158 #define _CUR_WM_SAGV_B 0x71158 @@ -98,10 +98,10 @@ #define _CUR_BUF_CFG_A 0x7017c #define _CUR_BUF_CFG_B 0x7117c #define CUR_BUF_CFG(pipe) _MMIO_PIPE((pipe), _CUR_BUF_CFG_A, _CUR_BUF_CFG_B) -/* skl+: 10 bits, icl+ 11 bits, adlp+ 12 bits */ -#define CUR_BUF_END_MASK REG_GENMASK(27, 16) +/* skl+: 10 bits, icl+ 11 bits, adlp+ 12 bits, xe3plpd+ 13 bits */ +#define CUR_BUF_END_MASK REG_GENMASK(28, 16) #define CUR_BUF_END(end) REG_FIELD_PREP(CUR_BUF_END_MASK, (end)) -#define CUR_BUF_START_MASK REG_GENMASK(11, 0) +#define CUR_BUF_START_MASK REG_GENMASK(12, 0) #define CUR_BUF_START(start) REG_FIELD_PREP(CUR_BUF_START_MASK, (start)) /* tgl+ */ From 1786d26887817a779641d3a093c66ac91382113b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 17 Jul 2026 18:51:06 +0300 Subject: [PATCH 41/72] drm/i915/cdclk: Avoid spurious cdclk sanitization on PTL+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently PTL+ no longer has the cd2x pipe select field in CDCLK_CTL. Take that into account during CDCLK sanitization. This currently triggers a spurious CDCLK sanitization during driver load on PTL+ which will causes a visible glitch on all active displays. Cc: stable@vger.kernel.org Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8550 Fixes: 3f9de66f8acb ("drm/i915/cdclk: Fix up CDCLK_FREQ_DECIMAL without a full PLL re-enable") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260717155107.17801-1-ville.syrjala@linux.intel.com Reviewed-by: Suraj Kandpal --- drivers/gpu/drm/i915/display/intel_cdclk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index a53d88727177..9e5e15b0c4d1 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2381,8 +2381,10 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * dividers both syncing to an active pipe, or asynchronously * (PIPE_NONE). */ - cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); - cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + if (DISPLAY_VER(display) < 30) { + cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); + cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + } if (cdctl != expected) { if (DISPLAY_VER(display) < 20) { From 1ceb1ef81c32e83d99a52225d6437832316654a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 17 Jul 2026 18:51:07 +0300 Subject: [PATCH 42/72] drm/i915/cdclk: Introduce has_cd2x_pipe_select() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have several open coded checks for the platform's cd2x pipe select capability, each written slightly differently. Move it all into a common has_cd2x_pipe_select() helper. No functional changes as the previous 'DISPLAY_VER < 30' checks were in codepaths that already exclude pre-bxt platforms, and intel_cdclk_can_cd2x_update() already rejects PTL+ via the later HAS_CDCLK_SQUASH() check. Also update the docs for the register bitfield while at it. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260717155107.17801-2-ville.syrjala@linux.intel.com Reviewed-by: Suraj Kandpal --- drivers/gpu/drm/i915/display/intel_cdclk.c | 12 ++++++++---- drivers/gpu/drm/i915/display/intel_display_regs.h | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 9e5e15b0c4d1..75ada941d113 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2177,6 +2177,11 @@ static bool pll_enable_wa_needed(struct intel_display *display) display->cdclk.hw.vco > 0; } +static bool has_cd2x_pipe_select(struct intel_display *display) +{ + return IS_DISPLAY_VER(display, 10, 20) || display->platform.broxton; +} + static u32 bxt_cdclk_ctl(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -2190,7 +2195,7 @@ static u32 bxt_cdclk_ctl(struct intel_display *display, val = bxt_cdclk_cd2x_div_sel(display, cdclk, vco, waveform); - if (DISPLAY_VER(display) < 30) + if (has_cd2x_pipe_select(display)) val |= bxt_cdclk_cd2x_pipe(display, pipe); /* @@ -2381,7 +2386,7 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * dividers both syncing to an active pipe, or asynchronously * (PIPE_NONE). */ - if (DISPLAY_VER(display) < 30) { + if (has_cd2x_pipe_select(display)) { cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); } @@ -2579,8 +2584,7 @@ static bool intel_cdclk_can_cd2x_update(struct intel_display *display, const struct intel_cdclk_config *a, const struct intel_cdclk_config *b) { - /* Older hw doesn't have the capability */ - if (DISPLAY_VER(display) < 10 && !display->platform.broxton) + if (!has_cd2x_pipe_select(display)) return false; /* diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index f98f76b308de..8251ec15190b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -2788,7 +2788,7 @@ enum skl_power_gate { #define BXT_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 20) /* bxt/glk */ #define BXT_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, (pipe)) #define BXT_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, 3) -#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl+ */ +#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl-lnl */ #define ICL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1) #define ICL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, 7) #define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) /* pre-icl */ From dcf423710d0253d7d729c3992bbae0c6197c9c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 17:31:00 +0300 Subject: [PATCH 43/72] drm/i915/cdclk: Fix dg2_power_well_count() return type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dg2_power_well_count() is supposed to return an integer, not a boolean. Make it so. Fixes: 9112ce99c1d7 ("drm/i915/cdclk: Extract dg2_power_well_count()") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826143100.19401-1-ville.syrjala@linux.intel.com Reviewed-by: Matt Roper --- drivers/gpu/drm/i915/display/intel_cdclk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 75ada941d113..279006f7b881 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2719,8 +2719,8 @@ static void intel_set_cdclk(struct intel_display *display, } } -static bool dg2_power_well_count(struct intel_display *display, - const struct intel_cdclk_state *cdclk_state) +static int dg2_power_well_count(struct intel_display *display, + const struct intel_cdclk_state *cdclk_state) { return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0; } From bed6347ae41b03bb80786b623c648cf221d7ed43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:20 +0300 Subject: [PATCH 44/72] drm/i915/cdclk: Don't bail if pcode post nofify fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already changed the actual cdclk frequency by the time we do the pcode post notify. So skipping the subsequent readout is plain wrong. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-2-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 279006f7b881..0c2d80edc5ab 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2328,12 +2328,10 @@ static void bxt_set_cdclk(struct intel_display *display, HSW_PCODE_DE_WRITE_FREQ_REQ, cdclk_config->voltage_level, 2); } - if (ret) { + if (ret) drm_err(display->drm, "PCode CDCLK freq set failed, (err %d, freq %d)\n", ret, cdclk); - return; - } intel_update_cdclk(display); From dc2a960b6435477b3c769ab925e63d398ce4d336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:21 +0300 Subject: [PATCH 45/72] drm/i915/cdclk: Pass CDCLK in MHz to pcode on DG2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are currently trying to pass the CDCLK in kHz to the pcode on DG2, while the pcode expects a value in MHz units. Adjust the units appropriately. v2: Call it 'cdclk_mhz' (Jani) Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-3-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 0c2d80edc5ab..f5b396c5955f 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2730,8 +2730,9 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) intel_atomic_get_old_cdclk_state(state); const struct intel_cdclk_state *new_cdclk_state = intel_atomic_get_new_cdclk_state(state); - unsigned int cdclk = 0; u8 voltage_level, num_active_pipes = 0; + u8 voltage_level, num_active_pipes = 0; bool change_cdclk, update_pipe_count; + unsigned int cdclk_mhz = 0; if (!intel_cdclk_changed(&old_cdclk_state->actual, &new_cdclk_state->actual) && @@ -2752,8 +2753,12 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) * if CDCLK is decreasing or not changing, set bits 25:16 to current CDCLK, * which basically means we choose the maximum of old and new CDCLK, if we know both */ - if (change_cdclk) - cdclk = max(new_cdclk_state->actual.cdclk, old_cdclk_state->actual.cdclk); + if (change_cdclk) { + unsigned int cdclk = max(new_cdclk_state->actual.cdclk, + old_cdclk_state->actual.cdclk); + + cdclk_mhz = DIV_ROUND_UP(cdclk, 1000); + } /* * According to "Sequence For Pipe Count Change", @@ -2764,7 +2769,7 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk, + intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, change_cdclk, update_pipe_count); } @@ -2775,8 +2780,9 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) intel_atomic_get_new_cdclk_state(state); const struct intel_cdclk_state *old_cdclk_state = intel_atomic_get_old_cdclk_state(state); - unsigned int cdclk = 0; u8 voltage_level, num_active_pipes = 0; + u8 voltage_level, num_active_pipes = 0; bool update_cdclk, update_pipe_count; + unsigned int cdclk_mhz = 0; /* According to "Sequence After Frequency Change", set voltage to used level */ voltage_level = new_cdclk_state->actual.voltage_level; @@ -2789,8 +2795,11 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) * According to "Sequence After Frequency Change", * set bits 25:16 to current CDCLK */ - if (update_cdclk) - cdclk = new_cdclk_state->actual.cdclk; + if (update_cdclk) { + unsigned int cdclk = new_cdclk_state->actual.cdclk; + + cdclk_mhz = DIV_ROUND_UP(cdclk, 1000); + } /* * According to "Sequence For Pipe Count Change", @@ -2801,7 +2810,7 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk, + intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, update_cdclk, update_pipe_count); } From 3f8c7b2fdd0e8256b4b7a314e1dd4e1a35e7e9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:22 +0300 Subject: [PATCH 46/72] drm/i915/cdclk: Do the DG2 CDCLK/pipe power well notify properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pcode post notify needs to happen after the CDCLK has been changed, not before. Also move the pre_notify call a bit for the sake of symmetry. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-4-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index f5b396c5955f..fea76ea806fe 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2850,9 +2850,6 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) &new_cdclk_state->actual)) return; - if (display->platform.dg2) - intel_cdclk_pcode_pre_notify(state); - if (new_cdclk_state->disable_pipes) { cdclk_config = new_cdclk_state->actual; pipe = INVALID_PIPE; @@ -2877,6 +2874,9 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) drm_WARN_ON(display->drm, !new_cdclk_state->base.changed); + if (display->platform.dg2) + intel_cdclk_pcode_pre_notify(state); + intel_set_cdclk(display, &cdclk_config, pipe, "Pre changing CDCLK to"); } @@ -2905,9 +2905,6 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) &new_cdclk_state->actual)) return; - if (display->platform.dg2) - intel_cdclk_pcode_post_notify(state); - if (!new_cdclk_state->disable_pipes && new_cdclk_state->actual.cdclk < old_cdclk_state->actual.cdclk) pipe = new_cdclk_state->pipe; @@ -2918,6 +2915,9 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) intel_set_cdclk(display, &new_cdclk_state->actual, pipe, "Post changing CDCLK to"); + + if (display->platform.dg2) + intel_cdclk_pcode_post_notify(state); } /* pixels per CDCLK */ From e68e1b80d1639d017bc99857c0c0194df2461334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:23 +0300 Subject: [PATCH 47/72] drm/i915/cdclk: Notify DG2 pcode about pipe power wells regardless of CDCLK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're currently skipping the pcode notifies on DG2 if the CDCLK isn't changing while the power well counts would still need updating. Do the pcode notifications also for pure pipe power well changes. The lower level intel_set_cdclk() will skip doing any real work if the actual CDCLK stuff doesn't need to change, so it's fine to call it even if only the power wells need updating. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-5-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index fea76ea806fe..4c093d84da0d 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2847,7 +2847,9 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) return; if (!intel_cdclk_changed(&old_cdclk_state->actual, - &new_cdclk_state->actual)) + &new_cdclk_state->actual) && + dg2_power_well_count(display, old_cdclk_state) == + dg2_power_well_count(display, new_cdclk_state)) return; if (new_cdclk_state->disable_pipes) { @@ -2902,7 +2904,9 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) return; if (!intel_cdclk_changed(&old_cdclk_state->actual, - &new_cdclk_state->actual)) + &new_cdclk_state->actual) && + dg2_power_well_count(display, old_cdclk_state) == + dg2_power_well_count(display, new_cdclk_state)) return; if (!new_cdclk_state->disable_pipes && From 934b9973f712484af9ae8876533d9992fc59cc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:24 +0300 Subject: [PATCH 48/72] drm/i915/cdclk: Stop forcing voltage level to 3 all the time on DG2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SKL_CDCLK_PREPARE_FOR_CHANGE == DISPLAY_TO_PCODE_VOLTAGE(3) so we are currently forcing the voltage level to 3 all the time on DG2. Remove SKL_CDCLK_PREPARE_FOR_CHANGE from the mask to avoid this. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-6-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 4c093d84da0d..b9e13e40fec5 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2647,7 +2647,6 @@ static void intel_pcode_notify(struct intel_display *display, update_mask |= DISPLAY_TO_PCODE_PIPE_COUNT_VALID; ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, - SKL_CDCLK_PREPARE_FOR_CHANGE | update_mask, SKL_CDCLK_READY_FOR_CHANGE, SKL_CDCLK_READY_FOR_CHANGE, 3); From cd4cd3196060876640d76e85db962b6047873ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:25 +0300 Subject: [PATCH 49/72] drm/i915/cdclk: Drop pointless platform check from bxt_set_cdclk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Overwrite cdclk.hw.voltage_level from intel_update_cdclk() at the end on bxt_set_cdclk() also on bxt/glk. While this isn't actually necessary due to bxt/glk not having any extra DDI based voltage level requirements, it does avoid one less silly 'if' in the code. On icl+ the value derived by bxt_get_cdclk() may not be correct if the voltage level was bumped up due to DDI requirements, thus overwriting the assumed value is crucial there. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-7-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index b9e13e40fec5..4ebda4e289d0 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2335,12 +2335,11 @@ static void bxt_set_cdclk(struct intel_display *display, intel_update_cdclk(display); - if (DISPLAY_VER(display) >= 11) - /* - * Can't read out the voltage level :( - * Let's just assume everything is as expected. - */ - display->cdclk.hw.voltage_level = cdclk_config->voltage_level; + /* + * Can't read out the voltage level :( + * Let's just assume everything is as expected. + */ + display->cdclk.hw.voltage_level = cdclk_config->voltage_level; } static void bxt_sanitize_cdclk(struct intel_display *display) From 8961ea13bec45eedbd44788df597d979023405fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:26 +0300 Subject: [PATCH 50/72] drm/i915/cdclk: s/intel_/dg2_/ for DG2 specific stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_pcode_*notify() are all DG2 specific code. Rename them to have a dg2_ namespace. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-8-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 31 ++++++++++------------ 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 4ebda4e289d0..a8112d9f4b9e 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2624,19 +2624,16 @@ void intel_cdclk_dump_config(struct intel_display *display, cdclk_config->voltage_level); } -static void intel_pcode_notify(struct intel_display *display, - u8 voltage_level, - u8 active_pipe_count, - u16 cdclk, - bool cdclk_update_valid, - bool pipe_count_update_valid) +static void dg2_cdclk_pcode_notify(struct intel_display *display, + u8 voltage_level, + u8 active_pipe_count, + u16 cdclk, + bool cdclk_update_valid, + bool pipe_count_update_valid) { int ret; u32 update_mask = 0; - if (!display->platform.dg2) - return; - update_mask = DISPLAY_TO_PCODE_UPDATE_MASK(cdclk, active_pipe_count, voltage_level); if (cdclk_update_valid) @@ -2721,7 +2718,7 @@ static int dg2_power_well_count(struct intel_display *display, return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0; } -static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) +static void dg2_cdclk_pcode_pre_notify(struct intel_atomic_state *state) { struct intel_display *display = to_intel_display(state); const struct intel_cdclk_state *old_cdclk_state = @@ -2767,11 +2764,11 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, - change_cdclk, update_pipe_count); + dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, + change_cdclk, update_pipe_count); } -static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) +static void dg2_cdclk_pcode_post_notify(struct intel_atomic_state *state) { struct intel_display *display = to_intel_display(state); const struct intel_cdclk_state *new_cdclk_state = @@ -2808,8 +2805,8 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, - update_cdclk, update_pipe_count); + dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, + update_cdclk, update_pipe_count); } bool intel_cdclk_is_decreasing_later(struct intel_atomic_state *state) @@ -2875,7 +2872,7 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) drm_WARN_ON(display->drm, !new_cdclk_state->base.changed); if (display->platform.dg2) - intel_cdclk_pcode_pre_notify(state); + dg2_cdclk_pcode_pre_notify(state); intel_set_cdclk(display, &cdclk_config, pipe, "Pre changing CDCLK to"); @@ -2919,7 +2916,7 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) "Post changing CDCLK to"); if (display->platform.dg2) - intel_cdclk_pcode_post_notify(state); + dg2_cdclk_pcode_post_notify(state); } /* pixels per CDCLK */ From 9131fd68a86ba5148b133ba6b5811fe00e107863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:27 +0300 Subject: [PATCH 51/72] drm/i915/cdclk: Unify the pcode pre/post notify in bxt_set_cdclk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The control flow between the pcode pre and post notifications in bxt_set_cdclk() is written in two different ways, even though they end up doing the same thing. Unify the code. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-9-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index a8112d9f4b9e..bdf9b6d84a4a 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2264,7 +2264,7 @@ static void bxt_set_cdclk(struct intel_display *display, { struct intel_cdclk_config mid_cdclk_config; int cdclk = cdclk_config->cdclk; - int ret = 0; + int ret; /* * Inform power controller of upcoming frequency change. @@ -2273,7 +2273,7 @@ static void bxt_set_cdclk(struct intel_display *display, * this step. */ if (DISPLAY_VER(display) >= 14 || display->platform.dg2) - ; /* NOOP */ + ret = 0; /* NOOP */ else if (DISPLAY_VER(display) >= 11) ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, SKL_CDCLK_PREPARE_FOR_CHANGE, @@ -2309,15 +2309,12 @@ static void bxt_set_cdclk(struct intel_display *display, if (DISPLAY_VER(display) >= 20 && cdclk > display->cdclk.hw.cdclk) xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config); - if (DISPLAY_VER(display) >= 14) - /* - * NOOP - No Pcode communication needed for - * Display versions 14 and beyond - */; - else if (DISPLAY_VER(display) >= 11 && !display->platform.dg2) + if (DISPLAY_VER(display) >= 14 || display->platform.dg2) + ret = 0; /* NOOP */ + else if (DISPLAY_VER(display) >= 11) ret = intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, cdclk_config->voltage_level); - if (DISPLAY_VER(display) < 11) { + else /* * The timeout isn't specified, the 2ms used here is based on * experiment. @@ -2327,7 +2324,6 @@ static void bxt_set_cdclk(struct intel_display *display, ret = intel_parent_pcode_write_timeout(display, HSW_PCODE_DE_WRITE_FREQ_REQ, cdclk_config->voltage_level, 2); - } if (ret) drm_err(display->drm, "PCode CDCLK freq set failed, (err %d, freq %d)\n", From e2760d76d1a20f2226a11ac2fe372abc87a1e312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:28 +0300 Subject: [PATCH 52/72] drm/i915/cdclk: Unify pcode related debugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The debug spew for the cdclk pcode per/post notify is very inconsistent between different platforms. Unify it all to the same form. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-10-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 65 ++++++++++++---------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index bdf9b6d84a4a..07471a6cef8f 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -891,7 +891,7 @@ static void bdw_set_cdclk(struct intel_display *display, ret = intel_parent_pcode_write(display, BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ, 0x0); if (ret) { drm_err(display->drm, - "failed to inform pcode about cdclk change\n"); + "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); return; } @@ -918,8 +918,11 @@ static void bdw_set_cdclk(struct intel_display *display, if (ret) drm_err(display->drm, "Switching back to LCPLL failed\n"); - intel_parent_pcode_write(display, HSW_PCODE_DE_WRITE_FREQ_REQ, - cdclk_config->voltage_level); + ret = intel_parent_pcode_write(display, HSW_PCODE_DE_WRITE_FREQ_REQ, + cdclk_config->voltage_level); + if (ret) + drm_err(display->drm, + "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); intel_de_write(display, CDCLK_FREQ, DIV_ROUND_CLOSEST(cdclk, 1000) - 1); @@ -1181,7 +1184,7 @@ static void skl_set_cdclk(struct intel_display *display, SKL_CDCLK_READY_FOR_CHANGE, 3); if (ret) { drm_err(display->drm, - "Failed to inform PCU about cdclk change (%d)\n", ret); + "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); return; } @@ -1221,8 +1224,11 @@ static void skl_set_cdclk(struct intel_display *display, intel_de_posting_read(display, CDCLK_CTL); /* inform PCU of the change */ - intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, - cdclk_config->voltage_level); + ret = intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, + cdclk_config->voltage_level); + if (ret) + drm_err(display->drm, + "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); intel_update_cdclk(display); } @@ -2290,8 +2296,7 @@ static void bxt_set_cdclk(struct intel_display *display, if (ret) { drm_err(display->drm, - "Failed to inform PCU about cdclk change (err %d, freq %d)\n", - ret, cdclk); + "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); return; } @@ -2326,8 +2331,7 @@ static void bxt_set_cdclk(struct intel_display *display, cdclk_config->voltage_level, 2); if (ret) drm_err(display->drm, - "PCode CDCLK freq set failed, (err %d, freq %d)\n", - ret, cdclk); + "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); intel_update_cdclk(display); @@ -2620,14 +2624,13 @@ void intel_cdclk_dump_config(struct intel_display *display, cdclk_config->voltage_level); } -static void dg2_cdclk_pcode_notify(struct intel_display *display, - u8 voltage_level, - u8 active_pipe_count, - u16 cdclk, - bool cdclk_update_valid, - bool pipe_count_update_valid) +static int dg2_cdclk_pcode_notify(struct intel_display *display, + u8 voltage_level, + u8 active_pipe_count, + u16 cdclk, + bool cdclk_update_valid, + bool pipe_count_update_valid) { - int ret; u32 update_mask = 0; update_mask = DISPLAY_TO_PCODE_UPDATE_MASK(cdclk, active_pipe_count, voltage_level); @@ -2638,14 +2641,10 @@ static void dg2_cdclk_pcode_notify(struct intel_display *display, if (pipe_count_update_valid) update_mask |= DISPLAY_TO_PCODE_PIPE_COUNT_VALID; - ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, - update_mask, - SKL_CDCLK_READY_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, 3); - if (ret) - drm_err(display->drm, - "Failed to inform PCU about display config (err %d)\n", - ret); + return intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, + update_mask, + SKL_CDCLK_READY_FOR_CHANGE, + SKL_CDCLK_READY_FOR_CHANGE, 3); } static void intel_set_cdclk(struct intel_display *display, @@ -2724,6 +2723,7 @@ static void dg2_cdclk_pcode_pre_notify(struct intel_atomic_state *state) u8 voltage_level, num_active_pipes = 0; bool change_cdclk, update_pipe_count; unsigned int cdclk_mhz = 0; + int ret; if (!intel_cdclk_changed(&old_cdclk_state->actual, &new_cdclk_state->actual) && @@ -2760,8 +2760,11 @@ static void dg2_cdclk_pcode_pre_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, - change_cdclk, update_pipe_count); + ret = dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, + change_cdclk, update_pipe_count); + if (ret) + drm_err(display->drm, + "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); } static void dg2_cdclk_pcode_post_notify(struct intel_atomic_state *state) @@ -2774,6 +2777,7 @@ static void dg2_cdclk_pcode_post_notify(struct intel_atomic_state *state) u8 voltage_level, num_active_pipes = 0; bool update_cdclk, update_pipe_count; unsigned int cdclk_mhz = 0; + int ret; /* According to "Sequence After Frequency Change", set voltage to used level */ voltage_level = new_cdclk_state->actual.voltage_level; @@ -2801,8 +2805,11 @@ static void dg2_cdclk_pcode_post_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, - update_cdclk, update_pipe_count); + ret = dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, + update_cdclk, update_pipe_count); + if (ret) + drm_err(display->drm, + "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); } bool intel_cdclk_is_decreasing_later(struct intel_atomic_state *state) From 1098a94f822d24764a4ec2bf1e4643e231622cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:29 +0300 Subject: [PATCH 53/72] drm/i915/cdclk: Extract bdw_cdclk_pcode_{pre,post}_notify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the BDW pcode notify stuff to a few small helpers. The plan is to unify these between all the platforms and turn them into vfuncs. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-11-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 07471a6cef8f..96094e1b2bb3 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -872,6 +872,19 @@ static u32 bdw_cdclk_freq_sel(int cdclk) } } +static int bdw_cdclk_pcode_pre_notify(struct intel_display *display) +{ + return intel_parent_pcode_write(display, BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ, + 0x0); +} + +static int bdw_cdclk_pcode_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + return intel_parent_pcode_write(display, HSW_PCODE_DE_WRITE_FREQ_REQ, + cdclk_config->voltage_level); +} + static void bdw_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -888,7 +901,7 @@ static void bdw_set_cdclk(struct intel_display *display, "trying to change cdclk frequency with cdclk not enabled\n")) return; - ret = intel_parent_pcode_write(display, BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ, 0x0); + ret = bdw_cdclk_pcode_pre_notify(display); if (ret) { drm_err(display->drm, "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); @@ -918,8 +931,7 @@ static void bdw_set_cdclk(struct intel_display *display, if (ret) drm_err(display->drm, "Switching back to LCPLL failed\n"); - ret = intel_parent_pcode_write(display, HSW_PCODE_DE_WRITE_FREQ_REQ, - cdclk_config->voltage_level); + ret = bdw_cdclk_pcode_post_notify(display, cdclk_config); if (ret) drm_err(display->drm, "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); From 14890a802ea4d1b37d83f0b8277d812c61eae5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:30 +0300 Subject: [PATCH 54/72] drm/i915/cdclk: Extract skl_cdclk_pcode_{pre,post}_notify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the SKL/ICL+ pcode notify stuff to a few small helpers. The plan is to unify these between all the platforms and turn them into vfuncs. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-12-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 32 +++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 96094e1b2bb3..bbc9dcb72c1b 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1170,6 +1170,21 @@ static u32 skl_cdclk_freq_sel(struct intel_display *display, } } +static int skl_cdclk_pcode_pre_notify(struct intel_display *display) +{ + return intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, + SKL_CDCLK_PREPARE_FOR_CHANGE, + SKL_CDCLK_READY_FOR_CHANGE, + SKL_CDCLK_READY_FOR_CHANGE, 3); +} + +static int skl_cdclk_pcode_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + return intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, + cdclk_config->voltage_level); +} + static void skl_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -1190,10 +1205,7 @@ static void skl_set_cdclk(struct intel_display *display, drm_WARN_ON_ONCE(display->drm, display->platform.skylake && vco == 8640000); - ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, - SKL_CDCLK_PREPARE_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, 3); + ret = skl_cdclk_pcode_pre_notify(display); if (ret) { drm_err(display->drm, "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); @@ -1235,9 +1247,7 @@ static void skl_set_cdclk(struct intel_display *display, intel_de_write(display, CDCLK_CTL, cdclk_ctl); intel_de_posting_read(display, CDCLK_CTL); - /* inform PCU of the change */ - ret = intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, - cdclk_config->voltage_level); + ret = skl_cdclk_pcode_post_notify(display, cdclk_config); if (ret) drm_err(display->drm, "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); @@ -2293,10 +2303,7 @@ static void bxt_set_cdclk(struct intel_display *display, if (DISPLAY_VER(display) >= 14 || display->platform.dg2) ret = 0; /* NOOP */ else if (DISPLAY_VER(display) >= 11) - ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, - SKL_CDCLK_PREPARE_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, 3); + ret = skl_cdclk_pcode_pre_notify(display); else /* * BSpec requires us to wait up to 150usec, but that leads to @@ -2329,8 +2336,7 @@ static void bxt_set_cdclk(struct intel_display *display, if (DISPLAY_VER(display) >= 14 || display->platform.dg2) ret = 0; /* NOOP */ else if (DISPLAY_VER(display) >= 11) - ret = intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, - cdclk_config->voltage_level); + ret = skl_cdclk_pcode_post_notify(display, cdclk_config); else /* * The timeout isn't specified, the 2ms used here is based on From 6dc9d09567047b25092f846893be83d1acc70065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:31 +0300 Subject: [PATCH 55/72] drm/i915/cdclk: Extract bxt_cdclk_pcode_{pre,post}_notify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the BXT/GLK pcode notify stuff to a few small helpers. The plan is to unify these between all the platforms and turn them into vfuncs. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-13-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 42 +++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index bbc9dcb72c1b..7afbbc19e3b2 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2253,6 +2253,29 @@ static u32 bxt_cdclk_ctl(struct intel_display *display, return val; } +static int bxt_cdclk_pcode_pre_notify(struct intel_display *display) +{ + /* + * BSpec requires us to wait up to 150usec, but that leads to + * timeouts; the 2ms used here is based on experiment. + */ + return intel_parent_pcode_write_timeout(display, HSW_PCODE_DE_WRITE_FREQ_REQ, + 0x80000000, 2); +} + +static int bxt_cdclk_pcode_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + /* + * The timeout isn't specified, the 2ms used here is based on + * experiment. + * FIXME: Waiting for the request completion could be delayed + * until the next PCODE request based on BSpec. + */ + return intel_parent_pcode_write_timeout(display, HSW_PCODE_DE_WRITE_FREQ_REQ, + cdclk_config->voltage_level, 2); +} + static void _bxt_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -2305,13 +2328,7 @@ static void bxt_set_cdclk(struct intel_display *display, else if (DISPLAY_VER(display) >= 11) ret = skl_cdclk_pcode_pre_notify(display); else - /* - * BSpec requires us to wait up to 150usec, but that leads to - * timeouts; the 2ms used here is based on experiment. - */ - ret = intel_parent_pcode_write_timeout(display, - HSW_PCODE_DE_WRITE_FREQ_REQ, - 0x80000000, 2); + ret = bxt_cdclk_pcode_pre_notify(display); if (ret) { drm_err(display->drm, @@ -2338,15 +2355,8 @@ static void bxt_set_cdclk(struct intel_display *display, else if (DISPLAY_VER(display) >= 11) ret = skl_cdclk_pcode_post_notify(display, cdclk_config); else - /* - * The timeout isn't specified, the 2ms used here is based on - * experiment. - * FIXME: Waiting for the request completion could be delayed - * until the next PCODE request based on BSpec. - */ - ret = intel_parent_pcode_write_timeout(display, - HSW_PCODE_DE_WRITE_FREQ_REQ, - cdclk_config->voltage_level, 2); + ret = bxt_cdclk_pcode_post_notify(display, cdclk_config); + if (ret) drm_err(display->drm, "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); From 22d664affc4384cb82580d7448c0263b875e5122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:32 +0300 Subject: [PATCH 56/72] drm/i915/cdclk: Introduce CDCLK .{pre,post}_notify() vfuncs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turn the cdclk pcode pre/post notify functions into vfuncs. Mainly to get rid of the hideous if-ladders in bxt_set_cdclk(). DG2 is currently doing its own thing with its pcode notify funcs so can't be converted yet. And MTL+ go via the pmdemand stuff so this is all supposedly handled elsewhere. Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-14-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 122 ++++++++++++--------- 1 file changed, 73 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 7afbbc19e3b2..a0649cf6a3f7 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -163,6 +163,9 @@ struct intel_cdclk_funcs { void (*set_cdclk)(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe); + int (*pre_notify)(struct intel_display *display); + int (*post_notify)(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config); int (*modeset_calc_cdclk)(struct intel_atomic_state *state); u8 (*calc_voltage_level)(int cdclk); }; @@ -173,6 +176,35 @@ void intel_cdclk_get_cdclk(struct intel_display *display, display->cdclk.funcs->get_cdclk(display, cdclk_config); } +static int intel_cdclk_pre_notify(struct intel_display *display) +{ + int ret; + + if (!display->cdclk.funcs->pre_notify) + return 0; + + ret = display->cdclk.funcs->pre_notify(display); + if (ret) + drm_err(display->drm, + "Failed to inform system about start of CDCLK change (%d)\n", ret); + + return ret; +} + +static void intel_cdclk_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + int ret; + + if (!display->cdclk.funcs->post_notify) + return; + + ret = display->cdclk.funcs->post_notify(display, cdclk_config); + if (ret) + drm_err(display->drm, + "Failed to inform system about end of CDCLK change (%d)\n", ret); +} + static void intel_cdclk_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -901,12 +933,9 @@ static void bdw_set_cdclk(struct intel_display *display, "trying to change cdclk frequency with cdclk not enabled\n")) return; - ret = bdw_cdclk_pcode_pre_notify(display); - if (ret) { - drm_err(display->drm, - "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); + ret = intel_cdclk_pre_notify(display); + if (ret) return; - } intel_de_rmw(display, LCPLL_CTL, 0, LCPLL_CD_SOURCE_FCLK); @@ -931,10 +960,7 @@ static void bdw_set_cdclk(struct intel_display *display, if (ret) drm_err(display->drm, "Switching back to LCPLL failed\n"); - ret = bdw_cdclk_pcode_post_notify(display, cdclk_config); - if (ret) - drm_err(display->drm, - "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); + intel_cdclk_post_notify(display, cdclk_config); intel_de_write(display, CDCLK_FREQ, DIV_ROUND_CLOSEST(cdclk, 1000) - 1); @@ -1205,12 +1231,9 @@ static void skl_set_cdclk(struct intel_display *display, drm_WARN_ON_ONCE(display->drm, display->platform.skylake && vco == 8640000); - ret = skl_cdclk_pcode_pre_notify(display); - if (ret) { - drm_err(display->drm, - "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); + ret = intel_cdclk_pre_notify(display); + if (ret) return; - } freq_select = skl_cdclk_freq_sel(display, cdclk, vco); @@ -1247,10 +1270,7 @@ static void skl_set_cdclk(struct intel_display *display, intel_de_write(display, CDCLK_CTL, cdclk_ctl); intel_de_posting_read(display, CDCLK_CTL); - ret = skl_cdclk_pcode_post_notify(display, cdclk_config); - if (ret) - drm_err(display->drm, - "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); + intel_cdclk_post_notify(display, cdclk_config); intel_update_cdclk(display); } @@ -2317,24 +2337,9 @@ static void bxt_set_cdclk(struct intel_display *display, int cdclk = cdclk_config->cdclk; int ret; - /* - * Inform power controller of upcoming frequency change. - * Display versions 14 and beyond do not follow the PUnit - * mailbox communication, skip - * this step. - */ - if (DISPLAY_VER(display) >= 14 || display->platform.dg2) - ret = 0; /* NOOP */ - else if (DISPLAY_VER(display) >= 11) - ret = skl_cdclk_pcode_pre_notify(display); - else - ret = bxt_cdclk_pcode_pre_notify(display); - - if (ret) { - drm_err(display->drm, - "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); + ret = intel_cdclk_pre_notify(display); + if (ret) return; - } if (DISPLAY_VER(display) >= 20 && cdclk < display->cdclk.hw.cdclk) xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config); @@ -2350,16 +2355,7 @@ static void bxt_set_cdclk(struct intel_display *display, if (DISPLAY_VER(display) >= 20 && cdclk > display->cdclk.hw.cdclk) xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config); - if (DISPLAY_VER(display) >= 14 || display->platform.dg2) - ret = 0; /* NOOP */ - else if (DISPLAY_VER(display) >= 11) - ret = skl_cdclk_pcode_post_notify(display, cdclk_config); - else - ret = bxt_cdclk_pcode_post_notify(display, cdclk_config); - - if (ret) - drm_err(display->drm, - "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); + intel_cdclk_post_notify(display, cdclk_config); intel_update_cdclk(display); @@ -3983,9 +3979,25 @@ static const struct intel_cdclk_funcs xe3lpd_cdclk_funcs = { .calc_voltage_level = xe3lpd_calc_voltage_level, }; +static const struct intel_cdclk_funcs mtl_cdclk_funcs = { + .get_cdclk = bxt_get_cdclk, + .set_cdclk = bxt_set_cdclk, + .modeset_calc_cdclk = bxt_modeset_calc_cdclk, + .calc_voltage_level = rplu_calc_voltage_level, +}; + +static const struct intel_cdclk_funcs dg2_cdclk_funcs = { + .get_cdclk = bxt_get_cdclk, + .set_cdclk = bxt_set_cdclk, + .modeset_calc_cdclk = bxt_modeset_calc_cdclk, + .calc_voltage_level = tgl_calc_voltage_level, +}; + static const struct intel_cdclk_funcs rplu_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = rplu_calc_voltage_level, }; @@ -3993,6 +4005,8 @@ static const struct intel_cdclk_funcs rplu_cdclk_funcs = { static const struct intel_cdclk_funcs tgl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = tgl_calc_voltage_level, }; @@ -4000,6 +4014,8 @@ static const struct intel_cdclk_funcs tgl_cdclk_funcs = { static const struct intel_cdclk_funcs ehl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = ehl_calc_voltage_level, }; @@ -4007,6 +4023,8 @@ static const struct intel_cdclk_funcs ehl_cdclk_funcs = { static const struct intel_cdclk_funcs icl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = icl_calc_voltage_level, }; @@ -4014,6 +4032,8 @@ static const struct intel_cdclk_funcs icl_cdclk_funcs = { static const struct intel_cdclk_funcs bxt_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, + .pre_notify = bxt_cdclk_pcode_pre_notify, + .post_notify = bxt_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = bxt_calc_voltage_level, }; @@ -4021,12 +4041,16 @@ static const struct intel_cdclk_funcs bxt_cdclk_funcs = { static const struct intel_cdclk_funcs skl_cdclk_funcs = { .get_cdclk = skl_get_cdclk, .set_cdclk = skl_set_cdclk, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = skl_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs bdw_cdclk_funcs = { .get_cdclk = bdw_get_cdclk, .set_cdclk = bdw_set_cdclk, + .pre_notify = bdw_cdclk_pcode_pre_notify, + .post_notify = bdw_cdclk_pcode_post_notify, .modeset_calc_cdclk = bdw_modeset_calc_cdclk, }; @@ -4132,16 +4156,16 @@ void intel_init_cdclk_hooks(struct intel_display *display) display->cdclk.funcs = &xe3lpd_cdclk_funcs; display->cdclk.table = xe3lpd_cdclk_table; } else if (DISPLAY_VER(display) >= 20) { - display->cdclk.funcs = &rplu_cdclk_funcs; + display->cdclk.funcs = &mtl_cdclk_funcs; display->cdclk.table = xe2lpd_cdclk_table; } else if (DISPLAY_VERx100(display) >= 1401) { - display->cdclk.funcs = &rplu_cdclk_funcs; + display->cdclk.funcs = &mtl_cdclk_funcs; display->cdclk.table = xe2hpd_cdclk_table; } else if (DISPLAY_VER(display) >= 14) { - display->cdclk.funcs = &rplu_cdclk_funcs; + display->cdclk.funcs = &mtl_cdclk_funcs; display->cdclk.table = mtl_cdclk_table; } else if (display->platform.dg2) { - display->cdclk.funcs = &tgl_cdclk_funcs; + display->cdclk.funcs = &dg2_cdclk_funcs; display->cdclk.table = dg2_cdclk_table; } else if (display->platform.alderlake_p) { /* Wa_22011320316:adl-p[a0] */ From 050e8067dcb8f5787be41753832d4a023d0ffe74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2026 16:40:33 +0300 Subject: [PATCH 57/72] drm/i915/cdclk: Hoist intel_cdclk_{pre,post}_notify() calls upwards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that intel_cdclk_{pre,post}_notify() are implemented via vfuncs there is no need to keep them inside the .set_cdclk() hooks. Move the calls one level up to intel_cdclk_set_cdclk(). We do need to adjust {skl,bxt}_cdclk_(un)init_hw() to call the wrapper rather than the low level implementation directly, or else they would not do the pcode notification anymore. The two slight functional changes here are: - bdw_set_cdclk() might theoretically bail out after doing the pre notification, but that codepath would only come into play if the hardware is seriously misprogrammed, so should never happen - cdclk hw readout is still done from .set_cdclk(), so that now happens before the post notify vs. previously the readout happened before it. This should not matter as the readout is not affected by the post notify (since we can't actually read out anything from pcode). Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260826134033.13976-15-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 33 ++++++---------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index a0649cf6a3f7..92e6fca861f6 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -209,7 +209,12 @@ static void intel_cdclk_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) { + if (intel_cdclk_pre_notify(display)) + return; + display->cdclk.funcs->set_cdclk(display, cdclk_config, pipe); + + intel_cdclk_post_notify(display, cdclk_config); } static int intel_cdclk_modeset_calc_cdclk(struct intel_atomic_state *state) @@ -933,10 +938,6 @@ static void bdw_set_cdclk(struct intel_display *display, "trying to change cdclk frequency with cdclk not enabled\n")) return; - ret = intel_cdclk_pre_notify(display); - if (ret) - return; - intel_de_rmw(display, LCPLL_CTL, 0, LCPLL_CD_SOURCE_FCLK); @@ -960,8 +961,6 @@ static void bdw_set_cdclk(struct intel_display *display, if (ret) drm_err(display->drm, "Switching back to LCPLL failed\n"); - intel_cdclk_post_notify(display, cdclk_config); - intel_de_write(display, CDCLK_FREQ, DIV_ROUND_CLOSEST(cdclk, 1000) - 1); @@ -1218,7 +1217,6 @@ static void skl_set_cdclk(struct intel_display *display, int cdclk = cdclk_config->cdclk; int vco = cdclk_config->vco; u32 freq_select, cdclk_ctl; - int ret; /* * Based on WA#1183 CDCLK rates 308 and 617MHz CDCLK rates are @@ -1231,10 +1229,6 @@ static void skl_set_cdclk(struct intel_display *display, drm_WARN_ON_ONCE(display->drm, display->platform.skylake && vco == 8640000); - ret = intel_cdclk_pre_notify(display); - if (ret) - return; - freq_select = skl_cdclk_freq_sel(display, cdclk, vco); if (display->cdclk.hw.vco != 0 && @@ -1270,8 +1264,6 @@ static void skl_set_cdclk(struct intel_display *display, intel_de_write(display, CDCLK_CTL, cdclk_ctl); intel_de_posting_read(display, CDCLK_CTL); - intel_cdclk_post_notify(display, cdclk_config); - intel_update_cdclk(display); } @@ -1362,7 +1354,7 @@ static void skl_cdclk_init_hw(struct intel_display *display) cdclk_config.cdclk = skl_calc_cdclk(0, cdclk_config.vco); cdclk_config.voltage_level = skl_calc_voltage_level(cdclk_config.cdclk); - skl_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } static void skl_cdclk_uninit_hw(struct intel_display *display) @@ -1373,7 +1365,7 @@ static void skl_cdclk_uninit_hw(struct intel_display *display) cdclk_config.vco = 0; cdclk_config.voltage_level = skl_calc_voltage_level(cdclk_config.cdclk); - skl_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } struct intel_cdclk_vals { @@ -2335,11 +2327,6 @@ static void bxt_set_cdclk(struct intel_display *display, { struct intel_cdclk_config mid_cdclk_config; int cdclk = cdclk_config->cdclk; - int ret; - - ret = intel_cdclk_pre_notify(display); - if (ret) - return; if (DISPLAY_VER(display) >= 20 && cdclk < display->cdclk.hw.cdclk) xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config); @@ -2355,8 +2342,6 @@ static void bxt_set_cdclk(struct intel_display *display, if (DISPLAY_VER(display) >= 20 && cdclk > display->cdclk.hw.cdclk) xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config); - intel_cdclk_post_notify(display, cdclk_config); - intel_update_cdclk(display); /* @@ -2463,7 +2448,7 @@ static void bxt_cdclk_init_hw(struct intel_display *display) cdclk_config.voltage_level = intel_cdclk_calc_voltage_level(display, cdclk_config.cdclk); - bxt_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } static void bxt_cdclk_uninit_hw(struct intel_display *display) @@ -2475,7 +2460,7 @@ static void bxt_cdclk_uninit_hw(struct intel_display *display) cdclk_config.voltage_level = intel_cdclk_calc_voltage_level(display, cdclk_config.cdclk); - bxt_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } /** From 2727922084672cc274ecea726ea00363c2893731 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Thu, 13 Aug 2026 12:19:02 +0530 Subject: [PATCH 58/72] drm/i915: Guard against NULL driver_data in i915_pci_probe() pci_match_device() can return the dummy pci_device_id_any entry when a device is force-bound via sysfs driver_override, in which case ->driver_data is unset (NULL). i915_pci_probe() casts it to struct intel_device_info * unconditionally and dereferences intel_info->require_force_probe, causing a NULL-ptr-deref. Reported-by: syzbot+db96c5ff032f4292a8dc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=db96c5ff032f4292a8dc Tested-by: syzbot+db96c5ff032f4292a8dc@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Deepanshu Kartikey Link: https://patch.msgid.link/20260813064902.367504-1-kartikey406@gmail.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 82415af47d54..2f03f95945f1 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -958,6 +958,9 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) (struct intel_device_info *) ent->driver_data; int err; + if (!intel_info) + return -ENODEV; + if (intel_info->require_force_probe && !id_forced(pdev->device)) { dev_info(&pdev->dev, "Your graphics device %04x is not properly supported by i915 in this\n" From 9b8e6cfbc4d03655f5afeab58e9c73e517e3aa82 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 17 Aug 2026 12:26:20 +0300 Subject: [PATCH 59/72] drm/i915/display: consolidate Gen9 clock gating initialization Instead of exporting many of the display clock gating functions, consolidate them into a single function that checks the platform and calls the appropriate platform-specific function. Start consolidating the Gen 9 functions first into a new intel_display_init_clock_gating() function. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260817092911.342072-2-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- .../i915/display/intel_display_clock_gating.c | 24 +++++++++++++++---- .../i915/display/intel_display_clock_gating.h | 6 +---- drivers/gpu/drm/i915/intel_clock_gating.c | 10 ++++---- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_clock_gating.c b/drivers/gpu/drm/i915/display/intel_display_clock_gating.c index d036c7e5863d..d5ecdaeab421 100644 --- a/drivers/gpu/drm/i915/display/intel_display_clock_gating.c +++ b/drivers/gpu/drm/i915/display/intel_display_clock_gating.c @@ -27,7 +27,7 @@ static void intel_display_gen9_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_MEMORY_WAKE); } -void intel_display_skl_init_clock_gating(struct intel_display *display) +static void intel_display_skl_init_clock_gating(struct intel_display *display) { /* * WaCompressedResourceDisplayNewHashMode:skl,kbl @@ -47,7 +47,7 @@ void intel_display_skl_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_kbl_init_clock_gating(struct intel_display *display) +static void intel_display_kbl_init_clock_gating(struct intel_display *display) { /* * WaCompressedResourceDisplayNewHashMode:skl,kbl @@ -67,7 +67,7 @@ void intel_display_kbl_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_cfl_init_clock_gating(struct intel_display *display) +static void intel_display_cfl_init_clock_gating(struct intel_display *display) { /* * WaCompressedResourceDisplayNewHashMode:skl,kbl (and cfl, cml) @@ -93,7 +93,7 @@ void intel_display_cfl_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_bxt_init_clock_gating(struct intel_display *display) +static void intel_display_bxt_init_clock_gating(struct intel_display *display) { intel_display_gen9_init_clock_gating(display); @@ -120,7 +120,7 @@ void intel_display_bxt_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_glk_init_clock_gating(struct intel_display *display) +static void intel_display_glk_init_clock_gating(struct intel_display *display) { intel_display_gen9_init_clock_gating(display); @@ -268,3 +268,17 @@ void intel_display_i965gm_init_clock_gating(struct intel_display *display) { intel_de_write(display, DSPCLK_GATE_D, 0); } + +void intel_display_init_clock_gating(struct intel_display *display) +{ + if (display->platform.skylake) + intel_display_skl_init_clock_gating(display); + else if (display->platform.kabylake) + intel_display_kbl_init_clock_gating(display); + else if (display->platform.coffeelake || display->platform.cometlake) + intel_display_cfl_init_clock_gating(display); + else if (display->platform.broxton) + intel_display_bxt_init_clock_gating(display); + else if (display->platform.geminilake) + intel_display_glk_init_clock_gating(display); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_clock_gating.h b/drivers/gpu/drm/i915/display/intel_display_clock_gating.h index b6dd34ca92dd..4e50e388656a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_clock_gating.h +++ b/drivers/gpu/drm/i915/display/intel_display_clock_gating.h @@ -8,11 +8,7 @@ struct intel_display; -void intel_display_skl_init_clock_gating(struct intel_display *display); -void intel_display_kbl_init_clock_gating(struct intel_display *display); -void intel_display_cfl_init_clock_gating(struct intel_display *display); -void intel_display_bxt_init_clock_gating(struct intel_display *display); -void intel_display_glk_init_clock_gating(struct intel_display *display); +void intel_display_init_clock_gating(struct intel_display *display); void intel_display_bdw_clock_gating_disable_fbcq(struct intel_display *display); void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display); void intel_display_bdw_clock_gating_kvm_notif(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 86bdeb20f427..84f838c660fd 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -57,12 +57,12 @@ static void bxt_init_clock_gating(struct drm_i915_private *i915) */ intel_uncore_rmw(&i915->uncore, GEN8_UCGCTL6, 0, GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ); - intel_display_bxt_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void glk_init_clock_gating(struct drm_i915_private *i915) { - intel_display_glk_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void ilk_init_clock_gating(struct drm_i915_private *i915) @@ -170,7 +170,7 @@ static void cfl_init_clock_gating(struct drm_i915_private *i915) /* WAC6entrylatency:cfl */ intel_uncore_rmw(&i915->uncore, FBC_LLC_READ_CTRL, 0, FBC_LLC_FULLY_OPEN); - intel_display_cfl_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void kbl_init_clock_gating(struct drm_i915_private *i915) @@ -188,7 +188,7 @@ static void kbl_init_clock_gating(struct drm_i915_private *i915) intel_uncore_rmw(&i915->uncore, GEN6_UCGCTL1, 0, GEN6_GAMUNIT_CLOCK_GATE_DISABLE); - intel_display_kbl_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void skl_init_clock_gating(struct drm_i915_private *i915) @@ -200,7 +200,7 @@ static void skl_init_clock_gating(struct drm_i915_private *i915) /* WAC6entrylatency:skl */ intel_uncore_rmw(&i915->uncore, FBC_LLC_READ_CTRL, 0, FBC_LLC_FULLY_OPEN); - intel_display_skl_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void bdw_init_clock_gating(struct drm_i915_private *i915) From 08e37f577e803ae09813dec2e4375aba44181382 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 17 Aug 2026 12:26:21 +0300 Subject: [PATCH 60/72] drm/i915/display: consolidate remaining clock gating initialization The remaining platform-specific display clock gating functions are still called directly by i915. Extend intel_display_init_clock_gating() so that it calls the correct functions for the remaining platforms. This groups Broadwell's three display operations into a single function. This changes the call order slighlty in relation to non-display workarounds, but it shouldn't be a problem. For IVB, we move the trickle-feed programming to the display helper and runs with the other display register setup. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260817092911.342072-3-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- .../i915/display/intel_display_clock_gating.c | 43 ++++++++++++++----- .../i915/display/intel_display_clock_gating.h | 10 ----- drivers/gpu/drm/i915/intel_clock_gating.c | 20 +++------ 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_clock_gating.c b/drivers/gpu/drm/i915/display/intel_display_clock_gating.c index d5ecdaeab421..ef1ee72494df 100644 --- a/drivers/gpu/drm/i915/display/intel_display_clock_gating.c +++ b/drivers/gpu/drm/i915/display/intel_display_clock_gating.c @@ -134,13 +134,13 @@ static void intel_display_glk_init_clock_gating(struct intel_display *display) PWM1_GATING_DIS | PWM2_GATING_DIS); } -void intel_display_bdw_clock_gating_disable_fbcq(struct intel_display *display) +static void intel_display_bdw_clock_gating_disable_fbcq(struct intel_display *display) { /* WaFbcAsynchFlipDisableFbcQueue:hsw,bdw */ intel_de_rmw(display, CHICKEN_PIPESL_1(PIPE_A), 0, HSW_FBCQ_DIS); } -void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display) +static void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display) { enum pipe pipe; @@ -154,14 +154,21 @@ void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display) } } -void intel_display_bdw_clock_gating_kvm_notif(struct intel_display *display) +static void intel_display_bdw_clock_gating_kvm_notif(struct intel_display *display) { /* WaKVMNotificationOnConfigChange:bdw */ intel_de_rmw(display, CHICKEN_PAR2_1, 0, KVM_CONFIG_CHANGE_NOTIFICATION_SELECT); } -void intel_display_hsw_init_clock_gating(struct intel_display *display) +static void intel_display_bdw_init_clock_gating(struct intel_display *display) +{ + intel_display_bdw_clock_gating_disable_fbcq(display); + intel_display_bdw_clock_gating_vblank_in_srd(display); + intel_display_bdw_clock_gating_kvm_notif(display); +} + +static void intel_display_hsw_init_clock_gating(struct intel_display *display) { enum pipe pipe; @@ -178,7 +185,7 @@ void intel_display_hsw_init_clock_gating(struct intel_display *display) } } -void intel_display_disable_trickle_feed(struct intel_display *display) +static void intel_display_disable_trickle_feed(struct intel_display *display) { enum pipe pipe; @@ -191,7 +198,7 @@ void intel_display_disable_trickle_feed(struct intel_display *display) } } -void intel_display_ilk_init_clock_gating(struct intel_display *display) +static void intel_display_ilk_init_clock_gating(struct intel_display *display) { u32 dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; @@ -223,7 +230,7 @@ void intel_display_ilk_init_clock_gating(struct intel_display *display) intel_display_disable_trickle_feed(display); } -void intel_display_gen6_init_clock_gating(struct intel_display *display) +static void intel_display_gen6_init_clock_gating(struct intel_display *display) { u32 dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; @@ -244,13 +251,15 @@ void intel_display_gen6_init_clock_gating(struct intel_display *display) intel_display_disable_trickle_feed(display); } -void intel_display_ivb_init_clock_gating(struct intel_display *display) +static void intel_display_ivb_init_clock_gating(struct intel_display *display) { intel_de_write(display, ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); intel_de_rmw(display, ILK_DISPLAY_CHICKEN1, 0, ILK_FBCQ_DIS); + + intel_display_disable_trickle_feed(display); } -void intel_display_g4x_init_clock_gating(struct intel_display *display) +static void intel_display_g4x_init_clock_gating(struct intel_display *display) { u32 dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE | OVRUNIT_CLOCK_GATE_DISABLE | @@ -264,7 +273,7 @@ void intel_display_g4x_init_clock_gating(struct intel_display *display) intel_display_disable_trickle_feed(display); } -void intel_display_i965gm_init_clock_gating(struct intel_display *display) +static void intel_display_i965gm_init_clock_gating(struct intel_display *display) { intel_de_write(display, DSPCLK_GATE_D, 0); } @@ -281,4 +290,18 @@ void intel_display_init_clock_gating(struct intel_display *display) intel_display_bxt_init_clock_gating(display); else if (display->platform.geminilake) intel_display_glk_init_clock_gating(display); + else if (display->platform.broadwell) + intel_display_bdw_init_clock_gating(display); + else if (display->platform.haswell) + intel_display_hsw_init_clock_gating(display); + else if (display->platform.ivybridge) + intel_display_ivb_init_clock_gating(display); + else if (display->platform.sandybridge) + intel_display_gen6_init_clock_gating(display); + else if (display->platform.ironlake) + intel_display_ilk_init_clock_gating(display); + else if (display->platform.g4x) + intel_display_g4x_init_clock_gating(display); + else if (display->platform.i965gm) + intel_display_i965gm_init_clock_gating(display); } diff --git a/drivers/gpu/drm/i915/display/intel_display_clock_gating.h b/drivers/gpu/drm/i915/display/intel_display_clock_gating.h index 4e50e388656a..dbfa5892cffe 100644 --- a/drivers/gpu/drm/i915/display/intel_display_clock_gating.h +++ b/drivers/gpu/drm/i915/display/intel_display_clock_gating.h @@ -9,15 +9,5 @@ struct intel_display; void intel_display_init_clock_gating(struct intel_display *display); -void intel_display_bdw_clock_gating_disable_fbcq(struct intel_display *display); -void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display); -void intel_display_bdw_clock_gating_kvm_notif(struct intel_display *display); -void intel_display_hsw_init_clock_gating(struct intel_display *display); -void intel_display_disable_trickle_feed(struct intel_display *display); -void intel_display_ilk_init_clock_gating(struct intel_display *display); -void intel_display_gen6_init_clock_gating(struct intel_display *display); -void intel_display_ivb_init_clock_gating(struct intel_display *display); -void intel_display_g4x_init_clock_gating(struct intel_display *display); -void intel_display_i965gm_init_clock_gating(struct intel_display *display); #endif /* __INTEL_DISPLAY_CLOCK_GATING_H__ */ diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 84f838c660fd..c5c4441f3a61 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -73,7 +73,7 @@ static void ilk_init_clock_gating(struct drm_i915_private *i915) intel_uncore_write(&i915->uncore, PCH_3DCGDIS1, VFMUNIT_CLOCK_GATE_DISABLE); - intel_display_ilk_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); intel_pch_init_clock_gating(i915->display); } @@ -90,7 +90,7 @@ static void gen6_check_mch_setup(struct drm_i915_private *i915) static void gen6_init_clock_gating(struct drm_i915_private *i915) { - intel_display_gen6_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); intel_uncore_write(&i915->uncore, GEN6_UCGCTL1, intel_uncore_read(&i915->uncore, GEN6_UCGCTL1) | @@ -205,13 +205,11 @@ static void skl_init_clock_gating(struct drm_i915_private *i915) static void bdw_init_clock_gating(struct drm_i915_private *i915) { - intel_display_bdw_clock_gating_disable_fbcq(i915->display); + intel_display_init_clock_gating(i915->display); /* WaSwitchSolVfFArbitrationPriority:bdw */ intel_uncore_rmw(&i915->uncore, GAM_ECOCHK, 0, HSW_ECOCHK_ARB_PRIO_SOL); - intel_display_bdw_clock_gating_vblank_in_srd(i915->display); - /* WaVSRefCountFullforceMissDisable:bdw */ /* WaDSRefCountFullforceMissDisable:bdw */ intel_uncore_rmw(&i915->uncore, GEN7_FF_THREAD_MODE, @@ -226,8 +224,6 @@ static void bdw_init_clock_gating(struct drm_i915_private *i915) /* WaProgramL3SqcReg1Default:bdw */ gen8_set_l3sqc_credits(i915, 30, 2); - intel_display_bdw_clock_gating_kvm_notif(i915->display); - intel_pch_init_clock_gating(i915->display); /* WaDisableDopClockGating:bdw @@ -240,7 +236,7 @@ static void bdw_init_clock_gating(struct drm_i915_private *i915) static void hsw_init_clock_gating(struct drm_i915_private *i915) { - intel_display_hsw_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); /* This is required by WaCatErrorRejectionIssue:hsw */ intel_uncore_rmw(&i915->uncore, GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, @@ -256,7 +252,7 @@ static void ivb_init_clock_gating(struct drm_i915_private *i915) { struct intel_display *display = i915->display; - intel_display_ivb_init_clock_gating(display); + intel_display_init_clock_gating(display); /* WaDisableBackToBackFlipFix:ivb */ intel_uncore_write(&i915->uncore, IVB_CHICKEN3, @@ -285,8 +281,6 @@ static void ivb_init_clock_gating(struct drm_i915_private *i915) intel_uncore_rmw(&i915->uncore, GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, 0, GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - intel_display_disable_trickle_feed(display); - intel_uncore_rmw(&i915->uncore, GEN6_MBCUNIT_SNPCR, GEN6_MBC_SNPCR_MASK, GEN6_MBC_SNPCR_MED); @@ -362,7 +356,7 @@ static void g4x_init_clock_gating(struct drm_i915_private *i915) GS_UNIT_CLOCK_GATE_DISABLE | CL_UNIT_CLOCK_GATE_DISABLE); intel_uncore_write(&i915->uncore, RAMCLK_GATE_D, 0); - intel_display_g4x_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void i965gm_init_clock_gating(struct drm_i915_private *i915) @@ -371,7 +365,7 @@ static void i965gm_init_clock_gating(struct drm_i915_private *i915) intel_uncore_write(uncore, RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE); intel_uncore_write(uncore, RENCLK_GATE_D2, 0); - intel_display_i965gm_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); intel_uncore_write(uncore, RAMCLK_GATE_D, 0); intel_uncore_write16(uncore, DEUC, 0); intel_uncore_write(uncore, From 442be3b2a72d0c1f6fe41c2925375e44fff1b9c1 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 17 Aug 2026 12:26:22 +0300 Subject: [PATCH 61/72] drm/i915/display: remove unnecessary clock_gating include from intel_display.c We don't use any of the functions inclued in intel_clock_gating.h in the intel_display.c code anymore. Remove the respective include. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260817092911.342072-4-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_display.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 553c60d452dd..fc30a455bed3 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -60,7 +60,6 @@ #include "intel_bo.h" #include "intel_bw.h" #include "intel_cdclk.h" -#include "intel_clock_gating.h" #include "intel_cmtg.h" #include "intel_color.h" #include "intel_crt.h" From 3802cbc4b41c64500a11ba6ea8f36d42b9736989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grzelak?= Date: Fri, 28 Aug 2026 21:09:55 +0200 Subject: [PATCH 62/72] drm/i915/pcode: rename GEN6_PCODE_DATA to match spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable naming of val and val1 should match register macro naming. Historically, corresponding macros were named GEN6_PCODE_DATA and GEN6_PCODE_DATA1. Rename register macro GEN6_PCODE_DATA to GEN6_PCODE_DATA0 since spec appended '0' to the register name. Rename val into val0 in functions declaring val1 for both i915-specific and shared display code. v4->v5 - clean changelog's versioning v3->v4 - split i915 changes from xe (Jani) - apply the rename to the read()'s signature as well (Sashiko) v2->v3 - resolve conflict from rebase v1->v2 - rebase onto drm-tip (Jani) Bspec: 19978 Suggested-by: Jani Nikula Signed-off-by: Michał Grzelak Link: https://patch.msgid.link/20260828190956.2622350-2-michal.grzelak@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_parent.c | 4 ++-- drivers/gpu/drm/i915/display/intel_parent.h | 2 +- drivers/gpu/drm/i915/gvt/handlers.c | 2 +- drivers/gpu/drm/i915/i915_reg.h | 2 +- drivers/gpu/drm/i915/intel_gvt_mmio_table.c | 2 +- drivers/gpu/drm/i915/intel_pcode.c | 14 +++++++------- drivers/gpu/drm/i915/intel_pcode.h | 2 +- include/drm/intel/display_parent_interface.h | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index ffc1f5e0518c..99cebb17763a 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -281,9 +281,9 @@ void intel_parent_pc8_unblock(struct intel_display *display) } /* pcode */ -int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val, u32 *val1) +int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val0, u32 *val1) { - return display->parent->pcode->read(display->drm, mbox, val, val1); + return display->parent->pcode->read(display->drm, mbox, val0, val1); } int intel_parent_pcode_write_timeout(struct intel_display *display, u32 mbox, u32 val, int timeout_ms) diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index c817a826169c..cc4a58f63166 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -115,7 +115,7 @@ void intel_parent_pc8_block(struct intel_display *display); void intel_parent_pc8_unblock(struct intel_display *display); /* pcode */ -int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val, u32 *val1); +int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val0, u32 *val1); int intel_parent_pcode_write_timeout(struct intel_display *display, u32 mbox, u32 val, int timeout_ms); int intel_parent_pcode_write(struct intel_display *display, u32 mbox, u32 val); int intel_parent_pcode_request(struct intel_display *display, u32 mbox, u32 request, diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 9f61867e2478..22dd0179863e 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1706,7 +1706,7 @@ static int mailbox_write(struct intel_vgpu *vgpu, unsigned int offset, { u32 value = *(u32 *)p_data; u32 cmd = value & 0xff; - u32 *data0 = &vgpu_vreg_t(vgpu, GEN6_PCODE_DATA); + u32 *data0 = &vgpu_vreg_t(vgpu, GEN6_PCODE_DATA0); switch (cmd) { case GEN9_PCODE_READ_MEM_LATENCY: diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 64e906380131..d873072b7a92 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -697,7 +697,7 @@ #define EDRAM_WAYS_IDX(cap) (((cap) >> 5) & 0x7) #define EDRAM_SETS_IDX(cap) (((cap) >> 8) & 0x3) -#define GEN6_PCODE_DATA _MMIO(0x138128) +#define GEN6_PCODE_DATA0 _MMIO(0x138128) #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 #define GEN6_PCODE_FREQ_RING_RATIO_SHIFT 16 #define GEN6_PCODE_DATA1 _MMIO(0x13812C) diff --git a/drivers/gpu/drm/i915/intel_gvt_mmio_table.c b/drivers/gpu/drm/i915/intel_gvt_mmio_table.c index de118fae0a49..25b72b20e709 100644 --- a/drivers/gpu/drm/i915/intel_gvt_mmio_table.c +++ b/drivers/gpu/drm/i915/intel_gvt_mmio_table.c @@ -611,7 +611,7 @@ static int iterate_generic_mmio(struct intel_gvt_mmio_table_iter *iter) MMIO_D(GEN6_UCGCTL1); MMIO_D(GEN6_UCGCTL2); MMIO_F(_MMIO(0x4f000), 0x90); - MMIO_D(GEN6_PCODE_DATA); + MMIO_D(GEN6_PCODE_DATA0); MMIO_D(_MMIO(0x13812c)); MMIO_D(GEN7_ERR_INT); MMIO_D(HSW_EDRAM_CAP); diff --git a/drivers/gpu/drm/i915/intel_pcode.c b/drivers/gpu/drm/i915/intel_pcode.c index c07d48fc1b35..1c9d1f413757 100644 --- a/drivers/gpu/drm/i915/intel_pcode.c +++ b/drivers/gpu/drm/i915/intel_pcode.c @@ -58,7 +58,7 @@ static int gen7_check_mailbox_status(u32 mbox) } static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, - u32 *val, u32 *val1, + u32 *val0, u32 *val1, int fast_timeout_us, int slow_timeout_ms, bool is_read) { @@ -73,7 +73,7 @@ static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, if (intel_uncore_read_fw(uncore, GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) return -EAGAIN; - intel_uncore_write_fw(uncore, GEN6_PCODE_DATA, *val); + intel_uncore_write_fw(uncore, GEN6_PCODE_DATA0, *val0); intel_uncore_write_fw(uncore, GEN6_PCODE_DATA1, val1 ? *val1 : 0); intel_uncore_write_fw(uncore, GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox); @@ -87,7 +87,7 @@ static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, return -ETIMEDOUT; if (is_read) - *val = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA); + *val0 = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA0); if (is_read && val1) *val1 = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA1); @@ -97,12 +97,12 @@ static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, return gen6_check_mailbox_status(mbox); } -int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val, u32 *val1) +int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val0, u32 *val1) { int err; mutex_lock(&uncore->i915->sb_lock); - err = __snb_pcode_rw(uncore, mbox, val, val1, 500, 20, true); + err = __snb_pcode_rw(uncore, mbox, val0, val1, 500, 20, true); mutex_unlock(&uncore->i915->sb_lock); if (err) { @@ -278,11 +278,11 @@ int snb_pcode_write_p(struct intel_uncore *uncore, u32 mbcmd, u32 p1, u32 p2, u3 return err; } -static int intel_pcode_read(struct drm_device *drm, u32 mbox, u32 *val, u32 *val1) +static int intel_pcode_read(struct drm_device *drm, u32 mbox, u32 *val0, u32 *val1) { struct drm_i915_private *i915 = to_i915(drm); - return snb_pcode_read(&i915->uncore, mbox, val, val1); + return snb_pcode_read(&i915->uncore, mbox, val0, val1); } static int intel_pcode_write_timeout(struct drm_device *drm, u32 mbox, u32 val, int timeout_ms) diff --git a/drivers/gpu/drm/i915/intel_pcode.h b/drivers/gpu/drm/i915/intel_pcode.h index 19795ea8172e..deecc64bc1b3 100644 --- a/drivers/gpu/drm/i915/intel_pcode.h +++ b/drivers/gpu/drm/i915/intel_pcode.h @@ -11,7 +11,7 @@ struct drm_device; struct intel_uncore; -int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val, u32 *val1); +int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val0, u32 *val1); int snb_pcode_write_timeout(struct intel_uncore *uncore, u32 mbox, u32 val, int timeout_ms); #define snb_pcode_write(uncore, mbox, val) \ snb_pcode_write_timeout((uncore), (mbox), (val), 1) diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index d7d06dcaa699..5e44c022d1ae 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -179,7 +179,7 @@ struct intel_display_pc8_interface { }; struct intel_display_pcode_interface { - int (*read)(struct drm_device *drm, u32 mbox, u32 *val, u32 *val1); + int (*read)(struct drm_device *drm, u32 mbox, u32 *val0, u32 *val1); int (*write)(struct drm_device *drm, u32 mbox, u32 val, int timeout_ms); int (*request)(struct drm_device *drm, u32 mbox, u32 request, u32 reply_mask, u32 reply, int timeout_base_ms); From b37921c9f533ca936c5b5a484c1299680c570a7e Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Tue, 25 Aug 2026 13:02:04 +0530 Subject: [PATCH 63/72] drm/i915/dp: Gate UHBR SST SDP splitting on sink capability SDP splitting for 128b/132b (UHBR) SST audio must only be enabled when the sink advertises support for it. Previously sdp_split_enable was set for every UHBR SST stream carrying audio, regardless of sink capability. In MST mode SDP splitting is inherently supported, so the sink capability check (DP_SST_SPLIT_SDP_CAP) is applied only to the SST path. Fixes: 8853750dbad8 ("drm/i915: Enable SDP split for DP2.0") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Mitul Golani Reviewed-by: Suraj Kandpal Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260825073204.872441-1-mitulkumar.ajitkumar.golani@intel.com --- .../drm/i915/display/intel_display_types.h | 2 + drivers/gpu/drm/i915/display/intel_dp.c | 44 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 20a07ea06b5e..5f0fe18c0614 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1951,6 +1951,8 @@ struct intel_dp { bool colorimetry_support; + bool sst_split_sdp_support; + struct { enum transcoder transcoder; struct mutex lock; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 1e0dfc4333ef..c46397edf587 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3370,12 +3370,22 @@ intel_dp_audio_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + pipe_config->has_audio = intel_dp_has_audio(encoder, conn_state) && intel_audio_compute_config(encoder, pipe_config, conn_state); pipe_config->sdp_split_enable = pipe_config->has_audio && intel_dp_is_uhbr(pipe_config); + + /* + * SDP splitting for UHBR audio requires explicit sink capability in + * SST mode, whereas in MST mode it is inherently supported. + */ + if (pipe_config->sdp_split_enable && + !intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) + pipe_config->sdp_split_enable = intel_dp->sst_split_sdp_support; } void @@ -4417,16 +4427,25 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, str_enable_disable(tmp)); } -static bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp) +static u8 intel_dp_read_dprx_feature_enum(struct intel_dp *intel_dp) { u8 dprx = 0; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST, - &dprx) != 1) - return false; + drm_dp_dpcd_read_data(&intel_dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST, + &dprx, sizeof(dprx)); + return dprx; +} + +static bool intel_dp_get_colorimetry_status(u8 dprx) +{ return dprx & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED; } +static bool intel_dp_get_sst_split_sdp_status(u8 dprx) +{ + return dprx & DP_SST_SPLIT_SDP_CAP; +} + static int intel_dp_read_dsc_dpcd(struct drm_dp_aux *aux, u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) { @@ -4726,6 +4745,7 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector { struct intel_display *display = to_intel_display(intel_dp); int ret; + u8 dprx; /* this function is meant to be called only once */ drm_WARN_ON(display->drm, intel_dp->dpcd[DP_DPCD_REV] != 0); @@ -4737,8 +4757,13 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector drm_dp_is_branch(intel_dp->dpcd)); intel_init_dpcd_quirks(intel_dp, &intel_dp->desc.ident); + dprx = intel_dp_read_dprx_feature_enum(intel_dp); + intel_dp->colorimetry_support = - intel_dp_get_colorimetry_status(intel_dp); + intel_dp_get_colorimetry_status(dprx); + + intel_dp->sst_split_sdp_support = + intel_dp_get_sst_split_sdp_status(dprx); /* * Read the eDP display control registers. @@ -4829,13 +4854,20 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) * the OUI/ID since we know it won't change. */ if (!intel_dp_is_edp(intel_dp)) { + u8 dprx; + drm_dp_read_desc(&intel_dp->aux, &intel_dp->desc, drm_dp_is_branch(intel_dp->dpcd)); intel_init_dpcd_quirks(intel_dp, &intel_dp->desc.ident); + dprx = intel_dp_read_dprx_feature_enum(intel_dp); + intel_dp->colorimetry_support = - intel_dp_get_colorimetry_status(intel_dp); + intel_dp_get_colorimetry_status(dprx); + + intel_dp->sst_split_sdp_support = + intel_dp_get_sst_split_sdp_status(dprx); intel_dp_update_sink_caps(intel_dp); } From 13ab1c34d3908ffedfa2946b292d6df3feade36c Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 1 Sep 2026 12:58:09 +0530 Subject: [PATCH 64/72] drm/i915/cursor: Check joiner cursor commit status In joiner mode, secondary cursor commits may still be running even when the primary cursor commit is done. Walking the secondary pipes also requires holding the secondary planes modeset locks. Add intel_cursor_lock_joined_planes() to acquire modeset locks for all secondary cursor planes. Check all joined cursor commit status before taking the fast path. If any commit is still pending, fallback to slow path. v2: Use intel_crtc_joined_pipe_mask(). [Ville] v3: Lock secondary cursor CRTCs and planes. [sashiko] v4: Iterate the full joined mask uniformly in both helpers, no primary special-case. Move the parameter-change check above the lock acquisition so we don't grab secondary locks just to fall to slow path. [Chaitanya] v5: Remove extra header declaration. [Chaitanya] Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Nemesa Garg Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260901072813.2596383-2-nemesa.garg@intel.com --- drivers/gpu/drm/i915/display/intel_cursor.c | 68 ++++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 86bb96ac449b..47ddb213e547 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -801,6 +801,50 @@ void intel_cursor_unpin_work(struct kthread_work *base) intel_plane_destroy_state(&plane->base, &plane_state->uapi); } +static int intel_cursor_lock_joined_planes(struct intel_display *display, + const struct intel_crtc_state *crtc_state, + struct drm_modeset_acquire_ctx *ctx) +{ + struct intel_crtc *pipe_crtc; + int ret; + + for_each_intel_crtc_in_pipe_mask(display, pipe_crtc, + intel_crtc_joined_pipe_mask(crtc_state)) { + struct intel_plane *pipe_plane = + intel_crtc_get_plane(pipe_crtc, PLANE_CURSOR); + + ret = drm_modeset_lock(&pipe_crtc->base.mutex, ctx); + if (ret) + return ret; + + ret = drm_modeset_lock(&pipe_plane->base.mutex, ctx); + if (ret) + return ret; + } + return 0; +} + +static bool +intel_cursor_joiner_commits_idle(struct intel_display *display, + const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *pipe_crtc; + + for_each_intel_crtc_in_pipe_mask(display, pipe_crtc, + intel_crtc_joined_pipe_mask(crtc_state)) { + struct intel_plane *pipe_plane = + intel_crtc_get_plane(pipe_crtc, PLANE_CURSOR); + struct intel_plane_state *pipe_plane_state = + to_intel_plane_state(pipe_plane->base.state); + + if (pipe_plane_state->uapi.commit && + !try_wait_for_completion(&pipe_plane_state->uapi.commit->hw_done)) + return false; + } + + return true; +} + static int intel_legacy_cursor_update(struct drm_plane *_plane, struct drm_crtc *_crtc, @@ -838,15 +882,6 @@ intel_legacy_cursor_update(struct drm_plane *_plane, crtc_state->joiner_pipes) goto slow; - /* - * Don't do an async update if there is an outstanding commit modifying - * the plane. This prevents our async update's changes from getting - * overridden by a previous synchronous update's state. - */ - if (old_plane_state->uapi.commit && - !try_wait_for_completion(&old_plane_state->uapi.commit->hw_done)) - goto slow; - /* * If any parameters change that may affect watermarks, * take the slowpath. Only changing fb or position should be @@ -860,6 +895,21 @@ intel_legacy_cursor_update(struct drm_plane *_plane, !old_plane_state->uapi.fb != !fb) goto slow; + ret = intel_cursor_lock_joined_planes(display, crtc_state, ctx); + if (ret == -EDEADLK) + return ret; + if (ret) + goto slow; + + /* + * Don't do an async update if there is an outstanding commit modifying + * any of the joined cursor planes. This prevents our async update's + * changes from getting overridden by a previous synchronous update's + * state. + */ + if (!intel_cursor_joiner_commits_idle(display, crtc_state)) + goto slow; + new_plane_state = to_intel_plane_state(intel_plane_duplicate_state(&plane->base)); if (!new_plane_state) return -ENOMEM; From 5f6aeadf349527ff5bd171f4c8a878ca9f1d9279 Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 1 Sep 2026 12:58:10 +0530 Subject: [PATCH 65/72] drm/i915/cursor: Add helper to update cursor plane Move cursor fast path plane state update into helper function. The target hw.crtc is passed as a parameter so a later patch can reuse the helper for joiner secondary pipes, where hw.crtc points at the secondary being programmed while uapi.crtc stays on the primary. v4: Drop uapi.crtc assignment from the helper. [Chaitanya] Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Nemesa Garg Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260901072813.2596383-3-nemesa.garg@intel.com --- drivers/gpu/drm/i915/display/intel_cursor.c | 39 ++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 47ddb213e547..b57b446b9788 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -845,6 +845,29 @@ intel_cursor_joiner_commits_idle(struct intel_display *display, return true; } +static void +intel_cursor_fastpath_update_plane_state(struct intel_plane_state *plane_state, + struct drm_framebuffer *fb, + struct intel_crtc *hw_crtc, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + u32 src_x, u32 src_y, + u32 src_w, u32 src_h) +{ + drm_atomic_set_fb_for_plane(&plane_state->uapi, fb); + + plane_state->uapi.src_x = src_x; + plane_state->uapi.src_y = src_y; + plane_state->uapi.src_w = src_w; + plane_state->uapi.src_h = src_h; + plane_state->uapi.crtc_x = crtc_x; + plane_state->uapi.crtc_y = crtc_y; + plane_state->uapi.crtc_w = crtc_w; + plane_state->uapi.crtc_h = crtc_h; + + intel_plane_copy_uapi_to_hw_state(NULL, plane_state, plane_state, hw_crtc); +} + static int intel_legacy_cursor_update(struct drm_plane *_plane, struct drm_crtc *_crtc, @@ -920,18 +943,10 @@ intel_legacy_cursor_update(struct drm_plane *_plane, goto out_free; } - drm_atomic_set_fb_for_plane(&new_plane_state->uapi, fb); - - new_plane_state->uapi.src_x = src_x; - new_plane_state->uapi.src_y = src_y; - new_plane_state->uapi.src_w = src_w; - new_plane_state->uapi.src_h = src_h; - new_plane_state->uapi.crtc_x = crtc_x; - new_plane_state->uapi.crtc_y = crtc_y; - new_plane_state->uapi.crtc_w = crtc_w; - new_plane_state->uapi.crtc_h = crtc_h; - - intel_plane_copy_uapi_to_hw_state(NULL, new_plane_state, new_plane_state, crtc); + intel_cursor_fastpath_update_plane_state(new_plane_state, fb, + crtc, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state, old_plane_state, new_plane_state); From 0ea1a71705cd674e9701571adac58a1ac0cc6e3b Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 1 Sep 2026 12:58:11 +0530 Subject: [PATCH 66/72] drm/i915/cursor: Handle secondary cursor state In joiner mode the fast path cursor update must handle secondary pipes. Iterate over all joined pipes uniformly to duplicate plane state, run check_plane(), pin the framebuffer and on success swap in the new plane state for each secondary cursor. Track every successfully prepared pipe in a joined_pipe_state[] array of struct intel_cursor_joiner_state so that later frontbuffer, unpin and error-cleanup paths treat primary and secondaries uniformly, and ensures the primary's pinned framebuffer is released if a secondary fails partway through. Convert the primary-only vblank unpin block into a loop over the joined_pipe_state[] array so each pipe's old cursor framebuffer is scheduled for unpin (or unpinned inline when unchanged) independently. All unpin work is armed on the primary crtc's vblank (&crtc->base), consistent with the single primary vblank evasion used above. v2: Use intel_crtc_joined_pipe_mask(). [Ville] Add locking mechanism. [Ville] v3: Drop the per-pipe fastpath mutex array. [sashiko] v4: Fold parallel arrays into struct intel_cursor_joiner_state joined_pipe_state[]. Unify primary/secondary in a single loop. Use bare check_plane(). [Chaitanya] v5: Fix secondary uapi.crtc to each pipe's own crtc. Rename intel_cursor_pipe to intel_cursor_joiner_state and joined[] to joined_pipe_state[]. [Chaitanya] v6: Mirror primary uapi into secondary hw state, don't set uapi.crtc. [sashiko] v7: Squash the per-pipe cursor unpin scheduling into this patch.[Chaitanya] v8: Resolve checkpatch errors. Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Nemesa Garg Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260901072813.2596383-4-nemesa.garg@intel.com --- drivers/gpu/drm/i915/display/intel_cursor.c | 186 +++++++++++++------- 1 file changed, 127 insertions(+), 59 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index b57b446b9788..b4f0f70eaa1a 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -847,6 +847,7 @@ intel_cursor_joiner_commits_idle(struct intel_display *display, static void intel_cursor_fastpath_update_plane_state(struct intel_plane_state *plane_state, + const struct intel_plane_state *from_plane_state, struct drm_framebuffer *fb, struct intel_crtc *hw_crtc, int crtc_x, int crtc_y, @@ -854,20 +855,34 @@ intel_cursor_fastpath_update_plane_state(struct intel_plane_state *plane_state, u32 src_x, u32 src_y, u32 src_w, u32 src_h) { - drm_atomic_set_fb_for_plane(&plane_state->uapi, fb); + /* + * Only the primary owns its uapi state; a secondary mirrors it, so + * its uapi.crtc/fb stay NULL and hw.crtc comes from hw_crtc. + */ + if (plane_state == from_plane_state) { + drm_atomic_set_fb_for_plane(&plane_state->uapi, fb); - plane_state->uapi.src_x = src_x; - plane_state->uapi.src_y = src_y; - plane_state->uapi.src_w = src_w; - plane_state->uapi.src_h = src_h; - plane_state->uapi.crtc_x = crtc_x; - plane_state->uapi.crtc_y = crtc_y; - plane_state->uapi.crtc_w = crtc_w; - plane_state->uapi.crtc_h = crtc_h; + plane_state->uapi.src_x = src_x; + plane_state->uapi.src_y = src_y; + plane_state->uapi.src_w = src_w; + plane_state->uapi.src_h = src_h; + plane_state->uapi.crtc_x = crtc_x; + plane_state->uapi.crtc_y = crtc_y; + plane_state->uapi.crtc_w = crtc_w; + plane_state->uapi.crtc_h = crtc_h; + } - intel_plane_copy_uapi_to_hw_state(NULL, plane_state, plane_state, hw_crtc); + intel_plane_copy_uapi_to_hw_state(NULL, plane_state, from_plane_state, hw_crtc); } +struct intel_cursor_joiner_state { + struct intel_plane *plane; + struct intel_crtc *crtc; + struct intel_crtc_state *crtc_state; + struct intel_plane_state *old_plane_state; + struct intel_plane_state *new_plane_state; +}; + static int intel_legacy_cursor_update(struct drm_plane *_plane, struct drm_crtc *_crtc, @@ -883,11 +898,12 @@ intel_legacy_cursor_update(struct drm_plane *_plane, struct intel_display *display = to_intel_display(plane); struct intel_plane_state *old_plane_state = to_intel_plane_state(plane->base.state); - struct intel_plane_state *new_plane_state; struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); - struct intel_crtc_state *new_crtc_state; struct intel_vblank_evade_ctx evade; + struct intel_cursor_joiner_state joined_pipe_state[I915_MAX_PIPES] = {}; + struct intel_crtc *pipe_crtc; + int num_pipes = 0; int ret; /* @@ -933,38 +949,74 @@ intel_legacy_cursor_update(struct drm_plane *_plane, if (!intel_cursor_joiner_commits_idle(display, crtc_state)) goto slow; - new_plane_state = to_intel_plane_state(intel_plane_duplicate_state(&plane->base)); - if (!new_plane_state) - return -ENOMEM; + /* + * Iterate over all joined pipes (primary and secondary) uniformly. + * The joined pipe mask includes both the primary pipe and all + * secondary joiner pipes, allowing us to handle them all the same way. + */ + for_each_intel_crtc_in_pipe_mask(display, pipe_crtc, + intel_crtc_joined_pipe_mask(crtc_state)) { + struct intel_cursor_joiner_state *j = &joined_pipe_state[num_pipes]; - new_crtc_state = to_intel_crtc_state(intel_crtc_duplicate_state(&crtc->base)); - if (!new_crtc_state) { - ret = -ENOMEM; - goto out_free; + j->plane = intel_crtc_get_plane(pipe_crtc, PLANE_CURSOR); + j->crtc = pipe_crtc; + j->crtc_state = to_intel_crtc_state(pipe_crtc->base.state); + j->old_plane_state = to_intel_plane_state(j->plane->base.state); + j->new_plane_state = + to_intel_plane_state(intel_plane_duplicate_state(&j->plane->base)); + + if (!j->new_plane_state) { + ret = -ENOMEM; + goto out_free; + } + + intel_cursor_fastpath_update_plane_state(j->new_plane_state, + joined_pipe_state[0].new_plane_state, + fb, pipe_crtc, + crtc_x, crtc_y, + crtc_w, crtc_h, + src_x, src_y, + src_w, src_h); + + ret = j->plane->check_plane(j->crtc_state, j->new_plane_state); + if (ret) { + intel_plane_destroy_state(&j->plane->base, + &j->new_plane_state->uapi); + goto out_free; + } + + ret = intel_plane_pin_fb(j->new_plane_state, j->old_plane_state); + if (ret) { + intel_plane_destroy_state(&j->plane->base, + &j->new_plane_state->uapi); + goto out_free; + } + + num_pipes++; } - intel_cursor_fastpath_update_plane_state(new_plane_state, fb, - crtc, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - - ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state, - old_plane_state, new_plane_state); - if (ret) - goto out_free; - - ret = intel_plane_pin_fb(new_plane_state, old_plane_state); - if (ret) - goto out_free; - - intel_frontbuffer_flush(to_intel_frontbuffer(new_plane_state->hw.fb), + intel_frontbuffer_flush(to_intel_frontbuffer(joined_pipe_state[0].new_plane_state->hw.fb), ORIGIN_CURSOR_UPDATE); - intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->hw.fb), - to_intel_frontbuffer(new_plane_state->hw.fb), - plane->frontbuffer_bit); - /* Swap plane state */ - plane->base.state = &new_plane_state->uapi; + for (int i = 0; i < num_pipes; i++) { + struct intel_frontbuffer *old_front = + to_intel_frontbuffer(joined_pipe_state[i].old_plane_state->hw.fb); + struct intel_frontbuffer *new_front = + to_intel_frontbuffer(joined_pipe_state[i].new_plane_state->hw.fb); + + intel_frontbuffer_track(old_front, new_front, + joined_pipe_state[i].plane->frontbuffer_bit); + } + + for (int i = 0; i < num_pipes; i++) { + joined_pipe_state[i].plane->base.state = + &joined_pipe_state[i].new_plane_state->uapi; + + if (joined_pipe_state[i].new_plane_state->uapi.visible) + joined_pipe_state[i].crtc_state->active_planes |= BIT(PLANE_CURSOR); + else + joined_pipe_state[i].crtc_state->active_planes &= ~BIT(PLANE_CURSOR); + } /* * We cannot swap crtc_state as it may be in use by an atomic commit or @@ -976,7 +1028,6 @@ intel_legacy_cursor_update(struct drm_plane *_plane, * planes atomically. If the cursor was part of the atomic update then * we would have taken the slowpath. */ - crtc_state->active_planes = new_crtc_state->active_planes; intel_vblank_evade_init(crtc_state, crtc_state, &evade); @@ -998,9 +1049,11 @@ intel_legacy_cursor_update(struct drm_plane *_plane, local_irq_disable(); } - if (new_plane_state->uapi.visible) { - intel_plane_update_noarm(NULL, plane, crtc_state, new_plane_state); - intel_plane_update_arm(NULL, plane, crtc_state, new_plane_state); + if (joined_pipe_state[0].new_plane_state->uapi.visible) { + intel_plane_update_noarm(NULL, plane, crtc_state, + joined_pipe_state[0].new_plane_state); + intel_plane_update_arm(NULL, plane, crtc_state, + joined_pipe_state[0].new_plane_state); } else { intel_plane_disable_arm(NULL, plane, crtc_state); } @@ -1009,26 +1062,41 @@ intel_legacy_cursor_update(struct drm_plane *_plane, intel_psr_unlock(crtc_state); - if (old_plane_state->ggtt_vma != new_plane_state->ggtt_vma) { - drm_vblank_work_init(&old_plane_state->unpin_work, &crtc->base, - intel_cursor_unpin_work); + /* + * Schedule or immediately unpin old framebuffers. + * Protect against concurrent access. + */ + for (int i = 0; i < num_pipes; i++) { + struct intel_plane_state *old = joined_pipe_state[i].old_plane_state; - drm_vblank_work_schedule(&old_plane_state->unpin_work, - drm_crtc_accurate_vblank_count(&crtc->base) + 1, - false); - - old_plane_state = NULL; - } else { - intel_plane_unpin_fb(old_plane_state); + if (old->ggtt_vma != joined_pipe_state[i].new_plane_state->ggtt_vma) { + drm_vblank_work_init(&old->unpin_work, &crtc->base, + intel_cursor_unpin_work); + drm_vblank_work_schedule(&old->unpin_work, + drm_crtc_accurate_vblank_count(&crtc->base) + 1, + false); + joined_pipe_state[i].old_plane_state = NULL; + } else { + intel_plane_unpin_fb(old); + } } out_free: - if (new_crtc_state) - intel_crtc_destroy_state(&crtc->base, &new_crtc_state->uapi); - if (ret) - intel_plane_destroy_state(&plane->base, &new_plane_state->uapi); - else if (old_plane_state) - intel_plane_destroy_state(&plane->base, &old_plane_state->uapi); + if (ret) { + for (int i = 0; i < num_pipes; i++) { + intel_plane_unpin_fb(joined_pipe_state[i].new_plane_state); + intel_plane_destroy_state(&joined_pipe_state[i].plane->base, + &joined_pipe_state[i].new_plane_state->uapi); + } + } else { + for (int i = 0; i < num_pipes; i++) { + if (!joined_pipe_state[i].old_plane_state) + continue; + + intel_plane_destroy_state(&joined_pipe_state[i].plane->base, + &joined_pipe_state[i].old_plane_state->uapi); + } + } return ret; slow: From a991f3bbb7518d2a0d920eff685ae0d7036709cb Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 1 Sep 2026 12:58:12 +0530 Subject: [PATCH 67/72] drm/i915/cursor: Program secondary cursor planes Iterate over all joined pipes when arming/disabling the cursor plane so secondary pipes are updated together with the primary. The pin, check and state duplication for secondary pipes was already prepared in the previous commit; this converts the update_arm pass to a loop over the joined_pipe_state[] array. Because the whole loop runs inside a single primary vblank-evade, sample intel_crtc_get_vblank_counter() per pipe around each arm and emit a drm_err() if the counter ticks during that pipe's own programming. v2: Check primary and secondary pipe together. [Ville] v3: Use struct intel_cursor_joiner_state. [Ville] v4: Add per-pipe vblank straddle detection around the arm loop. [Chaitanya] v5: Move straddle check outside the loop to cover all pipes together. Sample vblank counter from primary pipe only. Move drm_err() after local_irq_enable(). [Chaitanya] Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Nemesa Garg Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260901072813.2596383-5-nemesa.garg@intel.com --- drivers/gpu/drm/i915/display/intel_cursor.c | 36 +++++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index b4f0f70eaa1a..894bd1df5f42 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -13,6 +13,7 @@ #include #include "intel_atomic.h" +#include "intel_crtc.h" #include "intel_cursor.h" #include "intel_cursor_regs.h" #include "intel_de.h" @@ -904,6 +905,7 @@ intel_legacy_cursor_update(struct drm_plane *_plane, struct intel_cursor_joiner_state joined_pipe_state[I915_MAX_PIPES] = {}; struct intel_crtc *pipe_crtc; int num_pipes = 0; + u32 start_vbl_count, end_vbl_count; int ret; /* @@ -1049,17 +1051,37 @@ intel_legacy_cursor_update(struct drm_plane *_plane, local_irq_disable(); } - if (joined_pipe_state[0].new_plane_state->uapi.visible) { - intel_plane_update_noarm(NULL, plane, crtc_state, - joined_pipe_state[0].new_plane_state); - intel_plane_update_arm(NULL, plane, crtc_state, - joined_pipe_state[0].new_plane_state); - } else { - intel_plane_disable_arm(NULL, plane, crtc_state); + /* + * Joiner pipes are vblank-synchronized, so sampling only the primary + * pipe is sufficient to detect a straddle across all joined pipes. + * The vblank evasion above also operates on the primary pipe only. + */ + start_vbl_count = intel_crtc_get_vblank_counter(joined_pipe_state[0].crtc); + + for (int i = 0; i < num_pipes; i++) { + if (joined_pipe_state[i].new_plane_state->uapi.visible) { + intel_plane_update_noarm(NULL, joined_pipe_state[i].plane, + joined_pipe_state[i].crtc_state, + joined_pipe_state[i].new_plane_state); + intel_plane_update_arm(NULL, joined_pipe_state[i].plane, + joined_pipe_state[i].crtc_state, + joined_pipe_state[i].new_plane_state); + } else { + intel_plane_disable_arm(NULL, joined_pipe_state[i].plane, + joined_pipe_state[i].crtc_state); + } } + end_vbl_count = intel_crtc_get_vblank_counter(joined_pipe_state[0].crtc); + local_irq_enable(); + if (start_vbl_count != end_vbl_count) + drm_err(display->drm, + "Atomic update failure on pipe %c (start=%u end=%u)\n", + pipe_name(joined_pipe_state[0].crtc->pipe), + start_vbl_count, end_vbl_count); + intel_psr_unlock(crtc_state); /* From e101b8823294268977a53ecb564038b19f3af794 Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 1 Sep 2026 12:58:13 +0530 Subject: [PATCH 68/72] drm/i915/cursor: Allow joiner cursor fast path update The legacy cursor path forced all joiner updates to the slow path by checking joiner_pipes. Drop the condition so that joiner cursor updates can use the fast path. v4: Trimmed to only the joiner_pipes gate drop. [Chaitanya] Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Nemesa Garg Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260901072813.2596383-6-nemesa.garg@intel.com --- drivers/gpu/drm/i915/display/intel_cursor.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 894bd1df5f42..cce041e1da51 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -914,13 +914,10 @@ intel_legacy_cursor_update(struct drm_plane *_plane, * PSR2 selective fetch also requires the slow path as * PSR2 plane and transcoder registers can only be updated during * vblank. - * - * FIXME joiner fastpath would be good */ if (!crtc_state->hw.active || intel_crtc_needs_modeset(crtc_state) || - intel_crtc_needs_fastset(crtc_state) || - crtc_state->joiner_pipes) + intel_crtc_needs_fastset(crtc_state)) goto slow; /* From 3659b6d90f98050366fea480af27d93cfd3c6d33 Mon Sep 17 00:00:00 2001 From: Jake Steinman Date: Tue, 1 Sep 2026 16:59:56 -0400 Subject: [PATCH 69/72] drm/i915/psr: clear the Panel Replay error status register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit psr_get_status_and_error_status() selects the DPCD offset to read the error status from based on whether Panel Replay is enabled: offset = intel_dp->psr.panel_replay_enabled ? DP_PANEL_REPLAY_ERROR_STATUS : DP_PSR_ERROR_STATUS; but intel_psr_short_pulse() acknowledges it unconditionally to the PSR register. Under Panel Replay the error is therefore read from DP_PANEL_REPLAY_ERROR_STATUS (0x2020) and the acknowledgment written to DP_PSR_ERROR_STATUS (0x2006). DP_PANEL_REPLAY_ERROR_STATUS is never written anywhere in the tree; it appears only in the read above and in its own The sink's Panel Replay error latch can consequently never be cleared. Once it latches, every subsequent short pulse re-reads the same errors, so PSR is disabled with sink_not_reliable set permanently, and until a short pulse arrives the driver keeps Panel Replay enabled while the sink is reporting errors it cannot see. Observed on a Dell XPS 16 DA16260 (Panther Lake, Arc B390, display version 30.00) with the eDP Panel Replay quirk from commit cb8d155b0806 ("drm/i915/psr: Disable Panel Replay on Dell XPS 16 DA16260 as a quirk") removed locally so the feature could be exercised. The sink reports a persistently latched error: Sink PANEL-REPLAY status: 0x2 [active, display from RFB] Sink PANEL-REPLAY error status: 0x1: PANEL-REPLAY Link CRC error which survives across reads indefinitely, while dmesg stays silent and Panel Replay Selective Update remains enabled. Use the same conditional offset when clearing. psr->panel_replay_enabled cannot be used at that point because intel_psr_disable_locked() clears it earlier in the same function whenever an error was detected, which is exactly the case that needs the Panel Replay offset, so save it beforehand. v2: use a copy of panel_replay_enabled taken before intel_psr_disable_locked() clears it. In v1 the condition was evaluated after the disable, so it selected DP_PSR_ERROR_STATUS in the error path and the patch was a no-op there. Caught by Sashiko AI review. Signed-off-by: Jake Steinman Reviewed-by: Jouni Högander Link: https://patch.msgid.link/20260901205956.130779-1-j@metarealtyinc.ca Signed-off-by: Rodrigo Vivi [Rodrigo: adjusted commit message based on checkpatch] --- drivers/gpu/drm/i915/display/intel_psr.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 52836496040f..f490beb66629 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -3846,6 +3846,7 @@ void intel_psr_short_pulse(struct intel_dp *intel_dp) struct intel_display *display = to_intel_display(intel_dp); struct intel_psr *psr = &intel_dp->psr; u8 status, error_status; + bool panel_replay_enabled; const u8 errors = DP_PSR_RFB_STORAGE_ERROR | DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR | DP_PSR_LINK_CRC_ERROR; @@ -3866,6 +3867,12 @@ void intel_psr_short_pulse(struct intel_dp *intel_dp) goto exit; } + /* + * Save this before intel_psr_disable_locked() clears it; the error + * status is acknowledged to a different DPCD address depending on it. + */ + panel_replay_enabled = psr->panel_replay_enabled; + if ((!psr->panel_replay_enabled && status == DP_PSR_SINK_INTERNAL_ERROR) || (error_status & errors)) { intel_psr_disable_locked(intel_dp); @@ -3891,7 +3898,10 @@ void intel_psr_short_pulse(struct intel_dp *intel_dp) "PSR_ERROR_STATUS unhandled errors %x\n", error_status & ~errors); /* clear status register */ - drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_ERROR_STATUS, error_status); + drm_dp_dpcd_writeb(&intel_dp->aux, + panel_replay_enabled ? + DP_PANEL_REPLAY_ERROR_STATUS : DP_PSR_ERROR_STATUS, + error_status); if (!psr->panel_replay_enabled) { psr_alpm_check(intel_dp); From 118ddfa814ba00eba3643058edc08e329472f950 Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Tue, 1 Sep 2026 16:06:38 +0530 Subject: [PATCH 70/72] drm/i915/hdmi: Move audio compute config after format selection As we plan to add audio BW calculation in i915, intel_audio_compute_config() would need the final output_format and pipe_bpp to correctly determine audio bandwidth constraints. Move the intel_audio_compute_config() call to after intel_hdmi_compute_formats() returns, ensuring the audio configuration is computed with the final parameters. Signed-off-by: Chaitanya Kumar Borah Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260901103639.617757-2-chaitanya.kumar.borah@intel.com --- drivers/gpu/drm/i915/display/intel_hdmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 9b637e38a1a5..b6fa1a1558b8 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2399,10 +2399,6 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder, if (!intel_link_bw_compute_pipe_bpp(pipe_config)) return -EINVAL; - pipe_config->has_audio = - intel_hdmi_has_audio(encoder, pipe_config, conn_state) && - intel_audio_compute_config(encoder, pipe_config, conn_state); - /* * Try to respect downstream TMDS clock limits first, if * that fails assume the user might know something we don't. @@ -2417,6 +2413,10 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder, return ret; } + pipe_config->has_audio = + intel_hdmi_has_audio(encoder, pipe_config, conn_state) && + intel_audio_compute_config(encoder, pipe_config, conn_state); + ret = intel_pfit_compute_config(pipe_config, conn_state); if (ret) return ret; From e5e84aaf3e10b1a52be14c0d6d5d504d363d8bd6 Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Tue, 1 Sep 2026 16:06:39 +0530 Subject: [PATCH 71/72] drm/i915/audio: Prune ELD SADs based on HDMI audio bandwidth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add bandwidth check to determine whether a given audio sample rate and channel count can be carried within the hblank period for HDMI TMDS mode. Use this check to prune unsupported sample rates from each SAD in the ELD during intel_audio_compute_config(). SADs with no remaining supported rates are removed entirely. Sample rates are pruned rather than channel counts, since compressed formats (e.g. AC-3) are associated with specific channel counts. v2: - Use DIV64_U64_ROUND_UP() instead of DIV_ROUND_UP_ULL() to avoid do_div() truncating the 64-bit divisor to 32-bit, which caused audio_packets_line to be wildly inflated and all SADs to be pruned. - Guard intel_audio_hdmi_eld_compute_config() against HDMI FRL modes. (Remove it when BW calculations for FRL are added.) v3: - Rebase v4: - Rework SAD removal into a single-pass, two-pointer compaction instead of memmove-ing the tail on every removal. (Jani) v5: - Drop the FRL guard, i915 doesn't support FRL yet. (Ankit) - s/required_tmds/hblank_audio_min (Ankit) - Add HDMI_TMDS_AUDIO_PACKETS_LINE_MAX macro instead of a magic number 18. (Ankit) - Use drm_eld_sad_get()/drm_eld_sad_set() instead of raw ELD byte access. (Ankit) - Drop extra blank line. (Ankit) BSpec: 68944 Cc: Kai Vehmanen Cc: Ankit Nautiyal Cc: Ville Syrjälä Cc: Vinod Govindapillai Cc: Mitul Golani Cc: Jani Nikula Assisted-by: GitHub-Copilot:claude-opus-4.6 Signed-off-by: Chaitanya Kumar Borah Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260901103639.617757-3-chaitanya.kumar.borah@intel.com --- drivers/gpu/drm/i915/display/intel_audio.c | 153 +++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index eae76e961105..32f8c5e55319 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -697,6 +697,156 @@ static void ibx_audio_codec_enable(struct intel_encoder *encoder, mutex_unlock(&display->audio.mutex); } +#define HDMI_TMDS_AUDIO_PACKETS_LINE_MAX 18 + +static bool hdmi_audio_rate_supported(const struct intel_crtc_state *crtc_state, + int available_tmds, + int audio_rate, int channels) +{ + const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode; + int pixel_clk_max_hz; + int audio_pkt_factor; + u64 audio_pkt_rate_x4_x1000; + int audio_packets_line; + int hblank_overhead; + int hblank_audio_min; + + /* + * Part 2: Calculate TMDS clock cycles required for Audio Bandwidth + * + * Step 1: pixelclk_max = nominal_pixel_rate * (1 + 0.5%) + * crtc_clock (kHz) * 1000 * 1.005 = crtc_clock * 1005 (Hz) + */ + pixel_clk_max_hz = mode->crtc_clock * 1005; + + /* + * Steps 3-4: Audio Packet Rate. + * R_AP = (audio_rate * AP + 2 * acrrate_max) * (1 + 1000 / 1e6) + * = (audio_rate * AP + 2*1500) * 1.001 + * + * AP = 0.25 (2ch) or 1.0 (3-8ch); acrrate_max = 1500 Hz (max ACR + * packet transmission rate per HDMI spec) + * + * Scale by 4*1000 to stay integer: + * x4: eliminates AP=0.25 -> audio_pkt_factor=1(2ch) or 4(3-8ch), + * scaled acrrate_max: 2 * 1500 * 4 = 12000 + * x1000: eliminates 1.001 -> *1000*1.001 = *1001 + * + * R_AP * 4 * 1000 = (audio_rate * audio_pkt_factor + 12000) * 1001 + */ + audio_pkt_factor = (channels <= 2) ? 1 : 4; + audio_pkt_rate_x4_x1000 = (u64)(audio_rate * audio_pkt_factor + 12000) * 1001; + + /* + * Steps 2+5-6: Audio packets per line. + * AudioPackets_Line = CEIL[R_AP * htotal / f_pixelclk_max] + * + * With audio_pkt_rate_x4_x1000 = R_AP * 4 * 1000: + * = CEIL[audio_pkt_rate_x4_x1000 * htotal / (4 * 1000 * pixel_clk_max_hz)] + */ + audio_packets_line = DIV64_U64_ROUND_UP(audio_pkt_rate_x4_x1000 * mode->htotal, + (u64)4 * 1000 * pixel_clk_max_hz); + + /* + * Steps 7-9: Hblank overhead. + * Standard: 2*dip_guardband + 2*control_period + video_guardband + * = 2*2 + 2*12 + 2 = 30 + * HDCP 1.x: rekey_period + dip_guardband + control_period + video_guardband + * = 58 + 2 + 12 + 2 = 74 + * + * Always use HDCP 1.x worst case (74) since HDCP can be toggled + * via fastset without compute_config. + */ + hblank_overhead = 74; + + /* + * Step 10: Required TMDS cycles for Audio. + * 32 TMDS clock cycles per audio packet. + * Hblank_audio_min = 32 * AudioPackets_Line + Hblank_overhead + */ + hblank_audio_min = 32 * audio_packets_line + hblank_overhead; + + /* + * Part 3: audio supported if Hblank_audio_min <= TB_blank and + * audio packets per line <= Maximum allowed packets per line + */ + return hblank_audio_min <= available_tmds && + audio_packets_line <= HDMI_TMDS_AUDIO_PACKETS_LINE_MAX; +} + +static void intel_audio_hdmi_eld_compute_config(struct intel_crtc_state *crtc_state) +{ + static const int sad_freqs[] = { + 32000, 44100, 48000, 88200, 96000, 176400, 192000 + }; + const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode; + int hblank = mode->htotal - mode->hdisplay; + int bpc = crtc_state->pipe_bpp / 3; + int ycbcr_420_divider = (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) ? 2 : 1; + int available_tmds; + u8 *eld = crtc_state->eld; + int mnl = drm_eld_mnl(eld); + int sad_count = drm_eld_sad_count(eld); + int src, dst = 0; + + /* + * Part 1: Calculate available TMDS clock cycles (TB_blank). + * + * TB_blank = CEILING[hblank * K_CD / K_420] + * + * K_CD = 1 for YCbCr4:2:2, bpc / 8 otherwise. + * K_420 = 2 for YCbCr4:2:0, 1 otherwise. + * Rearranged: CEILING[hblank * bpc / (8 * K_420)] + * + * TODO: As and when support for YCbCr4:2:2 is added, set bpc = 8 + * to achieve K_CD = 1 + */ + available_tmds = DIV_ROUND_UP(hblank * bpc, 8 * ycbcr_420_divider); + + /* + * Walk all SADs once, keeping the ones with at least one supported + * rate and compacting them down to a contiguous [0, dst) range. + */ + for (src = 0; src < sad_count; src++) { + struct cea_sad sad; + u8 new_freq_mask = 0; + int channels; + int bit; + + drm_eld_sad_get(eld, src, &sad); + channels = sad.channels + 1; + + for (bit = 0; bit < 7; bit++) { + if (!(sad.freq & BIT(bit))) + continue; + if (hdmi_audio_rate_supported(crtc_state, available_tmds, + sad_freqs[bit], channels)) + new_freq_mask |= BIT(bit); + } + + if (!new_freq_mask) + continue; + + sad.freq = new_freq_mask; + drm_eld_sad_set(eld, dst, &sad); + dst++; + } + + /* Clear the now-unused tail */ + if (dst != sad_count) + memset(&eld[DRM_ELD_CEA_SAD(mnl, dst)], 0, (sad_count - dst) * 3); + + sad_count = dst; + + /* Update SAD count in ELD header */ + eld[DRM_ELD_SAD_COUNT_CONN_TYPE] &= ~DRM_ELD_SAD_COUNT_MASK; + eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= sad_count << DRM_ELD_SAD_COUNT_SHIFT; + + /* Recalculate baseline ELD length (in dwords) */ + eld[DRM_ELD_BASELINE_ELD_LEN] = + DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4); +} + static bool intel_audio_needs_cpu_transcoder_id(const struct intel_crtc_state *crtc_state) { @@ -725,6 +875,9 @@ bool intel_audio_compute_config(struct intel_encoder *encoder, BUILD_BUG_ON(sizeof(crtc_state->eld) != sizeof(connector->eld)); memcpy(crtc_state->eld, connector->eld, sizeof(crtc_state->eld)); + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) + intel_audio_hdmi_eld_compute_config(crtc_state); + crtc_state->eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2; mutex_unlock(&connector->eld_mutex); From 0d63d6fc993b1fb97e314f435bdefbf0ccd947bd Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Wed, 2 Sep 2026 14:08:48 +0000 Subject: [PATCH 72/72] drm/i915/display: Reset use_flipq when duplicating crtc state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_crtc_duplicate_state() resets use_dsb to false but not use_flipq, letting a stale true value survive into the new state. Reset use_flipq alongside use_dsb. Reviewed-by: Ville Syrjälä Signed-off-by: Mika Kahola Link: https://patch.msgid.link/20260902140848.915941-1-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_atomic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 9d0d47c79dd1..b66c2d4ba2b3 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -276,6 +276,7 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc) crtc_state->dsb_color = NULL; crtc_state->dsb_commit = NULL; crtc_state->use_dsb = false; + crtc_state->use_flipq = false; return &crtc_state->uapi; }