From 1ac0574589f29bf49aacacb97ff50a3041fd97b4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 13:34:56 +0300 Subject: [PATCH 001/186] drm/i915: Keep display IRQs enabled for encoder suspend/shutdown A pending hotplug work or the encoder suspend/shutdown hooks may still require display IRQs at least for AUX accesses, so keep all display IRQs except for hotplug IRQs enabled until after intel_hpd_cancel_work() and the encoder suspend/shutdown hooks are called during system suspend and shutdown. Signed-off-by: Imre Deak Link: https://patch.msgid.link/0b4b4d489f91be9334554e5438d3f2aa79421d42.1780310011.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_driver.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 58081b52461a..93940cfe91a0 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -1055,7 +1055,8 @@ void i915_driver_shutdown(struct drm_i915_private *i915) intel_dp_mst_suspend(display); - intel_irq_suspend(i915); + intel_encoder_block_all_hpds(display); + intel_hpd_cancel_work(display); if (intel_display_device_present(display)) @@ -1064,6 +1065,8 @@ void i915_driver_shutdown(struct drm_i915_private *i915) intel_encoder_suspend_all(display); intel_encoder_shutdown_all(display); + intel_irq_suspend(i915); + intel_dmc_suspend(display); i915_gem_suspend(i915); @@ -1135,7 +1138,8 @@ static int i915_drm_suspend(struct drm_device *dev) intel_display_driver_suspend(display); - intel_irq_suspend(dev_priv); + intel_encoder_block_all_hpds(display); + intel_hpd_cancel_work(display); if (intel_display_device_present(display)) @@ -1143,6 +1147,8 @@ static int i915_drm_suspend(struct drm_device *dev) intel_encoder_suspend_all(display); + intel_irq_suspend(dev_priv); + /* Must be called before GGTT is suspended. */ intel_dpt_suspend(display); i915_ggtt_suspend(to_gt(dev_priv)->ggtt); @@ -1314,6 +1320,8 @@ static int i915_drm_resume(struct drm_device *dev) intel_hpd_init(display); + intel_encoder_unblock_all_hpds(display); + intel_display_driver_resume(display); if (intel_display_device_present(display)) { From 3eb3190a0a1fa798f58cb2dab18d0dd238762e3e Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 13:34:57 +0300 Subject: [PATCH 002/186] drm/i915/xe: Enable HPD polling later during system resume The detect hook of connectors - called by HPD polling - will check if user accesses are enabled and prevent the connector detection if that's not the case. Accordingly enable user accesses during system resume by calling intel_display_driver_enable_user_access() before enabling HPD polling. Signed-off-by: Imre Deak Link: https://patch.msgid.link/f9803b937ba3044052a81e1673ac374809548ba7.1780310011.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 08beaa4e89f4..b6a091b10fed 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -473,8 +473,8 @@ void xe_display_pm_resume(struct xe_device *xe) if (intel_display_device_present(display)) { intel_display_driver_resume(display); - drm_kms_helper_poll_enable(&xe->drm); intel_display_driver_enable_user_access(display); + drm_kms_helper_poll_enable(&xe->drm); } if (intel_display_device_present(display)) From a141146335ca299b4a0ba4ea3416e154927d0bdb Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 1 Jun 2026 13:34:58 +0300 Subject: [PATCH 003/186] drm/i915: add flush_workqueue(display->wq.cleanup) on shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're missing the cleanup workqueue flush on the shutdown path. Add it. Unfortunately have to briefly include intel_display_core.c here. To be removed later. Link: https://lore.kernel.org/r/agRp6Was9FCQbKee@intel.com Suggested-by: Ville Syrjälä Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/047afe4f8589d4391b95dfc4bff084292ba3bf32.1780310011.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_driver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 93940cfe91a0..60d5e06675ab 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -54,6 +54,7 @@ #include "display/intel_bw.h" #include "display/intel_cdclk.h" #include "display/intel_crtc.h" +#include "display/intel_display_core.h" #include "display/intel_display_device.h" #include "display/intel_display_driver.h" #include "display/intel_display_power.h" @@ -1053,6 +1054,8 @@ void i915_driver_shutdown(struct drm_i915_private *i915) drm_atomic_helper_shutdown(&i915->drm); } + flush_workqueue(display->wq.cleanup); + intel_dp_mst_suspend(display); intel_encoder_block_all_hpds(display); From 283d5aa3523c49662eef30744b564d397389e433 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 1 Jun 2026 13:34:59 +0300 Subject: [PATCH 004/186] drm/xe/display: remove intel_display_flush_cleanup_work() calls on suspend/shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_display_driver_suspend() already has drm_atomic_helper_suspend() and cleanup workqueue flush. The intel_display_flush_cleanup_work() calls on suspend/shutdown should be redundant. Remove. Link: https://lore.kernel.org/r/agRp6Was9FCQbKee@intel.com Suggested-by: Ville Syrjälä Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/8a6059f0cb63ad9a8e035583a79134c250b0ec71.1780310011.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index b6a091b10fed..e88c05b3e774 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -333,8 +333,6 @@ void xe_display_pm_suspend(struct xe_device *xe) intel_display_driver_suspend(display); } - intel_display_flush_cleanup_work(display); - intel_encoder_block_all_hpds(display); intel_hpd_cancel_work(display); @@ -365,7 +363,6 @@ void xe_display_pm_shutdown(struct xe_device *xe) intel_display_driver_suspend(display); } - intel_display_flush_cleanup_work(display); intel_dp_mst_suspend(display); intel_encoder_block_all_hpds(display); intel_hpd_cancel_work(display); From 3f2596e38d1921a6ab9faa061b6d83eb41661871 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 1 Jun 2026 13:35:00 +0300 Subject: [PATCH 005/186] drm/xe/display: drop duplicate intel_dp_mst_suspend() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_display_driver_suspend() already calls intel_dp_mst_suspend(). Remove the duplicate call. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/515ff69eb3ac08e2a0650a4acf72446c344121f9.1780310011.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 e88c05b3e774..bbe626ac85c7 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -363,7 +363,6 @@ void xe_display_pm_shutdown(struct xe_device *xe) intel_display_driver_suspend(display); } - intel_dp_mst_suspend(display); intel_encoder_block_all_hpds(display); intel_hpd_cancel_work(display); From 9cc47acc2f6ec74869e4be3ab59fe662cec887c3 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 2 Jun 2026 08:45:15 +0800 Subject: [PATCH 006/186] drm/i915/display: Fix intel_lpe_audio_irq_handler for PREEMPT-RT The LPE audio interrupt comes from the i915 interrupt handler. It should be in irq disabled context. With PREEMPT_RT enabled, the IRQ handler is threaded. Because intel_lpe_audio_irq_handler() may be called in threaded IRQ context, generic_handle_irq_safe API disables the interrupts before calling LPE's interrupt top half handler. This fixes braswell audio issues with RT enabled. Reviewed-by: Matthew Brost Reviewed-by: Uma Shankar Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260602004515.1907422-1-runyu.xiao@seu.edu.cn Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/i915/display/intel_lpe_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_lpe_audio.c b/drivers/gpu/drm/i915/display/intel_lpe_audio.c index 775493306a83..022ad18044bf 100644 --- a/drivers/gpu/drm/i915/display/intel_lpe_audio.c +++ b/drivers/gpu/drm/i915/display/intel_lpe_audio.c @@ -262,7 +262,7 @@ void intel_lpe_audio_irq_handler(struct intel_display *display) if (!HAS_LPE_AUDIO(display)) return; - ret = generic_handle_irq(display->audio.lpe.irq); + ret = generic_handle_irq_safe(display->audio.lpe.irq); if (ret) drm_err_ratelimited(display->drm, "error handling LPE audio irq: %d\n", ret); From 9ba383c2408426662fdc295336ebaa63ec91eb26 Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Mon, 1 Jun 2026 13:59:51 +0530 Subject: [PATCH 007/186] =?UTF-8?q?drm/i915/display:=20Don=E2=80=99t=20use?= =?UTF-8?q?=20atomic=20state=20back-pointer=20to=20derive=20color=20pipeli?= =?UTF-8?q?ne?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of relying on the plane_state->uapi.state back-pointer to reach the intel_atomic_state inside intel_plane_color_copy_uapi_to_hw_state(), accept the intel_atomic_state as an argument to make the dependency explicit. Update intel_plane_copy_uapi_to_hw_state() and its callers accordingly. Call sites that do not have an atomic state available (e.g. legacy cursor update and initial plane setup) pass NULL. In such cases, skip color pipeline programming as there is no corresponding atomic colorop state to consume. v2: - Rebase Suggested-by: Ville Syrjälä Assisted-by: GitHub-Copilot:Claude-Sonnet-4.6 Signed-off-by: Chaitanya Kumar Borah Reviewed-by: Uma Shankar Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260601082953.128539-2-chaitanya.kumar.borah@intel.com --- drivers/gpu/drm/i915/display/intel_cursor.c | 2 +- .../drm/i915/display/intel_initial_plane.c | 2 +- drivers/gpu/drm/i915/display/intel_plane.c | 22 +++++++++++-------- drivers/gpu/drm/i915/display/intel_plane.h | 3 ++- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 52347668f27d..88384dea868b 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -876,7 +876,7 @@ intel_legacy_cursor_update(struct drm_plane *_plane, new_plane_state->uapi.crtc_w = crtc_w; new_plane_state->uapi.crtc_h = crtc_h; - intel_plane_copy_uapi_to_hw_state(new_plane_state, new_plane_state, crtc); + intel_plane_copy_uapi_to_hw_state(NULL, new_plane_state, new_plane_state, crtc); ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state, old_plane_state, new_plane_state); diff --git a/drivers/gpu/drm/i915/display/intel_initial_plane.c b/drivers/gpu/drm/i915/display/intel_initial_plane.c index 6aa253678ec9..e414b5d1085c 100644 --- a/drivers/gpu/drm/i915/display/intel_initial_plane.c +++ b/drivers/gpu/drm/i915/display/intel_initial_plane.c @@ -170,7 +170,7 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc, drm_framebuffer_get(fb); plane_state->uapi.crtc = &crtc->base; - intel_plane_copy_uapi_to_hw_state(plane_state, plane_state, crtc); + intel_plane_copy_uapi_to_hw_state(NULL, plane_state, plane_state, crtc); atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits); diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 2a52b36c646c..74100629bb3e 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -381,25 +381,27 @@ intel_plane_colorop_replace_blob(struct intel_plane_state *plane_state, } static void -intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +intel_plane_color_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc) { struct drm_colorop *iter_colorop, *colorop; struct drm_colorop_state *new_colorop_state; - struct drm_atomic_commit *state = plane_state->uapi.state; struct intel_colorop *intel_colorop; struct drm_property_blob *blob; - struct intel_atomic_state *intel_atomic_state = to_intel_atomic_state(state); - struct intel_crtc_state *new_crtc_state = intel_atomic_state ? - intel_atomic_get_new_crtc_state(intel_atomic_state, crtc) : NULL; + struct intel_crtc_state *new_crtc_state = state ? + intel_atomic_get_new_crtc_state(state, crtc) : NULL; bool changed = false; int i = 0; + if (!state) + return; + iter_colorop = from_plane_state->uapi.color_pipeline; while (iter_colorop) { - for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + for_each_new_colorop_in_state(&state->base, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == iter_colorop) { blob = new_colorop_state->bypass ? NULL : new_colorop_state->data; intel_colorop = to_intel_colorop(colorop); @@ -415,7 +417,8 @@ intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, new_crtc_state->plane_color_changed = true; } -void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc) { @@ -444,7 +447,7 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi); plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi); - intel_plane_color_copy_uapi_to_hw_state(plane_state, from_plane_state, crtc); + intel_plane_color_copy_uapi_to_hw_state(state, plane_state, from_plane_state, crtc); } void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, @@ -841,7 +844,8 @@ static int plane_atomic_check(struct intel_atomic_state *state, old_primary_crtc_plane_state, new_primary_crtc_plane_state); - intel_plane_copy_uapi_to_hw_state(new_plane_state, + intel_plane_copy_uapi_to_hw_state(state, + new_plane_state, new_primary_crtc_plane_state, crtc); diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h index a6338bba72d9..87c79a644052 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.h +++ b/drivers/gpu/drm/i915/display/intel_plane.h @@ -35,7 +35,8 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, int color_plane); -void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc); void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, From dfd604b259ce4269a4643a385f2c1200b5c90d12 Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Mon, 1 Jun 2026 13:59:52 +0530 Subject: [PATCH 008/186] drm/i915: Avoid programming color HW blocks for NV12 Y planes link_nv12_planes() currently copies the full UV plane hw state to the Y plane. This includes the color pipeline blobs (ctm, degamma_lut, gamma_lut, lut_3d) which is incorrect as we don't need to program these HW blocks for Y plane. This is harmless currently as the color pipeline uapi does not support YUV (both packed and planar) formats but that can change in the future. Add a new static helper intel_plane_y_copy_hw_state() that copies only the rendering parameters a Y plane actually needs, leaving all color pipeline blobs unset. Remove the helper intel_plane_copy_hw_state() as there are no users for it. v2: - drop the extra spaces before ='s (Jani) Cc: Jani Nikula Assisted-by: GitHub-Copilot:Claude-Sonnet-4.6 Signed-off-by: Chaitanya Kumar Borah Reviewed-by: Uma Shankar Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260601082953.128539-3-chaitanya.kumar.borah@intel.com --- drivers/gpu/drm/i915/display/intel_plane.c | 22 ++++++++++++++-------- drivers/gpu/drm/i915/display/intel_plane.h | 2 -- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 74100629bb3e..5ae178e8a96b 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -450,16 +450,22 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, intel_plane_color_copy_uapi_to_hw_state(state, plane_state, from_plane_state, crtc); } -void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, - const struct intel_plane_state *from_plane_state) +static void intel_plane_y_copy_hw_state(struct intel_plane_state *y_plane_state, + const struct intel_plane_state *uv_plane_state) { - intel_plane_clear_hw_state(plane_state); + intel_plane_clear_hw_state(y_plane_state); - memcpy(&plane_state->hw, &from_plane_state->hw, - sizeof(plane_state->hw)); + y_plane_state->hw.crtc = uv_plane_state->hw.crtc; + y_plane_state->hw.fb = uv_plane_state->hw.fb; + if (y_plane_state->hw.fb) + drm_framebuffer_get(y_plane_state->hw.fb); - if (plane_state->hw.fb) - drm_framebuffer_get(plane_state->hw.fb); + y_plane_state->hw.alpha = uv_plane_state->hw.alpha; + y_plane_state->hw.pixel_blend_mode = uv_plane_state->hw.pixel_blend_mode; + y_plane_state->hw.rotation = uv_plane_state->hw.rotation; + y_plane_state->hw.color_encoding = uv_plane_state->hw.color_encoding; + y_plane_state->hw.color_range = uv_plane_state->hw.color_range; + y_plane_state->hw.scaling_filter = uv_plane_state->hw.scaling_filter; } static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, @@ -1665,7 +1671,7 @@ static void link_nv12_planes(struct intel_crtc_state *crtc_state, crtc_state->rel_data_rate[y_plane->id] = crtc_state->rel_data_rate_y[uv_plane->id]; /* Copy parameters to Y plane */ - intel_plane_copy_hw_state(y_plane_state, uv_plane_state); + intel_plane_y_copy_hw_state(y_plane_state, uv_plane_state); y_plane_state->uapi.src = uv_plane_state->uapi.src; y_plane_state->uapi.dst = uv_plane_state->uapi.dst; diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h index 87c79a644052..31a6229aea73 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.h +++ b/drivers/gpu/drm/i915/display/intel_plane.h @@ -39,8 +39,6 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc); -void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, - const struct intel_plane_state *from_plane_state); void intel_plane_async_flip(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, From c6eea1925154b6697fe22b217faab9bb30635e6b Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Mon, 1 Jun 2026 13:59:53 +0530 Subject: [PATCH 009/186] drm/i915: Fix color blob reference handling in intel_plane_state Take proper references for hw color blobs (degamma_lut, gamma_lut, ctm, lut_3d) in intel_plane_duplicate_state() and drop them in intel_plane_destroy_state(). v2: - handle blobs in hw state clear Cc: #v6.19+ Fixes: 3b7476e786c2 ("drm/i915/color: Add framework to program PRE/POST CSC LUT") Fixes: a78f1b6baf4d ("drm/i915/color: Add framework to program CSC") Fixes: 65db7a1f9cf7 ("drm/i915/color: Add 3D LUT to color pipeline") Reviewed-by: Pranay Samala #v1 Reviewed-by: Uma Shankar Signed-off-by: Chaitanya Kumar Borah Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260601082953.128539-4-chaitanya.kumar.borah@intel.com --- drivers/gpu/drm/i915/display/intel_plane.c | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 5ae178e8a96b..acfe974cdc92 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -145,6 +145,15 @@ intel_plane_duplicate_state(struct drm_plane *plane) if (intel_state->hw.fb) drm_framebuffer_get(intel_state->hw.fb); + if (intel_state->hw.degamma_lut) + drm_property_blob_get(intel_state->hw.degamma_lut); + if (intel_state->hw.gamma_lut) + drm_property_blob_get(intel_state->hw.gamma_lut); + if (intel_state->hw.ctm) + drm_property_blob_get(intel_state->hw.ctm); + if (intel_state->hw.lut_3d) + drm_property_blob_get(intel_state->hw.lut_3d); + return &intel_state->uapi; } @@ -168,6 +177,16 @@ intel_plane_destroy_state(struct drm_plane *plane, __drm_atomic_helper_plane_destroy_state(&plane_state->uapi); if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); + kfree(plane_state); } @@ -340,6 +359,14 @@ static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state) { if (plane_state->hw.fb) drm_framebuffer_put(plane_state->hw.fb); + if (plane_state->hw.degamma_lut) + drm_property_blob_put(plane_state->hw.degamma_lut); + if (plane_state->hw.gamma_lut) + drm_property_blob_put(plane_state->hw.gamma_lut); + if (plane_state->hw.ctm) + drm_property_blob_put(plane_state->hw.ctm); + if (plane_state->hw.lut_3d) + drm_property_blob_put(plane_state->hw.lut_3d); memset(&plane_state->hw, 0, sizeof(plane_state->hw)); } From 466a751e17f4cc0c740f6cc8139bae5ba65e8a15 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Thu, 28 May 2026 19:07:46 -0400 Subject: [PATCH 010/186] drm/i915/color: Fix step discontinuity in Post-CSC Gamma LUT Fix a step discontinuity in the Post-CSC Gamma LUT when SDR dimming is active by clamping Segment 2 to the last user-provided LUT entry value instead of hardcoding it to 1.0 (1 << 24). Link: https://lore.kernel.org/intel-gfx/20260521180143.2143262-1-sean@poorly.run/ #v1 Link: https://lore.kernel.org/intel-gfx/20260525135730.1122696-1-sean@poorly.run/ #v2 Changes in v2: - Split out into separate patches for pre/post csc fixes - Dropped loop bounds fix in favor of [1] Changes in v3: - None [1]- https://lore.kernel.org/r/20260519075245.383864-1-pranay.samala@intel.com Signed-off-by: Sean Paul Reviewed-by: Uma Shankar Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260528230817.2455072-1-sean@poorly.run --- drivers/gpu/drm/i915/display/intel_color.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 7ef870cd9a16..7185f3628dcf 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -4038,11 +4038,11 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, lut_val); } - /* Segment 2 */ + /* Segment 2 - clamp to the last LUT value to prevent step discontinuity */ do { intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - (1 << 24)); + lut_val); } while (i++ < 34); } else { /*TODO: Add for segment 0 */ From de90ca97b21eb60c52e8461e081015434d8bb47f Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Thu, 28 May 2026 19:07:47 -0400 Subject: [PATCH 011/186] drm/i915/color: Fix step discontinuity in Pre-CSC Gamma LUT Clamp Segment 2 to the last user-provided LUT entry value instead of hardcoding it to 1.0 (1 << 24) to fix a step discontinuity. Link: https://lore.kernel.org/intel-gfx/20260521180143.2143262-1-sean@poorly.run/ #v1 Link: https://lore.kernel.org/intel-gfx/20260525135730.1122696-2-sean@poorly.run/ #v2 Changes in v2: - Split out into separate patches for pre/post csc fixes - Dropped loop bounds fix in favor of [1] Changes in v3: - Fix stale commit message [1]- https://lore.kernel.org/r/20260519075245.383864-1-pranay.samala@intel.com Signed-off-by: Sean Paul Reviewed-by: Uma Shankar Reviewed-by: Chaitanya Kumar Borah Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260528230817.2455072-2-sean@poorly.run --- drivers/gpu/drm/i915/display/intel_color.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 7185f3628dcf..458508bcf1f4 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3968,6 +3968,7 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, enum plane_id plane = to_intel_plane(state->plane)->id; const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data; u32 i, lut_size; + u32 lut_val = 1 << 24; if (icl_is_hdr_plane(display, plane)) { lut_size = 128; @@ -3978,7 +3979,7 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, if (pre_csc_lut) { for (i = 0; i < lut_size; i++) { - u32 lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); + lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), @@ -3990,7 +3991,7 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, do { intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - (1 << 24)); + lut_val); } while (i++ < 130); } else { for (i = 0; i < lut_size; i++) { From bd61c7756b34157e093028225a69383b4b1203cc Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Fri, 29 May 2026 17:57:58 +0300 Subject: [PATCH 012/186] drm/i915/edp: Check supported link rates DPCD read intel_edp_set_sink_rates() reads DP_SUPPORTED_LINK_RATES into a local stack array and then parses the array unconditionally. If the read fails, the array contents are not valid and may result in bogus sink link rates being used. Use drm_dp_dpcd_read_data() and clear the sink rate array on failure, so the existing parser falls back to the default sink rate handling. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 68f357cb7347 ("drm/i915/dp: generate and cache sink rate array for all DP, not just eDP 1.4") Signed-off-by: Nikita Zhandarovich Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260529145759.1640646-1-n.zhandarovich@fintech.ru Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 0ce0c09835f6..85d3aa3b9894 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4811,10 +4811,17 @@ intel_edp_set_sink_rates(struct intel_dp *intel_dp) if (intel_dp->edp_dpcd[0] >= DP_EDP_14) { __le16 sink_rates[DP_MAX_SUPPORTED_RATES]; + int ret; int i; - drm_dp_dpcd_read(&intel_dp->aux, DP_SUPPORTED_LINK_RATES, - sink_rates, sizeof(sink_rates)); + ret = drm_dp_dpcd_read_data(&intel_dp->aux, + DP_SUPPORTED_LINK_RATES, + sink_rates, sizeof(sink_rates)); + if (ret < 0) { + drm_dbg_kms(display->drm, + "Unable to read eDP supported link rates, using default rates\n"); + memset(sink_rates, 0, sizeof(sink_rates)); + } for (i = 0; i < ARRAY_SIZE(sink_rates); i++) { int rate; From b70bd3d40ddd716c5bea749e5e65be68979db0b0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:41 +0300 Subject: [PATCH 013/186] drm/i915/display: add "pm" to intel_display_driver_{suspend, resume}() names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start naming the functions that are supposed to be called from the struct dem_pm_ops hooks with intel_display_driver_pm_*() to distinguish them better from the rest. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/b4dd07b6375752900f583d56eda16a1c2a0b1e49.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_driver.c | 4 ++-- drivers/gpu/drm/i915/display/intel_display_driver.h | 5 +++-- drivers/gpu/drm/i915/i915_driver.c | 12 ++++++------ drivers/gpu/drm/xe/display/xe_display.c | 6 +++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index d0729936f681..9be4c94740dc 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -678,7 +678,7 @@ void intel_display_driver_unregister(struct intel_display *display) * turn all crtc's off, but do not adjust state * This has to be paired with a call to intel_modeset_setup_hw_state. */ -int intel_display_driver_suspend(struct intel_display *display) +int intel_display_driver_pm_suspend(struct intel_display *display) { struct drm_atomic_commit *state; int ret; @@ -741,7 +741,7 @@ __intel_display_driver_resume(struct intel_display *display, return ret; } -void intel_display_driver_resume(struct intel_display *display) +void intel_display_driver_pm_resume(struct intel_display *display) { struct drm_atomic_commit *state = display->restore.modeset_state; struct drm_modeset_acquire_ctx ctx; diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h index 5270c26a32e0..d8a08ca68d4e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.h +++ b/drivers/gpu/drm/i915/display/intel_display_driver.h @@ -24,8 +24,9 @@ void intel_display_driver_remove(struct intel_display *display); void intel_display_driver_remove_noirq(struct intel_display *display); void intel_display_driver_remove_nogem(struct intel_display *display); void intel_display_driver_unregister(struct intel_display *display); -int intel_display_driver_suspend(struct intel_display *display); -void intel_display_driver_resume(struct intel_display *display); + +int intel_display_driver_pm_suspend(struct intel_display *display); +void intel_display_driver_pm_resume(struct intel_display *display); /* interface for intel_display_reset.c */ int __intel_display_driver_resume(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 60d5e06675ab..bd73d64c1ccb 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -1114,10 +1114,10 @@ static int i915_drm_prepare(struct drm_device *dev) intel_pxp_suspend_prepare(i915->pxp); /* - * NB intel_display_driver_suspend() may issue new requests after we've - * ostensibly marked the GPU as ready-to-sleep here. We need to - * split out that work and pull it forward so that after point, - * the GPU is not woken again. + * NB intel_display_driver_pm_suspend() may issue new requests after + * we've ostensibly marked the GPU as ready-to-sleep here. We need to + * split out that work and pull it forward so that after point, the GPU + * is not woken again. */ return i915_gem_backup_suspend(i915); } @@ -1139,7 +1139,7 @@ static int i915_drm_suspend(struct drm_device *dev) intel_display_driver_disable_user_access(display); } - intel_display_driver_suspend(display); + intel_display_driver_pm_suspend(display); intel_encoder_block_all_hpds(display); @@ -1325,7 +1325,7 @@ static int i915_drm_resume(struct drm_device *dev) intel_encoder_unblock_all_hpds(display); - intel_display_driver_resume(display); + intel_display_driver_pm_resume(display); if (intel_display_device_present(display)) { intel_display_driver_enable_user_access(display); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index bbe626ac85c7..3921f6672139 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -330,7 +330,7 @@ void xe_display_pm_suspend(struct xe_device *xe) if (intel_display_device_present(display)) { drm_kms_helper_poll_disable(&xe->drm); intel_display_driver_disable_user_access(display); - intel_display_driver_suspend(display); + intel_display_driver_pm_suspend(display); } intel_encoder_block_all_hpds(display); @@ -360,7 +360,7 @@ void xe_display_pm_shutdown(struct xe_device *xe) if (intel_display_device_present(display)) { drm_kms_helper_poll_disable(&xe->drm); intel_display_driver_disable_user_access(display); - intel_display_driver_suspend(display); + intel_display_driver_pm_suspend(display); } intel_encoder_block_all_hpds(display); @@ -468,7 +468,7 @@ void xe_display_pm_resume(struct xe_device *xe) intel_encoder_unblock_all_hpds(display); if (intel_display_device_present(display)) { - intel_display_driver_resume(display); + intel_display_driver_pm_resume(display); intel_display_driver_enable_user_access(display); drm_kms_helper_poll_enable(&xe->drm); } From b4843a4dece46bdbc7e3a5a64197edf0259ddcc9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:42 +0300 Subject: [PATCH 014/186] drm/xe/display: rename xe_display_pm_shutdown*() to xe_display_shutdown*() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shutdown functions get called from the struct pci_driver .shutdown hook, not through the struct dev_pm_ops hooks. Name accordingly, dropping the "pm" from the name, even if shutdown has a lot of similarities with suspend. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/8d5b5aa92cff77a99b3687c231a50ec576d6f37e.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 4 ++-- drivers/gpu/drm/xe/display/xe_display.h | 10 ++++++---- drivers/gpu/drm/xe/xe_device.c | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 3921f6672139..a97a5f134ad6 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -347,7 +347,7 @@ void xe_display_pm_suspend(struct xe_device *xe) intel_dmc_suspend(display); } -void xe_display_pm_shutdown(struct xe_device *xe) +void xe_display_shutdown(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -421,7 +421,7 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe) intel_dmc_wl_flush_release_work(display); } -void xe_display_pm_shutdown_late(struct xe_device *xe) +void xe_display_shutdown_late(struct xe_device *xe) { struct intel_display *display = xe->display; diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h index 76db95c25f7e..39e7ab173d0a 100644 --- a/drivers/gpu/drm/xe/display/xe_display.h +++ b/drivers/gpu/drm/xe/display/xe_display.h @@ -23,15 +23,16 @@ int xe_display_init(struct xe_device *xe); void xe_display_register(struct xe_device *xe); void xe_display_unregister(struct xe_device *xe); +void xe_display_shutdown(struct xe_device *xe); +void xe_display_shutdown_late(struct xe_device *xe); + void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl); void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir); void xe_display_irq_reset(struct xe_device *xe); void xe_display_irq_postinstall(struct xe_device *xe); void xe_display_pm_suspend(struct xe_device *xe); -void xe_display_pm_shutdown(struct xe_device *xe); void xe_display_pm_suspend_late(struct xe_device *xe); -void xe_display_pm_shutdown_late(struct xe_device *xe); void xe_display_pm_resume_early(struct xe_device *xe); void xe_display_pm_resume(struct xe_device *xe); void xe_display_pm_runtime_suspend(struct xe_device *xe); @@ -52,15 +53,16 @@ static inline int xe_display_init(struct xe_device *xe) { return 0; } static inline void xe_display_register(struct xe_device *xe) {} static inline void xe_display_unregister(struct xe_device *xe) {} +static inline void xe_display_shutdown(struct xe_device *xe) {} +static inline void xe_display_shutdown_late(struct xe_device *xe) {} + static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {} static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {} static inline void xe_display_irq_reset(struct xe_device *xe) {} static inline void xe_display_irq_postinstall(struct xe_device *xe) {} static inline void xe_display_pm_suspend(struct xe_device *xe) {} -static inline void xe_display_pm_shutdown(struct xe_device *xe) {} static inline void xe_display_pm_suspend_late(struct xe_device *xe) {} -static inline void xe_display_pm_shutdown_late(struct xe_device *xe) {} static inline void xe_display_pm_resume_early(struct xe_device *xe) {} static inline void xe_display_pm_resume(struct xe_device *xe) {} static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {} diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 4b45b617a039..fe8e14fc25bf 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1102,14 +1102,14 @@ void xe_device_shutdown(struct xe_device *xe) drm_dbg(&xe->drm, "Shutting down device\n"); - xe_display_pm_shutdown(xe); + xe_display_shutdown(xe); xe_irq_suspend(xe); for_each_gt(gt, xe, id) xe_gt_shutdown(gt); - xe_display_pm_shutdown_late(xe); + xe_display_shutdown_late(xe); if (!xe_driver_flr_disabled(xe)) { /* BOOM! */ From bc16416bd7235abcd8c61ee30fc3d4d25fb74dbc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:43 +0300 Subject: [PATCH 015/186] drm/xe/display: relocate the xe_display_shutdown*() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group the xe_display_shutdown() and xe_display_shutdown_late() functions together, away from the pm hooks. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/ca79ec22c7d8961bc82debf2ccc9ece4d1c7c906.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 90 ++++++++++++------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index a97a5f134ad6..96ed98369415 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -212,6 +212,51 @@ void xe_display_unregister(struct xe_device *xe) intel_display_driver_unregister(display); } +void xe_display_shutdown(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + intel_display_power_disable(display); + drm_client_dev_suspend(&xe->drm); + + if (intel_display_device_present(display)) { + drm_kms_helper_poll_disable(&xe->drm); + intel_display_driver_disable_user_access(display); + intel_display_driver_pm_suspend(display); + } + + intel_encoder_block_all_hpds(display); + intel_hpd_cancel_work(display); + + if (intel_display_device_present(display)) + intel_display_driver_suspend_access(display); + + intel_encoder_suspend_all(display); + intel_encoder_shutdown_all(display); + + intel_opregion_suspend(display, PCI_D3cold); + + intel_dmc_suspend(display); +} + +void xe_display_shutdown_late(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + /* + * The only requirement is to reboot with display DC states disabled, + * for now leaving all display power wells in the INIT power domain + * enabled. + */ + intel_display_power_driver_remove(display); +} + /* IRQ-related functions */ void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) @@ -347,36 +392,6 @@ void xe_display_pm_suspend(struct xe_device *xe) intel_dmc_suspend(display); } -void xe_display_shutdown(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - intel_display_power_disable(display); - drm_client_dev_suspend(&xe->drm); - - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&xe->drm); - intel_display_driver_disable_user_access(display); - intel_display_driver_pm_suspend(display); - } - - intel_encoder_block_all_hpds(display); - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); - intel_encoder_shutdown_all(display); - - intel_opregion_suspend(display, PCI_D3cold); - - intel_dmc_suspend(display); -} - void xe_display_pm_runtime_suspend(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -421,21 +436,6 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe) intel_dmc_wl_flush_release_work(display); } -void xe_display_shutdown_late(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - /* - * The only requirement is to reboot with display DC states disabled, - * for now leaving all display power wells in the INIT power domain - * enabled. - */ - intel_display_power_driver_remove(display); -} - void xe_display_pm_resume_early(struct xe_device *xe) { struct intel_display *display = xe->display; From 1908dbad53d9515bcef14ee7b990d3e47f0b3f63 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:44 +0300 Subject: [PATCH 016/186] drm/xe/display: relocate the xe_display_pm_runtime_*() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the xe_display_pm_runtime_*() functions together, in suspend/suspend_late/resume order. Also relocate the dependent d3cold functions near usage in the runtime pm functions. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/3e7de41c1a27ef250eb9c35c4858120bc9846301.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 160 ++++++++++++------------ 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 96ed98369415..7f4ad09f9cce 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -310,53 +310,6 @@ static bool suspend_to_idle(void) return false; } -static void xe_display_enable_d3cold(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - /* - * We do a lot of poking in a lot of registers, make sure they work - * properly. - */ - intel_display_power_disable(display); - - intel_display_flush_cleanup_work(display); - - intel_opregion_suspend(display, PCI_D3cold); - - intel_dmc_suspend(display); - - if (intel_display_device_present(display)) - intel_hpd_poll_enable(display); -} - -static void xe_display_disable_d3cold(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - intel_dmc_resume(display); - - if (intel_display_device_present(display)) - drm_mode_config_reset(&xe->drm); - - intel_display_driver_init_hw(display); - - intel_hpd_init(display); - - if (intel_display_device_present(display)) - intel_hpd_poll_disable(display); - - intel_opregion_resume(display); - - intel_display_power_enable(display); -} - void xe_display_pm_suspend(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -392,21 +345,6 @@ void xe_display_pm_suspend(struct xe_device *xe) intel_dmc_suspend(display); } -void xe_display_pm_runtime_suspend(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - if (xe->d3cold.allowed) { - xe_display_enable_d3cold(xe); - return; - } - - intel_hpd_poll_enable(display); -} - void xe_display_pm_suspend_late(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -418,24 +356,6 @@ void xe_display_pm_suspend_late(struct xe_device *xe) intel_display_power_suspend_late(display, s2idle); } -void xe_display_pm_runtime_suspend_late(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - if (xe->d3cold.allowed) - xe_display_pm_suspend_late(xe); - - /* - * If xe_display_pm_suspend_late() is not called, it is likely - * that we will be on dynamic DC states with DMC wakelock enabled. We - * need to flush the release work in that case. - */ - intel_dmc_wl_flush_release_work(display); -} - void xe_display_pm_resume_early(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -483,6 +403,86 @@ void xe_display_pm_resume(struct xe_device *xe) intel_display_power_enable(display); } +static void xe_display_enable_d3cold(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + /* + * We do a lot of poking in a lot of registers, make sure they work + * properly. + */ + intel_display_power_disable(display); + + intel_display_flush_cleanup_work(display); + + intel_opregion_suspend(display, PCI_D3cold); + + intel_dmc_suspend(display); + + if (intel_display_device_present(display)) + intel_hpd_poll_enable(display); +} + +static void xe_display_disable_d3cold(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + intel_dmc_resume(display); + + if (intel_display_device_present(display)) + drm_mode_config_reset(&xe->drm); + + intel_display_driver_init_hw(display); + + intel_hpd_init(display); + + if (intel_display_device_present(display)) + intel_hpd_poll_disable(display); + + intel_opregion_resume(display); + + intel_display_power_enable(display); +} + +void xe_display_pm_runtime_suspend(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + if (xe->d3cold.allowed) { + xe_display_enable_d3cold(xe); + return; + } + + intel_hpd_poll_enable(display); +} + +void xe_display_pm_runtime_suspend_late(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + if (xe->d3cold.allowed) + xe_display_pm_suspend_late(xe); + + /* + * If xe_display_pm_suspend_late() is not called, it is likely + * that we will be on dynamic DC states with DMC wakelock enabled. We + * need to flush the release work in that case. + */ + intel_dmc_wl_flush_release_work(display); +} + void xe_display_pm_runtime_resume(struct xe_device *xe) { struct intel_display *display = xe->display; From 19cae40d70301ed1881317eb2ff1aa7df4160ea8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:45 +0300 Subject: [PATCH 017/186] drm/{i915, xe}: move more calls inside intel_display_driver_pm_suspend() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intel_display_driver_pm_suspend() calls are surrounded by near identical display calls. Move the calls inside intel_display_driver_pm_suspend(). There's a slight functional change in that intel_display_driver_pm_suspend() returns early for !HAS_DISPLAY(). Assume this is what we want, and there are no cases where display engine is present but all pipes have been fused off. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/82cfac0b966a8a82c8cf85e6b7b050223b7d5e33.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_driver.c | 11 ++++++++++ drivers/gpu/drm/i915/i915_driver.c | 9 -------- drivers/gpu/drm/xe/display/xe_display.c | 22 ++----------------- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 9be4c94740dc..41a2244985fa 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -686,6 +686,17 @@ int intel_display_driver_pm_suspend(struct intel_display *display) if (!HAS_DISPLAY(display)) return 0; + /* + * We do a lot of poking in a lot of registers, make sure they work + * properly. + */ + intel_display_power_disable(display); + + drm_client_dev_suspend(display->drm); + + drm_kms_helper_poll_disable(display->drm); + intel_display_driver_disable_user_access(display); + state = drm_atomic_helper_suspend(display->drm); ret = PTR_ERR_OR_ZERO(state); if (ret) diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index bd73d64c1ccb..f161723f653e 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -1130,15 +1130,6 @@ static int i915_drm_suspend(struct drm_device *dev) disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); - /* We do a lot of poking in a lot of registers, make sure they work - * properly. */ - intel_display_power_disable(display); - drm_client_dev_suspend(dev); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(dev); - intel_display_driver_disable_user_access(display); - } - intel_display_driver_pm_suspend(display); intel_encoder_block_all_hpds(display); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 7f4ad09f9cce..3a84154febe9 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -219,14 +219,7 @@ void xe_display_shutdown(struct xe_device *xe) if (!xe->info.probe_display) return; - intel_display_power_disable(display); - drm_client_dev_suspend(&xe->drm); - - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&xe->drm); - intel_display_driver_disable_user_access(display); - intel_display_driver_pm_suspend(display); - } + intel_display_driver_pm_suspend(display); intel_encoder_block_all_hpds(display); intel_hpd_cancel_work(display); @@ -318,18 +311,7 @@ void xe_display_pm_suspend(struct xe_device *xe) if (!xe->info.probe_display) return; - /* - * We do a lot of poking in a lot of registers, make sure they work - * properly. - */ - intel_display_power_disable(display); - drm_client_dev_suspend(&xe->drm); - - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&xe->drm); - intel_display_driver_disable_user_access(display); - intel_display_driver_pm_suspend(display); - } + intel_display_driver_pm_suspend(display); intel_encoder_block_all_hpds(display); From f597d4b2fd0d7ab17538d444e17302aca0a4d4ea Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:46 +0300 Subject: [PATCH 018/186] drm/{i915, xe}: move more calls inside intel_display_driver_pm_resume() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intel_display_driver_pm_resume() calls are surrounded by near identical display calls. Move the calls inside intel_display_driver_pm_resume(). There's a slight functional change in that intel_display_driver_pm_resume() returns early for !HAS_DISPLAY(). Assume this is what we want, and there are no cases where display engine is present but all pipes have been fused off. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/2cb01e10de88e6436c54643acbcef2afd3188a58.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_driver.c | 18 +++++++++++++++ drivers/gpu/drm/i915/i915_driver.c | 19 ---------------- drivers/gpu/drm/xe/display/xe_display.c | 22 +------------------ 3 files changed, 19 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 41a2244985fa..f362532c6834 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -43,6 +43,7 @@ #include "intel_dp_tunnel.h" #include "intel_dpll.h" #include "intel_dpll_mgr.h" +#include "intel_encoder.h" #include "intel_fb.h" #include "intel_fbc.h" #include "intel_fbdev.h" @@ -761,6 +762,12 @@ void intel_display_driver_pm_resume(struct intel_display *display) if (!HAS_DISPLAY(display)) return; + intel_display_driver_resume_access(display); + + intel_hpd_init(display); + + intel_encoder_unblock_all_hpds(display); + /* MST sideband requires HPD interrupts enabled */ intel_dp_mst_resume(display); @@ -790,4 +797,15 @@ void intel_display_driver_pm_resume(struct intel_display *display) "Restoring old state failed with %i\n", ret); if (state) drm_atomic_commit_put(state); + + intel_display_driver_enable_user_access(display); + drm_kms_helper_poll_enable(display->drm); + + intel_hpd_poll_disable(display); + + intel_opregion_resume(display); + + drm_client_dev_resume(display->drm); + + intel_display_power_enable(display); } diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index f161723f653e..063d4bdec2d9 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -1309,27 +1309,8 @@ static int i915_drm_resume(struct drm_device *dev) intel_clock_gating_init(&dev_priv->drm); - if (intel_display_device_present(display)) - intel_display_driver_resume_access(display); - - intel_hpd_init(display); - - intel_encoder_unblock_all_hpds(display); - intel_display_driver_pm_resume(display); - if (intel_display_device_present(display)) { - intel_display_driver_enable_user_access(display); - drm_kms_helper_poll_enable(dev); - } - intel_hpd_poll_disable(display); - - intel_opregion_resume(display); - - drm_client_dev_resume(dev); - - intel_display_power_enable(display); - intel_gvt_resume(dev_priv); enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 3a84154febe9..4f3f0b6162db 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -362,27 +362,7 @@ void xe_display_pm_resume(struct xe_device *xe) intel_display_driver_init_hw(display); - if (intel_display_device_present(display)) - intel_display_driver_resume_access(display); - - intel_hpd_init(display); - - intel_encoder_unblock_all_hpds(display); - - if (intel_display_device_present(display)) { - intel_display_driver_pm_resume(display); - intel_display_driver_enable_user_access(display); - drm_kms_helper_poll_enable(&xe->drm); - } - - if (intel_display_device_present(display)) - intel_hpd_poll_disable(display); - - intel_opregion_resume(display); - - drm_client_dev_resume(&xe->drm); - - intel_display_power_enable(display); + intel_display_driver_pm_resume(display); } static void xe_display_enable_d3cold(struct xe_device *xe) From 91391b1a77199b8dbdf2bb7973bddaf0263cbca8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:47 +0300 Subject: [PATCH 019/186] drm/{i915, xe}: add intel_display_driver_pm_{suspend_late, resume_early}() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new functions intel_display_driver_pm_suspend_late() and intel_display_driver_pm_resume_early(), to be called from the corresponding struct dev_pm_ops hooks. There's a slight functional change for !HAS_DISPLAY() in that the new functions return early. Assume this is what we want, and there are no cases where display engine is present but all pipes have been fused off. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/a429a0ea7ac8e97a98c26ffe5be5db934267ec8d.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../gpu/drm/i915/display/intel_display_driver.c | 16 ++++++++++++++++ .../gpu/drm/i915/display/intel_display_driver.h | 2 ++ drivers/gpu/drm/i915/i915_driver.c | 6 +++--- drivers/gpu/drm/xe/display/xe_display.c | 4 ++-- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index f362532c6834..77fa4497b442 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -714,6 +714,22 @@ int intel_display_driver_pm_suspend(struct intel_display *display) return ret; } +void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle) +{ + if (!HAS_DISPLAY(display)) + return; + + intel_display_power_suspend_late(display, s2idle); +} + +void intel_display_driver_pm_resume_early(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + intel_display_power_resume_early(display); +} + int __intel_display_driver_resume(struct intel_display *display, struct drm_atomic_commit *state, diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h index d8a08ca68d4e..adfde02465ea 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.h +++ b/drivers/gpu/drm/i915/display/intel_display_driver.h @@ -26,6 +26,8 @@ void intel_display_driver_remove_nogem(struct intel_display *display); void intel_display_driver_unregister(struct intel_display *display); int intel_display_driver_pm_suspend(struct intel_display *display); +void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle); +void intel_display_driver_pm_resume_early(struct intel_display *display); void intel_display_driver_pm_resume(struct intel_display *display); /* interface for intel_display_reset.c */ diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 063d4bdec2d9..6fd3e8b155b1 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -1181,12 +1181,12 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation) for_each_gt(gt, dev_priv, i) intel_uncore_suspend(gt->uncore); - intel_display_power_suspend_late(display, s2idle); + intel_display_driver_pm_suspend_late(display, s2idle); ret = vlv_suspend_complete(dev_priv); if (ret) { drm_err(&dev_priv->drm, "Suspend complete failed: %d\n", ret); - intel_display_power_resume_early(display); + intel_display_driver_pm_resume_early(display); } enable_rpm_wakeref_asserts(rpm); @@ -1345,7 +1345,7 @@ static int i915_drm_resume_early(struct drm_device *dev) for_each_gt(gt, dev_priv, i) intel_gt_resume_early(gt); - intel_display_power_resume_early(display); + intel_display_driver_pm_resume_early(display); enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 4f3f0b6162db..066f7f18b91e 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -335,7 +335,7 @@ void xe_display_pm_suspend_late(struct xe_device *xe) if (!xe->info.probe_display) return; - intel_display_power_suspend_late(display, s2idle); + intel_display_driver_pm_suspend_late(display, s2idle); } void xe_display_pm_resume_early(struct xe_device *xe) @@ -345,7 +345,7 @@ void xe_display_pm_resume_early(struct xe_device *xe) if (!xe->info.probe_display) return; - intel_display_power_resume_early(display); + intel_display_driver_pm_resume_early(display); } void xe_display_pm_resume(struct xe_device *xe) From 131acec1ce8b6b976cd620e3443163cb752b8e42 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:48 +0300 Subject: [PATCH 020/186] drm/{i915,xe}: add intel_display_driver_shutdown_late() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new function intel_display_driver_shutdown_late() to be called "later" in the struct pci_driver .shutdown hook. There's a slight functional change in that intel_display_driver_shutdown_late() returns early for !HAS_DISPLAY(). Assume this is what we want, and there are no cases where display engine is present but all pipes have been fused off. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/d200966191a3845e6b9586d20884e285670734d1.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_driver.c | 13 +++++++++++++ drivers/gpu/drm/i915/display/intel_display_driver.h | 1 + drivers/gpu/drm/i915/i915_driver.c | 7 ++----- drivers/gpu/drm/xe/display/xe_display.c | 7 +------ 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 77fa4497b442..0326a292ee1e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -675,6 +675,19 @@ void intel_display_driver_unregister(struct intel_display *display) intel_vga_unregister(display); } +void intel_display_driver_shutdown_late(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + /* + * The only requirement is to reboot with display DC states disabled, + * for now leaving all display power wells in the INIT power domain + * enabled. + */ + intel_display_power_driver_remove(display); +} + /* * turn all crtc's off, but do not adjust state * This has to be paired with a call to intel_modeset_setup_hw_state. diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h index adfde02465ea..61515577758b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.h +++ b/drivers/gpu/drm/i915/display/intel_display_driver.h @@ -24,6 +24,7 @@ void intel_display_driver_remove(struct intel_display *display); void intel_display_driver_remove_noirq(struct intel_display *display); void intel_display_driver_remove_nogem(struct intel_display *display); void intel_display_driver_unregister(struct intel_display *display); +void intel_display_driver_shutdown_late(struct intel_display *display); int intel_display_driver_pm_suspend(struct intel_display *display); void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle); diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 6fd3e8b155b1..99fa42561989 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -1075,17 +1075,14 @@ void i915_driver_shutdown(struct drm_i915_private *i915) i915_gem_suspend(i915); /* - * The only requirement is to reboot with display DC states disabled, - * for now leaving all display power wells in the INIT power domain - * enabled. - * * TODO: * - unify the pci_driver::shutdown sequence here with the * pci_driver.driver.pm.poweroff,poweroff_late sequence. * - unify the driver remove and system/runtime suspend sequences with * the above unified shutdown/poweroff sequence. */ - intel_display_power_driver_remove(display); + + intel_display_driver_shutdown_late(display); enable_rpm_wakeref_asserts(&i915->runtime_pm); intel_runtime_pm_driver_last_release(&i915->runtime_pm); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 066f7f18b91e..f1a84f5ced48 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -242,12 +242,7 @@ void xe_display_shutdown_late(struct xe_device *xe) if (!xe->info.probe_display) return; - /* - * The only requirement is to reboot with display DC states disabled, - * for now leaving all display power wells in the INIT power domain - * enabled. - */ - intel_display_power_driver_remove(display); + intel_display_driver_shutdown_late(display); } /* IRQ-related functions */ From d5c141521349d39e9afbfdffd054ce2b0063e4be Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:49 +0300 Subject: [PATCH 021/186] drm/i915: add intel_display_driver_shutdown() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add intel_display_driver_shutdown() to be called from the struct pci_driver .shutdown path. Initially, only migrate i915, as there are some subtle differences with xe that will be addressed later. Pick up as much as we can at this point without making major functional changes. There's a slight functional change in that intel_display_driver_shutdown() returns early for !HAS_DISPLAY(). Assume this is what we want, and there are no cases where display engine is present but all pipes have been fused off. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/b6a3ed62d11b4b4999611e9b34c0bd1c531c1069.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_driver.c | 28 +++++++++++++++++++ .../drm/i915/display/intel_display_driver.h | 1 + drivers/gpu/drm/i915/i915_driver.c | 24 +--------------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 0326a292ee1e..ab0639e8101a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -675,6 +675,34 @@ void intel_display_driver_unregister(struct intel_display *display) intel_vga_unregister(display); } +void intel_display_driver_shutdown(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + intel_display_power_disable(display); + + drm_client_dev_suspend(display->drm); + drm_kms_helper_poll_disable(display->drm); + + intel_display_driver_disable_user_access(display); + + drm_atomic_helper_shutdown(display->drm); + + flush_workqueue(display->wq.cleanup); + + intel_dp_mst_suspend(display); + + intel_encoder_block_all_hpds(display); + + intel_hpd_cancel_work(display); + + intel_display_driver_suspend_access(display); + + intel_encoder_suspend_all(display); + intel_encoder_shutdown_all(display); +} + void intel_display_driver_shutdown_late(struct intel_display *display) { if (!HAS_DISPLAY(display)) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h index 61515577758b..7eca3d17dd82 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.h +++ b/drivers/gpu/drm/i915/display/intel_display_driver.h @@ -24,6 +24,7 @@ void intel_display_driver_remove(struct intel_display *display); void intel_display_driver_remove_noirq(struct intel_display *display); void intel_display_driver_remove_nogem(struct intel_display *display); void intel_display_driver_unregister(struct intel_display *display); +void intel_display_driver_shutdown(struct intel_display *display); void intel_display_driver_shutdown_late(struct intel_display *display); int intel_display_driver_pm_suspend(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 99fa42561989..2458a8a684e9 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -54,7 +54,6 @@ #include "display/intel_bw.h" #include "display/intel_cdclk.h" #include "display/intel_crtc.h" -#include "display/intel_display_core.h" #include "display/intel_display_device.h" #include "display/intel_display_driver.h" #include "display/intel_display_power.h" @@ -1044,29 +1043,8 @@ void i915_driver_shutdown(struct drm_i915_private *i915) disable_rpm_wakeref_asserts(&i915->runtime_pm); intel_runtime_pm_disable(&i915->runtime_pm); - intel_display_power_disable(display); - drm_client_dev_suspend(&i915->drm); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&i915->drm); - intel_display_driver_disable_user_access(display); - - drm_atomic_helper_shutdown(&i915->drm); - } - - flush_workqueue(display->wq.cleanup); - - intel_dp_mst_suspend(display); - - intel_encoder_block_all_hpds(display); - - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); - intel_encoder_shutdown_all(display); + intel_display_driver_shutdown(display); intel_irq_suspend(i915); From f7cadbee8aa4768713e57650e7afd8b67e26664d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:50 +0300 Subject: [PATCH 022/186] drm/i915/display: deduplicate suspend and shutdown a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_display_driver_shutdown() and intel_display_driver_pm_suspend() are quite similar. Abstract a shared function to call from both. This is intentionally just the first non-functional step. More gradual changes will follow. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/4d32548f99655650a9e53f3d33b8359998a00e6d.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_driver.c | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index ab0639e8101a..5f6c3d741196 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -66,6 +66,8 @@ #include "intel_wm.h" #include "skl_watermark.h" +static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown); + bool intel_display_driver_probe_defer(struct pci_dev *pdev) { struct drm_privacy_screen *privacy_screen; @@ -680,18 +682,7 @@ void intel_display_driver_shutdown(struct intel_display *display) if (!HAS_DISPLAY(display)) return; - intel_display_power_disable(display); - - drm_client_dev_suspend(display->drm); - drm_kms_helper_poll_disable(display->drm); - - intel_display_driver_disable_user_access(display); - - drm_atomic_helper_shutdown(display->drm); - - flush_workqueue(display->wq.cleanup); - - intel_dp_mst_suspend(display); + __intel_display_driver_pm_suspend(display, true); intel_encoder_block_all_hpds(display); @@ -720,10 +711,9 @@ void intel_display_driver_shutdown_late(struct intel_display *display) * turn all crtc's off, but do not adjust state * This has to be paired with a call to intel_modeset_setup_hw_state. */ -int intel_display_driver_pm_suspend(struct intel_display *display) +static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown) { - struct drm_atomic_commit *state; - int ret; + int ret = 0; if (!HAS_DISPLAY(display)) return 0; @@ -739,13 +729,19 @@ int intel_display_driver_pm_suspend(struct intel_display *display) drm_kms_helper_poll_disable(display->drm); intel_display_driver_disable_user_access(display); - state = drm_atomic_helper_suspend(display->drm); - ret = PTR_ERR_OR_ZERO(state); - if (ret) - drm_err(display->drm, "Suspending crtc's failed with %i\n", - ret); - else - display->restore.modeset_state = state; + if (shutdown) { + drm_atomic_helper_shutdown(display->drm); + } else { + struct drm_atomic_commit *state; + + state = drm_atomic_helper_suspend(display->drm); + ret = PTR_ERR_OR_ZERO(state); + if (ret) + drm_err(display->drm, "Suspending crtc's failed with %i\n", + ret); + else + display->restore.modeset_state = state; + } /* ensure all DPT VMAs have been unpinned for intel_dpt_suspend() */ flush_workqueue(display->wq.cleanup); @@ -755,6 +751,11 @@ int intel_display_driver_pm_suspend(struct intel_display *display) return ret; } +int intel_display_driver_pm_suspend(struct intel_display *display) +{ + return __intel_display_driver_pm_suspend(display, false); +} + void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle) { if (!HAS_DISPLAY(display)) From ad317c4de574b356a684954663842263d1acd044 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:51 +0300 Subject: [PATCH 023/186] drm/xe/display: use intel_display_driver_pm_shutdown() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace direct intel_display_driver_pm_suspend() and additional calls with intel_display_driver_shutdown(). This switches to use drm_atomic_helper_shutdown() instead of drm_atomic_helper_suspend(), which is the more appropriate thing to do anyway. Not calling intel_display_driver_pm_suspend() from the xe shutdown path unblocks further follow-up changes. There's a slight functional change in that intel_display_driver_shutdown() returns early for !HAS_DISPLAY(). Assume this is what we want, and there are no cases where display engine is present but all pipes have been fused off. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/e6783c063f8c0d4a2b413e550165896a3d977585.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index f1a84f5ced48..e4673d8d6c48 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -219,16 +219,7 @@ void xe_display_shutdown(struct xe_device *xe) if (!xe->info.probe_display) return; - intel_display_driver_pm_suspend(display); - - intel_encoder_block_all_hpds(display); - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); - intel_encoder_shutdown_all(display); + intel_display_driver_shutdown(display); intel_opregion_suspend(display, PCI_D3cold); From 04bfc500ad3d845a6056324f293adf505b2971c1 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 11:32:52 +0300 Subject: [PATCH 024/186] drm/{i915, xe}: move more stuff to __intel_display_driver_pm_suspend() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The calls leading up to __intel_display_driver_pm_suspend() are surrounded by near identical display calls. Move the calls inside __intel_display_driver_pm_suspend() to clean up and deduplicate. There's a slight functional change in that intel_display_driver_pm_suspend() returns early for !HAS_DISPLAY(). Assume this is what we want, and there are no cases where display engine is present but all pipes have been fused off. Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/fdca3720537b6b79a754a83811d07b97d47e0db0.1780389001.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../gpu/drm/i915/display/intel_display_driver.c | 15 ++++++++------- drivers/gpu/drm/i915/i915_driver.c | 10 ---------- drivers/gpu/drm/xe/display/xe_display.c | 10 ---------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 5f6c3d741196..462f78d5b020 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -684,13 +684,6 @@ void intel_display_driver_shutdown(struct intel_display *display) __intel_display_driver_pm_suspend(display, true); - intel_encoder_block_all_hpds(display); - - intel_hpd_cancel_work(display); - - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); intel_encoder_shutdown_all(display); } @@ -748,6 +741,14 @@ static int __intel_display_driver_pm_suspend(struct intel_display *display, bool intel_dp_mst_suspend(display); + intel_encoder_block_all_hpds(display); + + intel_hpd_cancel_work(display); + + intel_display_driver_suspend_access(display); + + intel_encoder_suspend_all(display); + return ret; } diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 2458a8a684e9..0520cd124686 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -61,7 +61,6 @@ #include "display/intel_dp.h" #include "display/intel_dpt.h" #include "display/intel_dram.h" -#include "display/intel_encoder.h" #include "display/intel_fbdev.h" #include "display/intel_gmbus.h" #include "display/intel_hotplug.h" @@ -1107,15 +1106,6 @@ static int i915_drm_suspend(struct drm_device *dev) intel_display_driver_pm_suspend(display); - intel_encoder_block_all_hpds(display); - - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); - intel_irq_suspend(dev_priv); /* Must be called before GGTT is suspended. */ diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index e4673d8d6c48..42fd87a6b26e 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -30,7 +30,6 @@ #include "intel_dmc_wl.h" #include "intel_dp.h" #include "intel_dram.h" -#include "intel_encoder.h" #include "intel_fbdev.h" #include "intel_hdcp.h" #include "intel_hotplug.h" @@ -299,15 +298,6 @@ void xe_display_pm_suspend(struct xe_device *xe) intel_display_driver_pm_suspend(display); - intel_encoder_block_all_hpds(display); - - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) { - intel_display_driver_suspend_access(display); - intel_encoder_suspend_all(display); - } - intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold); intel_dmc_suspend(display); From 6b48cb3cb53e316f5a9cda4c2c13d70cfd24e556 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:14 +0300 Subject: [PATCH 025/186] drm/i915/dp_link_training: Introduce link training state struct Start isolating the link training state from the generic DP code by introducing a separate intel_dp_link_training state struct. Allocate the state so it can remain opaque within its own module. Follow-up changes will move link training fields from the DP struct to the link training state. v2: Remove unnecessary function documentation. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-2-imre.deak@intel.com --- drivers/gpu/drm/i915/display/g4x_dp.c | 10 ++++++++- drivers/gpu/drm/i915/display/intel_ddi.c | 19 ++++++++++++++-- .../drm/i915/display/intel_display_types.h | 2 ++ drivers/gpu/drm/i915/display/intel_dp.c | 14 ++++++++++++ drivers/gpu/drm/i915/display/intel_dp.h | 3 +++ .../drm/i915/display/intel_dp_link_training.c | 22 +++++++++++++++++++ .../drm/i915/display/intel_dp_link_training.h | 4 ++++ 7 files changed, 71 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index 5ff1cdf4581a..d211e6c49e0a 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -1252,10 +1252,13 @@ static void g4x_dp_suspend_complete(struct intel_encoder *encoder) static void intel_dp_encoder_destroy(struct drm_encoder *encoder) { + struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder)); + intel_dp_encoder_flush_work(encoder); drm_encoder_cleanup(encoder); - kfree(enc_to_dig_port(to_intel_encoder(encoder))); + intel_dp_link_cleanup(&dig_port->dp); + kfree(dig_port); } static void intel_dp_encoder_reset(struct drm_encoder *encoder) @@ -1350,6 +1353,9 @@ bool g4x_dp_init(struct intel_display *display, intel_encoder->audio_enable = g4x_dp_audio_enable; intel_encoder->audio_disable = g4x_dp_audio_disable; + if (intel_dp_link_init(&dig_port->dp) != 0) + goto err_dp_init; + if ((display->platform.ivybridge && port == PORT_A) || (HAS_PCH_CPT(display) && port != PORT_A)) { dig_port->dp.set_link_train = cpt_set_link_train; @@ -1419,6 +1425,8 @@ bool g4x_dp_init(struct intel_display *display, return true; err_init_connector: + intel_dp_link_cleanup(&dig_port->dp); +err_dp_init: drm_encoder_cleanup(encoder); err_encoder_init: kfree(intel_connector); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 205978c9feb6..6399b16405c8 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4654,6 +4654,7 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder) drm_encoder_cleanup(encoder); kfree(dig_port->hdcp.port_data.streams); + intel_dp_link_cleanup(&dig_port->dp); kfree(dig_port); } @@ -4691,11 +4692,16 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) struct intel_display *display = to_intel_display(dig_port); struct intel_connector *connector; enum port port = dig_port->base.port; + int err; connector = intel_connector_alloc(); if (!connector) return -ENOMEM; + err = intel_dp_link_init(&dig_port->dp); + if (err) + goto err_dp_init; + dig_port->dp.output_reg = DDI_BUF_CTL(port); if (DISPLAY_VER(display) >= 14) dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain; @@ -4708,8 +4714,9 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) dig_port->dp.preemph_max = intel_ddi_dp_preemph_max; if (!intel_dp_init_connector(dig_port, connector)) { - kfree(connector); - return -EINVAL; + err = -EINVAL; + + goto err_init_connector; } if (dig_port->base.type == INTEL_OUTPUT_EDP) { @@ -4725,6 +4732,13 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) } return 0; + +err_init_connector: + intel_dp_link_cleanup(&dig_port->dp); +err_dp_init: + kfree(connector); + + return err; } static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port) @@ -4733,6 +4747,7 @@ static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port) struct intel_connector *connector = intel_dp->attached_connector; intel_dp_cleanup_connector(dig_port, connector); + intel_dp_link_cleanup(intel_dp); kfree(connector); } diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index c21e0c0ef0b1..b34848b6ad45 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -58,6 +58,7 @@ struct cec_notifier; struct drm_printer; struct intel_connector; struct intel_ddi_buf_trans; +struct intel_dp_link_training; struct intel_fbc; struct intel_global_objs_state; struct intel_hdcp_shim; @@ -1858,6 +1859,7 @@ struct intel_dp { int seq_train_failures; int force_train_failure; bool force_retrain; + struct intel_dp_link_training *training; } link; bool reset_link_params; int mso_link_count; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 85d3aa3b9894..5ba72bc728b6 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -7649,3 +7649,17 @@ u8 intel_dp_as_sdp_transmission_time(void) return DP_PR_AS_SDP_SETUP_TIME_T1; } + +int intel_dp_link_init(struct intel_dp *intel_dp) +{ + intel_dp->link.training = intel_dp_link_training_init(intel_dp); + if (!intel_dp->link.training) + return -ENOMEM; + + return 0; +} + +void intel_dp_link_cleanup(struct intel_dp *intel_dp) +{ + intel_dp_link_training_cleanup(intel_dp->link.training); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 46a7f5c70981..27cc95a34493 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -243,4 +243,7 @@ bool intel_dp_joiner_candidate_valid(struct intel_connector *connector, u8 intel_dp_as_sdp_transmission_time(void); +int intel_dp_link_init(struct intel_dp *intel_dp); +void intel_dp_link_cleanup(struct intel_dp *intel_dp); + #endif /* __INTEL_DP_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index e566f2b49594..9ab18cc80639 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -62,6 +62,10 @@ #define MAX_SEQ_TRAIN_FAILURES 2 +struct intel_dp_link_training { + struct intel_dp *dp; +}; + static void intel_dp_reset_lttpr_common_caps(struct intel_dp *intel_dp) { memset(intel_dp->lttpr_common_caps, 0, sizeof(intel_dp->lttpr_common_caps)); @@ -2233,3 +2237,21 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) debugfs_create_file("i915_dp_link_retrain_disabled", 0444, root, connector, &i915_dp_link_retrain_disabled_fops); } + +struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp) +{ + struct intel_dp_link_training *link_training; + + link_training = kzalloc_obj(*link_training); + if (!link_training) + return NULL; + + link_training->dp = intel_dp; + + return link_training; +} + +void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training) +{ + kfree(link_training); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index 18c34c1a472f..eefc6df8bc85 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -12,6 +12,7 @@ struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; struct intel_dp; +struct intel_dp_link_training; int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_SIZE]); int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp); @@ -56,4 +57,7 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, void intel_dp_link_training_debugfs_add(struct intel_connector *connector); +struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp); +void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training); + #endif /* __INTEL_DP_LINK_TRAINING_H__ */ From 84778cb2b101321416646e9f89511b190ddbabb3 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:15 +0300 Subject: [PATCH 026/186] drm/i915/dp_link_training: Factor out link training state reset helper Factor out the link training state reset into a helper in intel_dp_link_training.c to prepare for isolating the link training state from the generic DP code. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-3-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 3 +-- drivers/gpu/drm/i915/display/intel_dp_link_training.c | 8 ++++++++ drivers/gpu/drm/i915/display/intel_dp_link_training.h | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 5ba72bc728b6..91e3853f0a08 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3775,8 +3775,7 @@ void intel_dp_reset_link_params(struct intel_dp *intel_dp) intel_dp->link.max_rate = intel_dp_max_common_rate(intel_dp); intel_dp->link.mst_probed_lane_count = 0; intel_dp->link.mst_probed_rate = 0; - intel_dp->link.retrain_disabled = false; - intel_dp->link.seq_train_failures = 0; + intel_dp_link_training_reset(intel_dp->link.training); } /* Enable backlight PWM and backlight PP control. */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 9ab18cc80639..eb75819a85c0 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -2238,6 +2238,14 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) connector, &i915_dp_link_retrain_disabled_fops); } +void intel_dp_link_training_reset(struct intel_dp_link_training *link_training) +{ + struct intel_dp *intel_dp = link_training->dp; + + intel_dp->link.retrain_disabled = false; + intel_dp->link.seq_train_failures = 0; +} + struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp) { struct intel_dp_link_training *link_training; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index eefc6df8bc85..c9a1ca4557f4 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -57,6 +57,8 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, void intel_dp_link_training_debugfs_add(struct intel_connector *connector); +void intel_dp_link_training_reset(struct intel_dp_link_training *link_training); + struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp); void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training); From e958b3d3a341abd19a2947608cc72431b7e81876 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:16 +0300 Subject: [PATCH 027/186] drm/i915/dp_link_training: Flush commits in debugfs entries Flush pending connector commits before accessing the link training state from debugfs. Access to connector state - like the link training state - that may be updated from an asynchronous commit tail must hold the connection mutex and wait for the tail to complete. The commit tail cannot hold the connection mutex, so all other accessors must wait for it explicitly. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-4-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index eb75819a85c0..eea75a744b5b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1882,8 +1882,11 @@ static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) if (err) return err; + intel_dp_flush_connector_commits(connector); + if (intel_dp->link.active) current_rate = intel_dp->link_rate; + force_rate = intel_dp->link.force_rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -1955,6 +1958,8 @@ static ssize_t i915_dp_force_link_rate_write(struct file *file, if (err) return err; + intel_dp_flush_connector_commits(connector); + intel_dp_reset_link_params(intel_dp); intel_dp->link.force_rate = rate; @@ -1980,6 +1985,8 @@ static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) if (err) return err; + intel_dp_flush_connector_commits(connector); + if (intel_dp->link.active) current_lane_count = intel_dp->lane_count; force_lane_count = intel_dp->link.force_lane_count; @@ -2057,6 +2064,8 @@ static ssize_t i915_dp_force_lane_count_write(struct file *file, if (err) return err; + intel_dp_flush_connector_commits(connector); + intel_dp_reset_link_params(intel_dp); intel_dp->link.force_lane_count = lane_count; @@ -2079,6 +2088,8 @@ static int i915_dp_max_link_rate_show(void *data, u64 *val) if (err) return err; + intel_dp_flush_connector_commits(connector); + *val = intel_dp->link.max_rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2098,6 +2109,8 @@ static int i915_dp_max_lane_count_show(void *data, u64 *val) if (err) return err; + intel_dp_flush_connector_commits(connector); + *val = intel_dp->link.max_lane_count; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2117,6 +2130,8 @@ static int i915_dp_force_link_training_failure_show(void *data, u64 *val) if (err) return err; + intel_dp_flush_connector_commits(connector); + *val = intel_dp->link.force_train_failure; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2138,6 +2153,8 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) if (err) return err; + intel_dp_flush_connector_commits(connector); + intel_dp->link.force_train_failure = val; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2159,6 +2176,8 @@ static int i915_dp_force_link_retrain_show(void *data, u64 *val) if (err) return err; + intel_dp_flush_connector_commits(connector); + *val = intel_dp->link.force_retrain; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2177,6 +2196,8 @@ static int i915_dp_force_link_retrain_write(void *data, u64 val) if (err) return err; + intel_dp_flush_connector_commits(connector); + intel_dp->link.force_retrain = val; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2200,6 +2221,8 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data) if (err) return err; + intel_dp_flush_connector_commits(connector); + seq_printf(m, "%s\n", str_yes_no(intel_dp->link.retrain_disabled)); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); From a026b8ce9c2809b0ca2487026b40c6a23e20ea6e Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:17 +0300 Subject: [PATCH 028/186] drm/i915/dp_link_training: Move link training helpers to link training code Move the link retraining helpers to intel_dp_link_training.c, next to the other link training helpers. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-5-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 197 ----------------- drivers/gpu/drm/i915/display/intel_dp.h | 4 - .../drm/i915/display/intel_dp_link_training.c | 199 ++++++++++++++++++ .../drm/i915/display/intel_dp_link_training.h | 7 + 4 files changed, 206 insertions(+), 201 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 91e3853f0a08..8b3bc20578c8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -847,25 +847,6 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) return params_changed; } -bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, - u8 lane_count) -{ - /* - * FIXME: we need to synchronize the current link parameters with - * hardware readout. Currently fast link training doesn't work on - * boot-up. - */ - if (link_rate == 0 || - link_rate > intel_dp->link.max_rate) - return false; - - if (lane_count == 0 || - lane_count > intel_dp_max_lane_count(intel_dp)) - return false; - - return true; -} - u32 intel_dp_mode_to_fec_clock(u32 mode_clock) { return div_u64(mul_u32_u32(mode_clock, DP_DSC_FEC_OVERHEAD_FACTOR), @@ -5675,32 +5656,6 @@ void intel_read_dp_sdp(struct intel_encoder *encoder, } } -static bool intel_dp_link_ok(struct intel_dp *intel_dp, - u8 link_status[DP_LINK_STATUS_SIZE]) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; - bool uhbr = intel_dp->link_rate >= 1000000; - bool ok; - - if (uhbr) - ok = drm_dp_128b132b_lane_channel_eq_done(link_status, - intel_dp->lane_count); - else - ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count); - - if (ok) - return true; - - intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status); - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] %s link not ok, retraining\n", - encoder->base.base.id, encoder->base.name, - uhbr ? "128b/132b" : "8b/10b"); - - return false; -} - static void intel_dp_mst_hpd_irq(struct intel_dp *intel_dp, u8 *esi, u8 *ack) { @@ -5807,78 +5762,6 @@ intel_dp_handle_hdmi_link_status_change(struct intel_dp *intel_dp) } } -static int -intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE]) -{ - int err; - - memset(link_status, 0, DP_LINK_STATUS_SIZE); - - if (intel_dp_mst_active_streams(intel_dp) > 0) - err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI, - link_status, DP_LINK_STATUS_SIZE - 2); - else - err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX, - link_status); - - if (err) - return err; - - if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] & - DP_DOWNSTREAM_PORT_STATUS_CHANGED) - WRITE_ONCE(intel_dp->downstream_port_changed, true); - - return 0; -} - -static bool -intel_dp_needs_link_retrain(struct intel_dp *intel_dp) -{ - u8 link_status[DP_LINK_STATUS_SIZE]; - - if (!intel_dp->link.active) - return false; - - /* - * While PSR source HW is enabled, it will control main-link sending - * frames, enabling and disabling it so trying to do a retrain will fail - * as the link would or not be on or it could mix training patterns - * and frame data at the same time causing retrain to fail. - * Also when exiting PSR, HW will retrain the link anyways fixing - * any link status error. - */ - if (intel_psr_enabled(intel_dp)) - return false; - - if (intel_dp->link.force_retrain) - return true; - - if (intel_dp_read_link_status(intel_dp, link_status) < 0) - return false; - - /* - * Validate the cached values of intel_dp->link_rate and - * intel_dp->lane_count before attempting to retrain. - * - * FIXME would be nice to user the crtc state here, but since - * we need to call this from the short HPD handler that seems - * a bit hard. - */ - if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate, - intel_dp->lane_count)) - return false; - - if (intel_dp->link.retrain_disabled) - return false; - - if (intel_dp->link.seq_train_failures) - return true; - - /* Retrain if link not ok */ - return !intel_dp_link_ok(intel_dp, link_status) && - !intel_psr_link_ok(intel_dp); -} - bool intel_dp_has_connector(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state) { @@ -5970,86 +5853,6 @@ void intel_dp_flush_connector_commits(struct intel_connector *connector) wait_for_connector_hw_done(connector->base.state); } -static bool intel_dp_is_connected(struct intel_dp *intel_dp) -{ - struct intel_connector *connector = intel_dp->attached_connector; - - return connector->base.status == connector_status_connected || - intel_dp->is_mst; -} - -static int intel_dp_retrain_link(struct intel_encoder *encoder, - struct drm_modeset_acquire_ctx *ctx) -{ - struct intel_display *display = to_intel_display(encoder); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - u8 pipe_mask; - int ret; - - if (!intel_dp_is_connected(intel_dp)) - return 0; - - ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex, - ctx); - if (ret) - return ret; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return 0; - - ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); - if (ret) - return ret; - - if (pipe_mask == 0) - return 0; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return 0; - - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] retraining link (forced %s)\n", - encoder->base.base.id, encoder->base.name, - str_yes_no(intel_dp->link.force_retrain)); - - ret = intel_modeset_commit_pipes(display, pipe_mask, ctx); - if (ret == -EDEADLK) - return ret; - - intel_dp->link.force_retrain = false; - - if (ret) - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] link retraining failed: %pe\n", - encoder->base.base.id, encoder->base.name, - ERR_PTR(ret)); - - return ret; -} - -void intel_dp_link_check(struct intel_encoder *encoder) -{ - struct drm_modeset_acquire_ctx ctx; - int ret; - - intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) - ret = intel_dp_retrain_link(encoder, &ctx); -} - -void intel_dp_check_link_state(struct intel_dp *intel_dp) -{ - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *encoder = &dig_port->base; - - if (!intel_dp_is_connected(intel_dp)) - return; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return; - - intel_encoder_link_check_queue_work(encoder, 0); -} - static void intel_dp_handle_device_service_irq(struct intel_dp *intel_dp, u8 irq_mask) { struct intel_display *display = to_intel_display(intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 27cc95a34493..92ce04852326 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -59,8 +59,6 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp, struct drm_modeset_acquire_ctx *ctx, u8 *pipe_mask); void intel_dp_flush_connector_commits(struct intel_connector *connector); -void intel_dp_link_check(struct intel_encoder *encoder); -void intel_dp_check_link_state(struct intel_dp *intel_dp); void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode); void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); @@ -209,8 +207,6 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector); bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder); -bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, - u8 lane_count); bool intel_dp_has_connector(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state); int intel_dp_dsc_max_src_input_bpc(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index eea75a744b5b..d8f1834e5043 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -33,9 +33,11 @@ #include "intel_display_utils.h" #include "intel_dp.h" #include "intel_dp_link_training.h" +#include "intel_dp_mst.h" #include "intel_encoder.h" #include "intel_hdmi.h" #include "intel_hotplug.h" +#include "intel_modeset_lock.h" #include "intel_panel.h" #include "intel_psr.h" @@ -1868,6 +1870,203 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, lt_dbg(intel_dp, DP_PHY_DPRX, "DP2.0 SDP CRC16 for 128b/132b enabled\n"); } +bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, + u8 lane_count) +{ + /* + * FIXME: we need to synchronize the current link parameters with + * hardware readout. Currently fast link training doesn't work on + * boot-up. + */ + if (link_rate == 0 || + link_rate > intel_dp->link.max_rate) + return false; + + if (lane_count == 0 || + lane_count > intel_dp_max_lane_count(intel_dp)) + return false; + + return true; +} + +static bool intel_dp_link_ok(struct intel_dp *intel_dp, + u8 link_status[DP_LINK_STATUS_SIZE]) +{ + struct intel_display *display = to_intel_display(intel_dp); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + bool uhbr = intel_dp->link_rate >= 1000000; + bool ok; + + if (uhbr) + ok = drm_dp_128b132b_lane_channel_eq_done(link_status, + intel_dp->lane_count); + else + ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count); + + if (ok) + return true; + + intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status); + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] %s link not ok, retraining\n", + encoder->base.base.id, encoder->base.name, + uhbr ? "128b/132b" : "8b/10b"); + + return false; +} + +static int +intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE]) +{ + int err; + + memset(link_status, 0, DP_LINK_STATUS_SIZE); + + if (intel_dp_mst_active_streams(intel_dp) > 0) + err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI, + link_status, DP_LINK_STATUS_SIZE - 2); + else + err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX, + link_status); + + if (err) + return err; + + if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] & + DP_DOWNSTREAM_PORT_STATUS_CHANGED) + WRITE_ONCE(intel_dp->downstream_port_changed, true); + + return 0; +} + +static bool +intel_dp_needs_link_retrain(struct intel_dp *intel_dp) +{ + u8 link_status[DP_LINK_STATUS_SIZE]; + + if (!intel_dp->link.active) + return false; + + /* + * While PSR source HW is enabled, it will control main-link sending + * frames, enabling and disabling it so trying to do a retrain will fail + * as the link would or not be on or it could mix training patterns + * and frame data at the same time causing retrain to fail. + * Also when exiting PSR, HW will retrain the link anyways fixing + * any link status error. + */ + if (intel_psr_enabled(intel_dp)) + return false; + + if (intel_dp->link.force_retrain) + return true; + + if (intel_dp_read_link_status(intel_dp, link_status) < 0) + return false; + + /* + * Validate the cached values of intel_dp->link_rate and + * intel_dp->lane_count before attempting to retrain. + * + * FIXME would be nice to user the crtc state here, but since + * we need to call this from the short HPD handler that seems + * a bit hard. + */ + if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate, + intel_dp->lane_count)) + return false; + + if (intel_dp->link.retrain_disabled) + return false; + + if (intel_dp->link.seq_train_failures) + return true; + + /* Retrain if link not ok */ + return !intel_dp_link_ok(intel_dp, link_status) && + !intel_psr_link_ok(intel_dp); +} + +static bool intel_dp_is_connected(struct intel_dp *intel_dp) +{ + struct intel_connector *connector = intel_dp->attached_connector; + + return connector->base.status == connector_status_connected || + intel_dp->is_mst; +} + +static int intel_dp_retrain_link(struct intel_encoder *encoder, + struct drm_modeset_acquire_ctx *ctx) +{ + struct intel_display *display = to_intel_display(encoder); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + u8 pipe_mask; + int ret; + + if (!intel_dp_is_connected(intel_dp)) + return 0; + + ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex, + ctx); + if (ret) + return ret; + + if (!intel_dp_needs_link_retrain(intel_dp)) + return 0; + + ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); + if (ret) + return ret; + + if (pipe_mask == 0) + return 0; + + if (!intel_dp_needs_link_retrain(intel_dp)) + return 0; + + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] retraining link (forced %s)\n", + encoder->base.base.id, encoder->base.name, + str_yes_no(intel_dp->link.force_retrain)); + + ret = intel_modeset_commit_pipes(display, pipe_mask, ctx); + if (ret == -EDEADLK) + return ret; + + intel_dp->link.force_retrain = false; + + if (ret) + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] link retraining failed: %pe\n", + encoder->base.base.id, encoder->base.name, + ERR_PTR(ret)); + + return ret; +} + +void intel_dp_link_check(struct intel_encoder *encoder) +{ + struct drm_modeset_acquire_ctx ctx; + int ret; + + intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) + ret = intel_dp_retrain_link(encoder, &ctx); +} + +void intel_dp_check_link_state(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &dig_port->base; + + if (!intel_dp_is_connected(intel_dp)) + return; + + if (!intel_dp_needs_link_retrain(intel_dp)) + return; + + intel_encoder_link_check_queue_work(encoder, 0); +} + static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index c9a1ca4557f4..36ba9535fc34 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -13,6 +13,7 @@ struct intel_connector; struct intel_crtc_state; struct intel_dp; struct intel_dp_link_training; +struct intel_encoder; int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_SIZE]); int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp); @@ -55,6 +56,12 @@ static inline u8 intel_dp_training_pattern_symbol(u8 pattern) void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); +bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, + u8 lane_count); + +void intel_dp_link_check(struct intel_encoder *encoder); +void intel_dp_check_link_state(struct intel_dp *intel_dp); + void intel_dp_link_training_debugfs_add(struct intel_connector *connector); void intel_dp_link_training_reset(struct intel_dp_link_training *link_training); From 4aa3b4b97eefa5929983ceb78fc4099f15bb0c51 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:18 +0300 Subject: [PATCH 029/186] drm/i915/dp_link_training: Use link_training as base pointer in debugfs Retrieve the link_training pointer from the connector and derive the DP pointer from it in debugfs entries. This prepares for a follow-up change where values exposed via debugfs entries will be retrieved from the link training state. v2: Join unnecessarily wrapped lines. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-6-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index d8f1834e5043..4ecc00b7c9ff 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -68,6 +68,11 @@ struct intel_dp_link_training { struct intel_dp *dp; }; +static struct intel_dp_link_training *connector_to_link_training(struct intel_connector *connector) +{ + return intel_attached_dp(connector)->link.training; +} + static void intel_dp_reset_lttpr_common_caps(struct intel_dp *intel_dp) { memset(intel_dp->lttpr_common_caps, 0, sizeof(intel_dp->lttpr_common_caps)); @@ -2322,7 +2327,8 @@ static int i915_dp_force_link_training_failure_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); + struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -2342,7 +2348,8 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); + struct intel_dp *intel_dp = link_training->dp; int err; if (val > 2) @@ -2368,7 +2375,8 @@ static int i915_dp_force_link_retrain_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); + struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -2388,7 +2396,8 @@ static int i915_dp_force_link_retrain_write(void *data, u64 val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); + struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -2413,7 +2422,8 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); + struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); From 316b3060252a57e86d846a5355c454e6fc8f400a Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:19 +0300 Subject: [PATCH 030/186] drm/i915/dp_link_training: Add helpers to access force retrain state Add helpers to get and set the force retrain state in preparation for moving the state from the DP struct to the link training state. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-7-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 2 +- .../drm/i915/display/intel_dp_link_training.c | 29 +++++++++++++++---- .../drm/i915/display/intel_dp_link_training.h | 2 ++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 8b3bc20578c8..920d5b5e8caf 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5689,7 +5689,7 @@ static bool intel_dp_check_mst_status(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - bool force_retrain = intel_dp->link.force_retrain; + bool force_retrain = intel_dp_link_training_get_force_retrain(intel_dp->link.training); bool reprobe_needed = false; for (;;) { diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 4ecc00b7c9ff..831dec86febd 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1944,9 +1944,25 @@ intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STAT return 0; } +bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training) +{ + struct intel_dp *intel_dp = link_training->dp; + + return intel_dp->link.force_retrain; +} + +static void intel_dp_link_training_set_force_retrain(struct intel_dp_link_training *link_training, + bool forced) +{ + struct intel_dp *intel_dp = link_training->dp; + + intel_dp->link.force_retrain = forced; +} + static bool intel_dp_needs_link_retrain(struct intel_dp *intel_dp) { + struct intel_dp_link_training *link_training = intel_dp->link.training; u8 link_status[DP_LINK_STATUS_SIZE]; if (!intel_dp->link.active) @@ -1963,7 +1979,7 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp) if (intel_psr_enabled(intel_dp)) return false; - if (intel_dp->link.force_retrain) + if (intel_dp_link_training_get_force_retrain(link_training)) return true; if (intel_dp_read_link_status(intel_dp, link_status) < 0) @@ -2005,6 +2021,8 @@ static int intel_dp_retrain_link(struct intel_encoder *encoder, { struct intel_display *display = to_intel_display(encoder); struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_dp_link_training *link_training = + intel_dp->link.training; u8 pipe_mask; int ret; @@ -2032,13 +2050,13 @@ static int intel_dp_retrain_link(struct intel_encoder *encoder, drm_dbg_kms(display->drm, "[ENCODER:%d:%s] retraining link (forced %s)\n", encoder->base.base.id, encoder->base.name, - str_yes_no(intel_dp->link.force_retrain)); + str_yes_no(intel_dp_link_training_get_force_retrain(link_training))); ret = intel_modeset_commit_pipes(display, pipe_mask, ctx); if (ret == -EDEADLK) return ret; - intel_dp->link.force_retrain = false; + intel_dp_link_training_set_force_retrain(link_training, false); if (ret) drm_dbg_kms(display->drm, @@ -2376,7 +2394,6 @@ static int i915_dp_force_link_retrain_show(void *data, u64 *val) struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); struct intel_dp_link_training *link_training = connector_to_link_training(connector); - struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -2385,7 +2402,7 @@ static int i915_dp_force_link_retrain_show(void *data, u64 *val) intel_dp_flush_connector_commits(connector); - *val = intel_dp->link.force_retrain; + *val = intel_dp_link_training_get_force_retrain(link_training); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2406,7 +2423,7 @@ static int i915_dp_force_link_retrain_write(void *data, u64 val) intel_dp_flush_connector_commits(connector); - intel_dp->link.force_retrain = val; + intel_dp_link_training_set_force_retrain(link_training, val); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index 36ba9535fc34..ef16fcabd6da 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -59,6 +59,8 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, u8 lane_count); +bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training); + void intel_dp_link_check(struct intel_encoder *encoder); void intel_dp_check_link_state(struct intel_dp *intel_dp); From 6d553eb36ac4752efa0896250feb26162dedcac7 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:20 +0300 Subject: [PATCH 031/186] drm/i915/dp_link_training: Move link recovery/debug state to link_training Move all state related to link recovery and link training debugging from struct intel_dp to struct intel_dp_link_training. This moves towards grouping all link training and recovery state and logic in a single place and prepares for follow-up changes in the link recovery state handling. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-8-imre.deak@intel.com --- .../drm/i915/display/intel_display_types.h | 5 -- drivers/gpu/drm/i915/display/intel_dp.c | 2 +- .../drm/i915/display/intel_dp_link_training.c | 52 +++++++++---------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b34848b6ad45..aa4772a1c208 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1854,11 +1854,6 @@ struct intel_dp { int mst_probed_rate; int force_lane_count; int force_rate; - bool retrain_disabled; - /* Sequential link training failures after a passing LT */ - int seq_train_failures; - int force_train_failure; - bool force_retrain; struct intel_dp_link_training *training; } link; bool reset_link_params; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 920d5b5e8caf..3569e61e7fee 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5948,7 +5948,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) /* * Force checking the link status for DPCD_REV < 1.2 * TODO: let the link status check depend on LINK_STATUS_CHANGED - * or intel_dp->link.force_retrain for DPCD_REV >= 1.2 + * or intel_dp->link.training.force_retrain for DPCD_REV >= 1.2 */ esi[3] |= LINK_STATUS_CHANGED; if (intel_dp_handle_link_service_irq(intel_dp, esi[3])) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 831dec86febd..154caecacecb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -66,6 +66,12 @@ struct intel_dp_link_training { struct intel_dp *dp; + + bool retrain_disabled; + /* Sequential link training failures after a passing LT */ + int seq_train_failures; + int force_train_failure; + bool force_retrain; }; static struct intel_dp_link_training *connector_to_link_training(struct intel_connector *connector) @@ -1277,6 +1283,7 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp, void intel_dp_stop_link_train(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { + struct intel_dp_link_training *link_training = intel_dp->link.training; struct intel_display *display = to_intel_display(intel_dp); struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; int ret; @@ -1297,8 +1304,8 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp, intel_hpd_unblock(encoder); if (!display->hotplug.ignore_long_hpd && - intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES) { - int delay_ms = intel_dp->link.seq_train_failures ? 0 : 2000; + link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) { + int delay_ms = link_training->seq_train_failures ? 0 : 2000; intel_encoder_link_check_queue_work(encoder, delay_ms); } @@ -1791,6 +1798,8 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, struct intel_display *display = to_intel_display(state); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *encoder = &dig_port->base; + struct intel_dp_link_training *link_training = + intel_dp->link.training; bool passed; /* * Reinit the LTTPRs here to ensure that they are switched to @@ -1814,15 +1823,15 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, else passed = intel_dp_link_train_all_phys(intel_dp, crtc_state, lttpr_count); - if (intel_dp->link.force_train_failure) { - intel_dp->link.force_train_failure--; + if (link_training->force_train_failure) { + link_training->force_train_failure--; lt_dbg(intel_dp, DP_PHY_DPRX, "Forcing link training failure\n"); } else if (passed) { - intel_dp->link.seq_train_failures = 0; + link_training->seq_train_failures = 0; return; } - intel_dp->link.seq_train_failures++; + link_training->seq_train_failures++; /* * Ignore the link failure in CI @@ -1841,13 +1850,13 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - if (intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES) + if (link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) return; if (intel_dp_schedule_fallback_link_training(state, intel_dp, crtc_state)) return; - intel_dp->link.retrain_disabled = true; + link_training->retrain_disabled = true; if (!passed) lt_err(intel_dp, DP_PHY_DPRX, "Can't reduce link training parameters after failure\n"); @@ -1946,17 +1955,13 @@ intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STAT bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training) { - struct intel_dp *intel_dp = link_training->dp; - - return intel_dp->link.force_retrain; + return link_training->force_retrain; } static void intel_dp_link_training_set_force_retrain(struct intel_dp_link_training *link_training, bool forced) { - struct intel_dp *intel_dp = link_training->dp; - - intel_dp->link.force_retrain = forced; + link_training->force_retrain = forced; } static bool @@ -1997,10 +2002,10 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp) intel_dp->lane_count)) return false; - if (intel_dp->link.retrain_disabled) + if (link_training->retrain_disabled) return false; - if (intel_dp->link.seq_train_failures) + if (link_training->seq_train_failures) return true; /* Retrain if link not ok */ @@ -2346,7 +2351,6 @@ static int i915_dp_force_link_training_failure_show(void *data, u64 *val) struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); struct intel_dp_link_training *link_training = connector_to_link_training(connector); - struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -2355,7 +2359,7 @@ static int i915_dp_force_link_training_failure_show(void *data, u64 *val) intel_dp_flush_connector_commits(connector); - *val = intel_dp->link.force_train_failure; + *val = link_training->force_train_failure; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2367,7 +2371,6 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); struct intel_dp_link_training *link_training = connector_to_link_training(connector); - struct intel_dp *intel_dp = link_training->dp; int err; if (val > 2) @@ -2379,7 +2382,7 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) intel_dp_flush_connector_commits(connector); - intel_dp->link.force_train_failure = val; + link_training->force_train_failure = val; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2440,7 +2443,6 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data) struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp_link_training *link_training = connector_to_link_training(connector); - struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -2449,7 +2451,7 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data) intel_dp_flush_connector_commits(connector); - seq_printf(m, "%s\n", str_yes_no(intel_dp->link.retrain_disabled)); + seq_printf(m, "%s\n", str_yes_no(link_training->retrain_disabled)); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2489,10 +2491,8 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) void intel_dp_link_training_reset(struct intel_dp_link_training *link_training) { - struct intel_dp *intel_dp = link_training->dp; - - intel_dp->link.retrain_disabled = false; - intel_dp->link.seq_train_failures = 0; + link_training->retrain_disabled = false; + link_training->seq_train_failures = 0; } struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp) From dff708148702db7da72d9415e3d9f50a25e57e24 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:21 +0300 Subject: [PATCH 032/186] drm/i915/dp_link_training: Prevent repeated autoretrain attempts After a regular modeset link training failure, the driver attempts to recover the link via an autoretrain using the same link parameters as the modeset. If the autoretrain fails as well, the set of allowed link configurations is reduced via a fallback mechanism. For further link training, the modeset parameters will likely need to change. This lowers the required link bandwidth and allows selecting a link configuration from the fallback-reduced set. Only userspace can perform such a modeset change. Therefore, the driver notifies userspace to take over link recovery. Userspace is expected to continue with the recovery attempt via a modeset with updated parameters. The driver must not interfere with these modesets. link_training->seq_train_failures is set to MAX_SEQ_TRAIN_FAILURES after the autoretrain fails. If a fallback selection also fails after this, as no link configurations remain, retrain_disabled is set as well. retrain_disabled is therefore somewhat misnamed: it indicates that no fallback is available, not that autoretraining is disabled. This will be addressed in a follow-up change by renaming the flag. For now, prevent further autoretrain attempts based on the correct condition: seq_train_failures == MAX_SEQ_TRAIN_FAILURES. This also prepares for replacing the counter with an enum in a follow-up change. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-9-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp_link_training.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 154caecacecb..e766f7c323f7 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -2002,7 +2002,7 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp) intel_dp->lane_count)) return false; - if (link_training->retrain_disabled) + if (link_training->seq_train_failures >= MAX_SEQ_TRAIN_FAILURES) return false; if (link_training->seq_train_failures) From e93e1ded5788a56a3ee78e123e2cffc281e9f0d6 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:22 +0300 Subject: [PATCH 033/186] drm/i915/dp_link_training: Clamp sequential link training failure counter Clamp link_training->seq_train_failures to MAX_SEQ_TRAIN_FAILURES to avoid - an unlikely - overflow. This is ok, because the code only makes a distinction between the cases where the counter is below or at the limit. This also prepares for replacing the counter with an enum in a follow-up change. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-10-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp_link_training.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index e766f7c323f7..f03e05c730a2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1831,7 +1831,8 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - link_training->seq_train_failures++; + if (link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) + link_training->seq_train_failures++; /* * Ignore the link failure in CI From cf3df2124adcc6266df19226c096cd6dccb3dcbc Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:23 +0300 Subject: [PATCH 034/186] drm/i915/dp_link_training: Check for pending autoretrain explicitly Check explicitly for a pending autoretrain by matching seq_train_failures == 1. This makes the actual condition clear, since at the point where the counter is checked it is also below MAX_SEQ_TRAIN_FAILURES. This also prepares for replacing the counter with an enum in a follow-up change. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-11-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp_link_training.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index f03e05c730a2..cbac9a70e8d5 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1305,7 +1305,7 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp, if (!display->hotplug.ignore_long_hpd && link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) { - int delay_ms = link_training->seq_train_failures ? 0 : 2000; + int delay_ms = link_training->seq_train_failures == 1 ? 0 : 2000; intel_encoder_link_check_queue_work(encoder, delay_ms); } @@ -2006,7 +2006,7 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp) if (link_training->seq_train_failures >= MAX_SEQ_TRAIN_FAILURES) return false; - if (link_training->seq_train_failures) + if (link_training->seq_train_failures == 1) return true; /* Retrain if link not ok */ From a45b203d54185d09feab5e4de518673cf1106ee4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:24 +0300 Subject: [PATCH 035/186] drm/i915/dp_link_training: Add helper to query pending autoretrain Add link_recovery_autoretrain_pending() to make it clearer what the condition is about at its callers: an autoretrain work has been queued. This also prepares for replacing the sequential link training failure counter with an enum in a follow-up change. v2: Remove unnecessary function documentation. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-12-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp_link_training.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index cbac9a70e8d5..f6a8102a300c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1264,6 +1264,12 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp, return sink_status & DP_INTRA_HOP_AUX_REPLY_INDICATION ? 1 : 0; } +static bool +link_recovery_autoretrain_pending(struct intel_dp_link_training *link_training) +{ + return link_training->seq_train_failures == 1; +} + /** * intel_dp_stop_link_train - stop link training * @intel_dp: DP struct @@ -1305,7 +1311,7 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp, if (!display->hotplug.ignore_long_hpd && link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) { - int delay_ms = link_training->seq_train_failures == 1 ? 0 : 2000; + int delay_ms = link_recovery_autoretrain_pending(link_training) ? 0 : 2000; intel_encoder_link_check_queue_work(encoder, delay_ms); } @@ -2006,7 +2012,7 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp) if (link_training->seq_train_failures >= MAX_SEQ_TRAIN_FAILURES) return false; - if (link_training->seq_train_failures == 1) + if (link_recovery_autoretrain_pending(link_training)) return true; /* Retrain if link not ok */ From 1a5e170bf7d8de8a501dc71f8cb79b7af0f47e6b Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:25 +0300 Subject: [PATCH 036/186] drm/i915/dp_link_training: Add helper to query allowed autoretrain Add link_recovery_autoretrain_allowed() to make it clearer what the condition is about at its callers: queuing work for and starting an autoretrain is allowed. This also prepares for replacing the sequential link training failure counter with an enum in a follow-up change. v2: Convert link_recovery_autoretrain_allowed()'s documentation to be a non kernel-doc comment and detail what an allowed autoretrain is. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-13-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index f6a8102a300c..6c48219d770b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1270,6 +1270,28 @@ link_recovery_autoretrain_pending(struct intel_dp_link_training *link_training) return link_training->seq_train_failures == 1; } +/* + * Automatic retraining is a driver-driven link recovery mechanism that + * retrains the link with the current userspace provided modeset + * configuration and link parameters. + * + * Autoretrain is allowed while the link configurations available for + * retraining, i.e. those not disabled yet via fallback selection, still + * make it possible to retrain the link for the current userspace provided + * modeset configuration. + * + * Once automatic retraining is no longer allowed, userspace driven link + * recovery via userspace notifications and userspace modesets takes over. + * + * See also: + * - DOC: DisplayPort link training + */ +static bool +link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training) +{ + return link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES; +} + /** * intel_dp_stop_link_train - stop link training * @intel_dp: DP struct @@ -1310,7 +1332,7 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp, intel_hpd_unblock(encoder); if (!display->hotplug.ignore_long_hpd && - link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) { + link_recovery_autoretrain_allowed(link_training)) { int delay_ms = link_recovery_autoretrain_pending(link_training) ? 0 : 2000; intel_encoder_link_check_queue_work(encoder, delay_ms); @@ -1837,7 +1859,7 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - if (link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) + if (link_recovery_autoretrain_allowed(link_training)) link_training->seq_train_failures++; /* @@ -1857,7 +1879,7 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - if (link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES) + if (link_recovery_autoretrain_allowed(link_training)) return; if (intel_dp_schedule_fallback_link_training(state, intel_dp, crtc_state)) @@ -2009,7 +2031,7 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp) intel_dp->lane_count)) return false; - if (link_training->seq_train_failures >= MAX_SEQ_TRAIN_FAILURES) + if (!link_recovery_autoretrain_allowed(link_training)) return false; if (link_recovery_autoretrain_pending(link_training)) From 9bd47678708cc22ffd59a08a9b2553a72c1d8319 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:26 +0300 Subject: [PATCH 037/186] drm/i915/dp_link_training: Add helper to mark link training failure Add link_recovery_mark_train_failure() to record the failure and make the link recovery state transition explicit after a link training failure: recovery can continue with an autoretrain, or must be handed over to userspace after fallback selection. This also prepares for replacing the sequential link training failure counter with an enum in a follow-up change. v2: (Jani) - Convert link_recovery_mark_train_failure()'s documentation to be a non kernel-doc comment. - Rename can_autoretrain flag to autoretrain_allowed. Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-14-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 6c48219d770b..e0bb9f45b0c8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1292,6 +1292,28 @@ link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training) return link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES; } +/* + * Record a link training failure and advance the recovery state to + * indicate the next required recovery step. + * + * The caller must proceed with recovery as instructed by the return + * value, either via automatic retraining or, once automatic retraining + * is no longer possible, via userspace modesets after fallback + * selection. + * + * See also: + * - DOC: DisplayPort link training + */ +static bool +link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) +{ + if (link_recovery_autoretrain_allowed(link_training)) + /* Move to autoretrain pending or autoretrain disabled state. */ + link_training->seq_train_failures++; + + return link_recovery_autoretrain_allowed(link_training); +} + /** * intel_dp_stop_link_train - stop link training * @intel_dp: DP struct @@ -1828,6 +1850,7 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, struct intel_encoder *encoder = &dig_port->base; struct intel_dp_link_training *link_training = intel_dp->link.training; + bool autoretrain_allowed; bool passed; /* * Reinit the LTTPRs here to ensure that they are switched to @@ -1859,8 +1882,7 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - if (link_recovery_autoretrain_allowed(link_training)) - link_training->seq_train_failures++; + autoretrain_allowed = link_recovery_mark_train_failure(link_training); /* * Ignore the link failure in CI @@ -1879,7 +1901,7 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - if (link_recovery_autoretrain_allowed(link_training)) + if (autoretrain_allowed) return; if (intel_dp_schedule_fallback_link_training(state, intel_dp, crtc_state)) From 1536edeee040f98455b6117a2f57bf54fab088b3 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:27 +0300 Subject: [PATCH 038/186] drm/i915/dp_link_training: Add helper to reset link recovery state Add link_recovery_reset() to make it explicit when link recovery is no longer needed and the recovery state can be cleared. This also prepares for replacing the sequential link training failure counter with an enum in a follow-up change. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-15-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index e0bb9f45b0c8..dc1ad0fc6bd8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1314,6 +1314,18 @@ link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) return link_recovery_autoretrain_allowed(link_training); } +/** + * link_recovery_reset - reset the link recovery state + * @link_training: link training state + * + * Reset the link recovery state to indicate that no link recovery is + * required. + */ +static void link_recovery_reset(struct intel_dp_link_training *link_training) +{ + link_training->seq_train_failures = 0; +} + /** * intel_dp_stop_link_train - stop link training * @intel_dp: DP struct @@ -1878,7 +1890,7 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, link_training->force_train_failure--; lt_dbg(intel_dp, DP_PHY_DPRX, "Forcing link training failure\n"); } else if (passed) { - link_training->seq_train_failures = 0; + link_recovery_reset(link_training); return; } @@ -2543,7 +2555,7 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) void intel_dp_link_training_reset(struct intel_dp_link_training *link_training) { link_training->retrain_disabled = false; - link_training->seq_train_failures = 0; + link_recovery_reset(link_training); } struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp) From 4ba52dc9b4e088ef1eb574ab92565e866f2160c4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:28 +0300 Subject: [PATCH 039/186] drm/i915/dp_link_training: Track link recovery state with an enum Replace the sequential link training failure counter with an explicit link recovery state enum. This makes the recovery states and transitions clearer: idle, automatic retraining pending, and automatic retraining disabled. A follow-up change will also move the retrain_disabled flag into this enum. v2: (Jani) - Convert enum intel_dp_link_recovery_state's documentation to be a non kernel-doc comment. - Compare against / set enum values explicitly. Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-16-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 71 ++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index dc1ad0fc6bd8..7f0918b9b698 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -62,14 +62,54 @@ lt_dbg(_intel_dp, _dp_phy, "Sink disconnected: " _format, ## __VA_ARGS__); \ } while (0) -#define MAX_SEQ_TRAIN_FAILURES 2 +/* + * enum intel_dp_link_recovery_state - LT recovery state + * @INTEL_DP_LINK_RECOVERY_IDLE: + * No link training failure is currently tracked and no recovery is + * in progress. This is the initial state after driver initialization, + * power state transitions, sink (re-)connection, or after a successful + * link training. + * + * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + * A first link training failure has been observed and an automatic + * retraining attempt with the same link parameters is pending. Exactly + * one such attempt is allowed before switching to userspace-driven + * recovery. + * + * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED: + * Automatic retraining is no longer possible. At this point, a + * fallback selection is made and userspace is notified to take over + * recovery, performing modesets with parameters it determines are + * required. The driver then selects a link configuration from the + * remaining fallback configuration set. Subsequent link training + * failures trigger further fallback selections and userspace + * notifications. + * + * Describes the link recovery state used by the Intel DP link recovery + * logic. + * + * See also: + * - link_recovery_autoretrain_pending() + * - link_recovery_autoretrain_allowed() + * - link_recovery_mark_train_failure() + * - link_recovery_reset() + */ +enum intel_dp_link_recovery_state { + /* + * Keep the enum values ordered from least to most severe + * recovery state; helper logic relies on that ordering. + */ + INTEL_DP_LINK_RECOVERY_IDLE, + INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING, + INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED, +}; struct intel_dp_link_training { struct intel_dp *dp; + enum intel_dp_link_recovery_state recovery_state; + bool retrain_disabled; - /* Sequential link training failures after a passing LT */ - int seq_train_failures; int force_train_failure; bool force_retrain; }; @@ -1267,7 +1307,7 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp, static bool link_recovery_autoretrain_pending(struct intel_dp_link_training *link_training) { - return link_training->seq_train_failures == 1; + return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING; } /* @@ -1289,7 +1329,13 @@ link_recovery_autoretrain_pending(struct intel_dp_link_training *link_training) static bool link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training) { - return link_training->seq_train_failures < MAX_SEQ_TRAIN_FAILURES; + switch (link_training->recovery_state) { + case INTEL_DP_LINK_RECOVERY_IDLE: + case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + return true; + default: + return false; + } } /* @@ -1307,9 +1353,16 @@ link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training) static bool link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) { - if (link_recovery_autoretrain_allowed(link_training)) - /* Move to autoretrain pending or autoretrain disabled state. */ - link_training->seq_train_failures++; + switch (link_training->recovery_state) { + case INTEL_DP_LINK_RECOVERY_IDLE: + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING; + break; + case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED; + break; + default: + break; + } return link_recovery_autoretrain_allowed(link_training); } @@ -1323,7 +1376,7 @@ link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) */ static void link_recovery_reset(struct intel_dp_link_training *link_training) { - link_training->seq_train_failures = 0; + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_IDLE; } /** From 3a6adea5d12c9338ff901e54ec0f49622176b42e Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:29 +0300 Subject: [PATCH 040/186] drm/i915/dp_link_training: Add no-fallback link recovery state Replace the misnamed retrain_disabled flag with a dedicated link recovery state indicating that no fallback link configurations remain. This clarifies the meaning of the state: it has always represented the situation where no further fallback link configurations are available. While at it, add a TODO comment to the debugfs entry, to expose this state via a more appropriately named entry. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-17-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 7f0918b9b698..ad67f9df46d7 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -85,13 +85,21 @@ * failures trigger further fallback selections and userspace * notifications. * + * @INTEL_DP_LINK_RECOVERY_NO_FALLBACK: + * Fallback selection is no longer possible, as no usable fallback link + * configurations remain. Recovery must proceed via userspace modesets + * using the remaining allowed link configuration. Userspace continues + * to be notified of subsequent link training failures. + * * Describes the link recovery state used by the Intel DP link recovery * logic. * * See also: * - link_recovery_autoretrain_pending() * - link_recovery_autoretrain_allowed() + * - link_recovery_has_no_fallback() * - link_recovery_mark_train_failure() + * - link_recovery_mark_no_fallback() * - link_recovery_reset() */ enum intel_dp_link_recovery_state { @@ -102,6 +110,7 @@ enum intel_dp_link_recovery_state { INTEL_DP_LINK_RECOVERY_IDLE, INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING, INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED, + INTEL_DP_LINK_RECOVERY_NO_FALLBACK, }; struct intel_dp_link_training { @@ -109,7 +118,6 @@ struct intel_dp_link_training { enum intel_dp_link_recovery_state recovery_state; - bool retrain_disabled; int force_train_failure; bool force_retrain; }; @@ -1338,6 +1346,12 @@ link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training) } } +static bool +link_recovery_has_no_fallback(struct intel_dp_link_training *link_training) +{ + return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_NO_FALLBACK; +} + /* * Record a link training failure and advance the recovery state to * indicate the next required recovery step. @@ -1367,6 +1381,13 @@ link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) return link_recovery_autoretrain_allowed(link_training); } +/* Record that no more link fallback configuration is available. */ +static void +link_recovery_mark_no_fallback(struct intel_dp_link_training *link_training) +{ + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_NO_FALLBACK; +} + /** * link_recovery_reset - reset the link recovery state * @link_training: link training state @@ -1972,7 +1993,7 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, if (intel_dp_schedule_fallback_link_training(state, intel_dp, crtc_state)) return; - link_training->retrain_disabled = true; + link_recovery_mark_no_fallback(link_training); if (!passed) lt_err(intel_dp, DP_PHY_DPRX, "Can't reduce link training parameters after failure\n"); @@ -2567,7 +2588,8 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data) intel_dp_flush_connector_commits(connector); - seq_printf(m, "%s\n", str_yes_no(link_training->retrain_disabled)); + /* TODO: Expose this via a debugfs entry reflecting what the state represents. */ + seq_printf(m, "%s\n", str_yes_no(link_recovery_has_no_fallback(link_training))); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2607,7 +2629,6 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) void intel_dp_link_training_reset(struct intel_dp_link_training *link_training) { - link_training->retrain_disabled = false; link_recovery_reset(link_training); } From 8e0e0fcdfe463c09e93d5dd677368b355866aef9 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:30 +0300 Subject: [PATCH 041/186] drm/i915/display: Factor out a helper to modeset a pipe with atomic state Factor out a helper modesetting a pipe that accepts an existing atomic state. This prepares for a follow-up change that needs to allocate its own atomic state. v2: Rebase on upstream drm_atomic_state -> drm_atomic_commit rename. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-18-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 30 ++++++++++++++------ drivers/gpu/drm/i915/display/intel_display.h | 3 ++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 8e269b71f18e..b15c28675d28 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5658,18 +5658,15 @@ int intel_modeset_all_pipes_late(struct intel_atomic_state *state, return 0; } -int intel_modeset_commit_pipes(struct intel_display *display, - u8 pipe_mask, - struct drm_modeset_acquire_ctx *ctx) +int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *intel_state, + u8 pipe_mask, + struct drm_modeset_acquire_ctx *ctx) { - struct drm_atomic_commit *state; + struct drm_atomic_commit *state = &intel_state->base; + struct intel_display *display = to_intel_display(intel_state); struct intel_crtc *crtc; int ret; - state = drm_atomic_commit_alloc(display->drm); - if (!state) - return -ENOMEM; - state->acquire_ctx = ctx; to_intel_atomic_state(state)->internal = true; @@ -5687,6 +5684,23 @@ int intel_modeset_commit_pipes(struct intel_display *display, ret = drm_atomic_commit(state); out: + return ret; +} + +int intel_modeset_commit_pipes(struct intel_display *display, + u8 pipe_mask, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_commit *state; + int ret; + + state = drm_atomic_commit_alloc(display->drm); + if (!state) + return -ENOMEM; + + ret = intel_modeset_commit_pipes_for_atomic_state(to_intel_atomic_state(state), + pipe_mask, ctx); + drm_atomic_commit_put(state); return ret; diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 1963dbc80221..98b589e8360d 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -474,6 +474,9 @@ int intel_modeset_pipes_in_mask_early(struct intel_atomic_state *state, const char *reason, u8 pipe_mask); int intel_modeset_all_pipes_late(struct intel_atomic_state *state, const char *reason); +int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state, + u8 pipe_mask, + struct drm_modeset_acquire_ctx *ctx); int intel_modeset_commit_pipes(struct intel_display *display, u8 pipe_mask, struct drm_modeset_acquire_ctx *ctx); From 0fd9803a2e49a0e7e74b218d1758fef7673aea0d Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:31 +0300 Subject: [PATCH 042/186] drm/i915/display: Simplify intel_modeset_commit_pipes_for_atomic_state() Simplify accessing the DRM atomic state via the intel atomic state in intel_modeset_commit_pipes_for_atomic_state(), which also allows dropping the cached DRM state pointer. Also streamline the success/error return flows. v2: Rebase on upstream drm_atomic_state -> drm_atomic_commit rename. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-19-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 22 +++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index b15c28675d28..bdf02b67c1d8 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5658,33 +5658,27 @@ int intel_modeset_all_pipes_late(struct intel_atomic_state *state, return 0; } -int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *intel_state, +int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state, u8 pipe_mask, struct drm_modeset_acquire_ctx *ctx) { - struct drm_atomic_commit *state = &intel_state->base; - struct intel_display *display = to_intel_display(intel_state); + struct intel_display *display = to_intel_display(state); struct intel_crtc *crtc; - int ret; - state->acquire_ctx = ctx; - to_intel_atomic_state(state)->internal = true; + state->base.acquire_ctx = ctx; + state->internal = true; for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) { struct intel_crtc_state *crtc_state = - intel_atomic_get_crtc_state(state, crtc); + intel_atomic_get_crtc_state(&state->base, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto out; - } + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); crtc_state->uapi.connectors_changed = true; } - ret = drm_atomic_commit(state); -out: - return ret; + return drm_atomic_commit(&state->base); } int intel_modeset_commit_pipes(struct intel_display *display, From 9074b9ff90dc2d3587c7469a8ee58d6af7d97737 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:32 +0300 Subject: [PATCH 043/186] drm/i915/dp_link_training: Allocate atomic state for autoretrain modeset Allocate a local atomic state for the autoretrain modeset. This prepares for a follow-up change that needs to access the state after the modeset for sending userspace notifications. v2: Rebase on upstream drm_atomic_state -> drm_atomic_commit rename. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-20-imre.deak@intel.com --- .../gpu/drm/i915/display/intel_dp_link_training.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index ad67f9df46d7..0231ca0cea30 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -27,6 +27,7 @@ #include #include +#include "intel_display.h" #include "intel_display_core.h" #include "intel_display_jiffies.h" #include "intel_display_types.h" @@ -2165,6 +2166,8 @@ static int intel_dp_retrain_link(struct intel_encoder *encoder, struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_dp_link_training *link_training = intel_dp->link.training; + struct intel_atomic_state *state; + struct drm_atomic_commit *_state; u8 pipe_mask; int ret; @@ -2194,9 +2197,15 @@ static int intel_dp_retrain_link(struct intel_encoder *encoder, encoder->base.base.id, encoder->base.name, str_yes_no(intel_dp_link_training_get_force_retrain(link_training))); - ret = intel_modeset_commit_pipes(display, pipe_mask, ctx); + _state = drm_atomic_commit_alloc(display->drm); + if (!_state) + return -ENOMEM; + + state = to_intel_atomic_state(_state); + + ret = intel_modeset_commit_pipes_for_atomic_state(state, pipe_mask, ctx); if (ret == -EDEADLK) - return ret; + goto out; intel_dp_link_training_set_force_retrain(link_training, false); @@ -2205,6 +2214,8 @@ static int intel_dp_retrain_link(struct intel_encoder *encoder, "[ENCODER:%d:%s] link retraining failed: %pe\n", encoder->base.base.id, encoder->base.name, ERR_PTR(ret)); +out: + drm_atomic_commit_put(&state->base); return ret; } From 11a42e9214033ddfa600787196cbbe4a418dd968 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:33 +0300 Subject: [PATCH 044/186] drm/i915/dp_link_training: Disallow autoretrains after failed modeset intel_dp_check_link_state() and intel_dp_link_params_valid() perform only a coarse validation of the link configuration used by the active mode against the available configurations (as constrained by the link training fallback code after a previous LT failure). Even if these coarse checks find a seemingly usable configuration, the modeset check, which performs full verification, may still fail. Disallow further autoretrain attempts if an autoretrain modeset fails. Further attempts would just reuse the same modeset parameters and fail in the same way. Autoretrain will be reallowed unconditionally when the sink reports a change in its capabilities. This allows an autoretrain to proceed once both the link validation and modeset checks confirm a usable configuration. Also clarify in intel_dp_check_link_state() and intel_dp_link_params_valid() that these checks are coarse and that a full validation is only performed by the subsequent atomic modeset check. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-21-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 95 ++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 0231ca0cea30..77e7beb65cdd 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -100,6 +100,7 @@ * - link_recovery_autoretrain_allowed() * - link_recovery_has_no_fallback() * - link_recovery_mark_train_failure() + * - link_recovery_mark_autoretrain_modeset_failure() * - link_recovery_mark_no_fallback() * - link_recovery_reset() */ @@ -1362,6 +1363,13 @@ link_recovery_has_no_fallback(struct intel_dp_link_training *link_training) * is no longer possible, via userspace modesets after fallback * selection. * + * Note that the error reported via this function is the error seen by + * the link training failure handler proper after an actual link + * training failure indicated by the sink device, and so the error and + * corresponding actions required are distinct from an autoretrain + * modeset failure. See link_recovery_mark_autoretrain_modeset_failure() to + * report a modeset failure. + * * See also: * - DOC: DisplayPort link training */ @@ -1382,6 +1390,29 @@ link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) return link_recovery_autoretrain_allowed(link_training); } +/* + * Record a failure of the autoretrain modeset before link training + * itself could run. + * + * Note that the error reported via this function and the corresponding + * expected actions are distinct from an actual link training failure: + * the modeset failed before a link training attempt could be performed. + * See link_recovery_mark_train_failure() to report an actual link + * training failure. + * + * Update the state to indicate that further recovery is to be delegated to + * userspace via a regular modeset. + * + * See also: + * - DOC: DisplayPort link training + */ +static void +link_recovery_mark_autoretrain_modeset_failure(struct intel_dp_link_training *link_training) +{ + if (link_recovery_autoretrain_allowed(link_training)) + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED; +} + /* Record that no more link fallback configuration is available. */ static void link_recovery_mark_no_fallback(struct intel_dp_link_training *link_training) @@ -2029,6 +2060,23 @@ bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, * FIXME: we need to synchronize the current link parameters with * hardware readout. Currently fast link training doesn't work on * boot-up. + * + * NOTE: + * This may be called from both serialized (locked and synced against + * async commit tails) and unserialized (e.g. HPD IRQ) contexts. It + * uses the current max link limits as upper bounds to reject + * obviously bogus values, even if those bounds may be observed in a + * transient or slightly stale state. + * + * This is not a full validation of the link configuration. Even in + * serialized contexts, additional constraints (e.g. source limitations, + * bandwidth checks, and other atomic state dependencies) are only + * verified during the atomic check of the subsequent commit. + * + * max_link_limits only provides independent upper bounds for rate and + * lane count. Callers must not assume it is itself an allowed link + * configuration. Although that happens to be true for now, it will + * stop being guaranteed once fallback depends only on disabled configs. */ if (link_rate == 0 || link_rate > intel_dp->link.max_rate) @@ -2159,6 +2207,21 @@ static bool intel_dp_is_connected(struct intel_dp *intel_dp) intel_dp->is_mst; } +static void queue_modeset_retry_for_links_in_state(struct intel_atomic_state *state, + struct intel_encoder *encoder, + u8 pipe_mask) +{ + const struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + + for_each_new_intel_crtc_in_state(state, crtc, crtc_state) { + if (!(BIT(crtc->pipe) & pipe_mask)) + continue; + + intel_dp_queue_modeset_retry_for_link(state, encoder, crtc_state); + } +} + static int intel_dp_retrain_link(struct intel_encoder *encoder, struct drm_modeset_acquire_ctx *ctx) { @@ -2209,11 +2272,23 @@ static int intel_dp_retrain_link(struct intel_encoder *encoder, intel_dp_link_training_set_force_retrain(link_training, false); - if (ret) + if (ret) { drm_dbg_kms(display->drm, "[ENCODER:%d:%s] link retraining failed: %pe\n", encoder->base.base.id, encoder->base.name, ERR_PTR(ret)); + /* + * intel_dp_needs_link_retrain() only performs a coarse check of + * retrainability, so the modeset commit may still fail. Disable + * further auto-retrain attempts in that case. + * + * A sink capability change may restore the retrainable state (see + * intel_dp_update_sink_caps(), intel_dp_reset_link_params()), + * allowing retraining to be attempted again. + */ + link_recovery_mark_autoretrain_modeset_failure(link_training); + queue_modeset_retry_for_links_in_state(state, encoder, pipe_mask); + } out: drm_atomic_commit_put(&state->base); @@ -2237,6 +2312,24 @@ void intel_dp_check_link_state(struct intel_dp *intel_dp) if (!intel_dp_is_connected(intel_dp)) return; + /* + * NOTE: + * This may race with an ongoing modeset updating the max link limits + * and, with that, the link's retrainability, so + * intel_dp_needs_link_retrain() may observe stale state. + * + * This is harmless: stale params captured as valid may spuriously + * allow retraining here, but the decision is rechecked later in a + * properly serialized context. + * + * Conversely, stale params captured as invalid may skip retraining, + * but that can only happen before the modeset has completed its own + * link training for the new, valid configuration, after which the + * link state is rechecked. + * + * See intel_dp_link_params_valid() for capturing and validating the + * params. + */ if (!intel_dp_needs_link_retrain(intel_dp)) return; From e065bc63fdc1eab77ab0d3d92db23a03b85e297e Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:34 +0300 Subject: [PATCH 045/186] drm/i915/dp_link_training: Fix kernel-doc of intel_dp_init_lttpr_and_dprx_caps() Fix the list formatting of return values in intel_dp_read_dprx_caps()'s kernel-doc. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-22-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp_link_training.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 77e7beb65cdd..db7b47665cb7 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -312,12 +312,12 @@ int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_S * transparent mode link training mode. * * Returns: - * >0 if LTTPRs were detected and the non-transparent LT mode was set. The + * - >0 if LTTPRs were detected and the non-transparent LT mode was + * set. The DPRX capabilities are read out. + * - 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of + * a detection failure and the transparent LT mode was set. The * DPRX capabilities are read out. - * 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of a - * detection failure and the transparent LT mode was set. The DPRX - * capabilities are read out. - * <0 Reading out the DPRX capabilities failed. + * - <0 Reading out the DPRX capabilities failed. */ int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp) { From 361fd015ad8e38cd17b6351f84e4f4ec15f0d040 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 1 Jun 2026 12:38:35 +0300 Subject: [PATCH 046/186] drm/i915/dp_link_training: Document DP link recovery logic Add kernel-doc documentation describing the Intel DP link training recovery state machine and the sequence of automatic retraining, fallback selection, and userspace notification. v2: - Rebase on dedicated intel-display documentation change. - Remove unnecessary indent in section bodies. (Jani) - Add recovery flowcharts. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260601093836.3057345-23-imre.deak@intel.com --- .../gpu/intel-display/dp-link-training.rst | 8 + Documentation/gpu/intel-display/index.rst | 1 + .../drm/i915/display/intel_dp_link_training.c | 323 ++++++++++++++++++ 3 files changed, 332 insertions(+) create mode 100644 Documentation/gpu/intel-display/dp-link-training.rst diff --git a/Documentation/gpu/intel-display/dp-link-training.rst b/Documentation/gpu/intel-display/dp-link-training.rst new file mode 100644 index 000000000000..d0bde965021d --- /dev/null +++ b/Documentation/gpu/intel-display/dp-link-training.rst @@ -0,0 +1,8 @@ +.. SPDX-License-Identifier: MIT +.. Copyright © 2026 Intel Corporation + +DisplayPort Link Training +========================= + +.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_training.c + :doc: DisplayPort link training diff --git a/Documentation/gpu/intel-display/index.rst b/Documentation/gpu/intel-display/index.rst index 01c3d1e576b7..6fa929d82c38 100644 --- a/Documentation/gpu/intel-display/index.rst +++ b/Documentation/gpu/intel-display/index.rst @@ -38,6 +38,7 @@ driver. The display driver isn't an independent driver in that sense. fifo-underrun frontbuffer hotplug + dp-link-training plane psr snps-phy diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index db7b47665cb7..97cb407d084c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -42,6 +42,328 @@ #include "intel_panel.h" #include "intel_psr.h" +/** + * DOC: DisplayPort link training + * + * This documents the Intel DisplayPort link training implementation and + * its internal interfaces, with a current focus on link recovery. + * + * Documentation of the full link training procedure is not yet included. + * + * The Intel DP link recovery logic governs how the driver reacts to + * link training failures and to links that degrade asynchronously + * after a previously successful training. Recovery is first attempted + * via automatic retraining (``autoretrain``) and, when that is no + * longer possible, by selecting fallback link configurations and + * notifying userspace to recover the link via a modeset. + * + * Recovery sequence and userspace notification + * -------------------------------------------- + * + * After the first link training failure following initialization or a + * previously successful training, recovery is first attempted by the + * driver via automatic retraining, without userspace involvement. + * During this phase, a given link configuration is attempted twice + * before being abandoned: after the initial link training failure, an + * automatic retraining modeset is performed with the same link + * parameters, constituting the second attempt. + * + * Once automatic retraining is no longer possible, recovery is delegated + * to userspace, which must select a new modeset configuration, as the + * kernel must not do so. From this point onwards, each link configuration + * may be attempted only once as userspace iterates through alternative + * configurations. A successful link training restores the automatic + * retraining model for subsequent failures. + * + * The failure of the last automatic retraining attempt is reported to + * userspace, and from that point onward the driver notifies userspace of + * each subsequent failure. This allows userspace to both initiate + * recovery via modesets and observe the outcome of those recovery + * attempts, even when no further fallback configurations remain. + * + * Link training failures are always reported to userspace, even when they + * result from a kernel-internal modeset. Such modesets only re-apply the + * existing userspace-provided state and must not modify it. A failure + * triggered by such a modeset is therefore treated the same as a link + * degradation after a previously successful training, and recovery is + * handled by userspace in place of the kernel caller. + * + * Contexts + * -------- + * + * The following execution contexts (A/B/C) describe how the different + * recovery states are reached but are not themselves implementation + * states. The actual state machine is defined by &enum + * intel_dp_link_training_recovery_state. + * + * A. Modeset context: + * + * Triggered by: + * - link training during a modeset, or + * - via the "i915_dp_force_link_training_failure" debugfs entry, + * forcing this path by emulating a link training failure. + * + * Transitions: + * - A1 Link training succeeds. + * + * A link check work to recover any degraded link is scheduled + * (and handled if needed in context B). + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * - A2 First link training fails after initialization or a previously + * successful link training. + * + * An automatic retraining work is scheduled (and handled in + * context B) with the same link parameters with which the link + * training failed. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING. + * + * - A3 Link training fails again after A2 or A3. + * + * Through fallback selection, the driver attempts to restrict the + * allowed link configurations for subsequent modesets. This may + * be done either by lowering global limits (rate/lane caps), or by + * disabling only the currently failing configuration while leaving + * all other configurations allowed, even if they use higher rate or + * lane count. + * + * (The current implementation may still apply parameter capping as + * a coarse fallback selection mechanism. This is transitional and is + * expected to be replaced by a scheme that disables only the failing + * configuration, rather than removing configurations that have not + * been observed to fail and may still train successfully.) + * + * This case may repeat in a loop: + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED -> + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * via repeated A3a -> A3a transitions until the configuration fallback + * space is exhausted, reaching the A3b terminal case. + * + * - A3a Fallback selection succeeds. + * + * Userspace is notified to retry the modeset. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED. + * + * - A3b Fallback selection fails. + * + * Userspace is notified of the failure and may continue recovery + * by retrying the modeset with the remaining allowed link + * configuration. + * + * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK. + * + * B. Automatic retraining context: + * + * Triggered by: + * - after a successful link training in context A1 followed by + * asynchronous link degradation, or + * - after the first failed link training attempt in context A2, or + * - via the "i915_dp_force_link_retrain" debugfs entry, which may + * bypass normal gating and force this path. + * + * Transitions: + * - B1 ``Autoretrain`` modeset check and link training succeeds. + * + * The case is handled as in A1, scheduling a link check work to + * recover any degraded link. + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * - B2 ``Autoretrain`` modeset check succeeds but link training fails. + * + * - B2a Previously the link degraded asynchronously (current state + * is %INTEL_DP_LINK_RECOVERY_IDLE). + * + * This corresponds to a first failure in a new failure + * sequence and is handled as in A2: an automatic retraining + * attempt is scheduled with the same link parameters. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING. + * + * - B2b Previously a link training failed (current state is + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING). + * + * In non-regular (debug-forced) scenarios this may also be + * reached from + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED or + * %INTEL_DP_LINK_RECOVERY_NO_FALLBACK, effectively behaving + * like a userspace-driven recovery attempt. + * + * The failure is handled as in A3, performing a fallback selection: + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED (via A3a). + * + * or + * + * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK (via A3b). + * + * - B3 ``Autoretrain`` modeset check fails (and hence the link training + * cannot be started). + * + * The modeset check may fail, for example, due to external conditions + * such as changed shared link bandwidth, which can make previously + * valid modeset parameters no longer acceptable. + * + * In this case, automatic retraining is disabled without selecting + * a fallback configuration. The driver hands recovery over to + * userspace without modifying the allowed configuration set, so a + * subsequent userspace modeset will retry with the current link + * configuration. Userspace is in a better position to select new + * modeset parameters (e.g. video mode or enabled outputs) that + * satisfy the updated constraints, as the driver is only allowed + * to retry the modeset with the existing userspace-provided modeset + * configuration. + * + * This policy preserves the normal retry model, where a given link + * configuration is attempted twice in the automatic retraining + * flow before being abandoned: after a first link training failure, + * an automatic retraining modeset is performed with the same link + * parameters, and if its atomic check passes, the link training + * itself may either succeed or fail, constituting the second + * attempt. In this case, however, the retry modeset's atomic check + * failed, so no second link training attempt with those parameters + * was performed, and selecting a fallback would cause that + * configuration to be tried only once rather than twice. + * + * The userspace-driven link recovery continues with subsequent + * userspace modesets handled in A3. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED. + * + * C. State reset context: + * + * Triggered by: + * - sink capability changes, or + * - sink disconnect/reconnect, or + * - system suspend/resume or power transitions where HPD + * handling may have been suppressed, or + * - successful link training. + * + * Transitions: + * - The recovery state is reset from any of the recovery states + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * After reset, the driver may re-check link status and schedule + * retraining if the link is found to remain degraded. + * + * State transition summary + * ------------------------ + * + * - From %INTEL_DP_LINK_RECOVERY_IDLE + * + * - To %INTEL_DP_LINK_RECOVERY_IDLE + * + * - | In context: B1 + * | Action: no action + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING + * + * - | In contexts: A2, B2a + * | Action: queue ``autoretrain`` work + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In context: B3 + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In contexts: A3a, B2b + * | Action: select fallback configurations, notify userspace + * + * - | In context: B3 + * | Action: notify userspace + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b, B2b + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In contexts: A3a, B2b + * | Action: select fallback configurations, notify userspace + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b, B2b + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b + * | Action: notify userspace + * + * - From any state + * + * - To %INTEL_DP_LINK_RECOVERY_IDLE + * + * - | In contexts: C + * | Action: no action + * + * Recovery flows + * -------------- + * + * Userspace modeset link recovery:: + * + * [IDLE] + * | + * | userspace modeset link training fails + * | (autoretrain link recovery work scheduled) + * v + * [AUTORETRAIN_PENDING]-- autoretrain link recovery succeeds -> [IDLE] + * | + * | autoretrain link recovery modeset check or link training fails + * | + * +--o--+ + * modeset check fails | | link training fails + * (userspace notified) | | + * | o-------- no fallback (userspace notified) ---> [NO_FALLBACK] + * | | + * +-------------+ | | fallback selected (userspace notified) + * | | | | + * | v v v + * | [AUTORETRAIN_DISABLED]--- userspace link recovery succeeds ----> [IDLE] + * | | + * | | userspace link recovery fails + * | | + * +-------------------o------------- no fallback (userspace notified) ----> [NO_FALLBACK] + * fallback selected + * (userspace notified) + * + * Asynchronous link degradation recovery:: + * + * [IDLE] + * | + * | link degrades + * | (autoretrain link recovery performed) + * | + * o--- autoretrain link recovery succeeds ---> [IDLE] + * | + * | autoretrain link recovery modeset check or link training fails + * | + * +--o--+ + * modeset check fails | | link training fails + * (userspace notified) | | (autoretrain work scheduled) + * v v + * [AUTORETRAIN_DISABLED*] [AUTORETRAIN_PENDING*] + * + * ``*`` marks states where the sequence continues from the corresponding state + * in the Userspace modeset link recovery flow above. + * + */ + #define LT_MSG_PREFIX "[CONNECTOR:%d:%s][ENCODER:%d:%s][%s] " #define LT_MSG_ARGS(_intel_dp, _dp_phy) (_intel_dp)->attached_connector->base.base.id, \ (_intel_dp)->attached_connector->base.name, \ @@ -96,6 +418,7 @@ * logic. * * See also: + * - DOC: DisplayPort link training * - link_recovery_autoretrain_pending() * - link_recovery_autoretrain_allowed() * - link_recovery_has_no_fallback() From c51978a2d6ef6518fb638fa99eca0526bd903ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 29 May 2026 19:11:47 +0300 Subject: [PATCH 047/186] drm/i915/de: Remove the 2 usec fast timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently any "slow" wait will first try a "fast" wait with a 2 usec timeout, and then fall back to whatever timeout was specified originally. The "slow" wait will anyway start off with a mere 10 usec polling interval, so basically we can only save 8 usec with the "fast" wait (maybe a bit more given hrtimer setup costs etc.). I don't think we really do any operations in the display code where that kinds of 8 usec saving would be meaningful. So just get rid of the whole "fast" wait complication and go straight for the "slow" wait. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260529161147.17573-1-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_de.c | 40 ++++++------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c index 6daee9e82503..a7417905192d 100644 --- a/drivers/gpu/drm/i915/display/intel_de.c +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -9,11 +9,11 @@ #include "intel_de.h" -static int __intel_de_wait_for_register(struct intel_display *display, - intel_reg_t reg, u32 mask, u32 value, - unsigned int timeout_us, - u32 (*read)(struct intel_display *display, intel_reg_t reg), - u32 *out_val, bool is_atomic) +static int intel_de_wait_for_register(struct intel_display *display, + intel_reg_t reg, u32 mask, u32 value, + unsigned int timeout_us, + u32 (*read)(struct intel_display *display, intel_reg_t reg), + u32 *out_val, bool is_atomic) { const ktime_t end = ktime_add_us(ktime_get_raw(), timeout_us); int wait_max = 1000; @@ -60,28 +60,6 @@ static int __intel_de_wait_for_register(struct intel_display *display, return ret; } -static int intel_de_wait_for_register(struct intel_display *display, - intel_reg_t reg, u32 mask, u32 value, - unsigned int fast_timeout_us, - unsigned int slow_timeout_us, - u32 (*read)(struct intel_display *display, intel_reg_t reg), - u32 *out_value, bool is_atomic) -{ - int ret = -EINVAL; - - if (fast_timeout_us) - ret = __intel_de_wait_for_register(display, reg, mask, value, - fast_timeout_us, read, - out_value, is_atomic); - - if (ret && slow_timeout_us) - ret = __intel_de_wait_for_register(display, reg, mask, value, - slow_timeout_us, read, - out_value, is_atomic); - - return ret; -} - int intel_de_wait_us(struct intel_display *display, intel_reg_t reg, u32 mask, u32 value, unsigned int timeout_us, u32 *out_value) @@ -91,7 +69,7 @@ int intel_de_wait_us(struct intel_display *display, intel_reg_t reg, intel_dmc_wl_get(display, reg); ret = intel_de_wait_for_register(display, reg, mask, value, - timeout_us, 0, + timeout_us, intel_de_read, out_value, false); @@ -109,7 +87,7 @@ int intel_de_wait_ms(struct intel_display *display, intel_reg_t reg, intel_dmc_wl_get(display, reg); ret = intel_de_wait_for_register(display, reg, mask, value, - 2, timeout_ms * 1000, + timeout_ms * 1000, intel_de_read, out_value, false); @@ -123,7 +101,7 @@ int intel_de_wait_fw_ms(struct intel_display *display, intel_reg_t reg, u32 *out_value) { return intel_de_wait_for_register(display, reg, mask, value, - 2, timeout_ms * 1000, + timeout_ms * 1000, intel_de_read_fw, out_value, false); } @@ -133,7 +111,7 @@ int intel_de_wait_fw_us_atomic(struct intel_display *display, intel_reg_t reg, u32 *out_value) { return intel_de_wait_for_register(display, reg, mask, value, - timeout_us, 0, + timeout_us, intel_de_read_fw, out_value, true); } From 173455d429f27642ff4005e2c5168b4bc4a684d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:48 +0300 Subject: [PATCH 048/186] drm/i915/bw: Don't memcpy() pointlessly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Structs can be copied with a simple assingment. Eliminate the pointless memcpy(). Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-2-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index dc5a5b639d87..d0ceffc93f36 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -758,8 +758,7 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, display->bw.max[0].num_planes = 1; display->bw.max[0].num_qgv_points = qi.num_points; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) - memcpy(&display->bw.max[i], &display->bw.max[0], - sizeof(display->bw.max[0])); + display->bw.max[i] = display->bw.max[0]; /* * Xe2_HPD should always have exactly two QGV points representing From fd5f8a3a92096345979f85c5af068cd8ff9cddcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:49 +0300 Subject: [PATCH 049/186] drm/i915/bw: Streamline dg2_get_bw_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make dg2_get_bw_info() look a bit more like xe2_hpd_get_bw_info() so that we don't have so many different ways of writing the same stuff (namely the "set all plane groups to the same value" part). Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-3-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index d0ceffc93f36..887628144864 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -699,25 +699,15 @@ static int tgl_get_bw_info(struct intel_display *display, static void dg2_get_bw_info(struct intel_display *display) { - unsigned int deratedbw = display->platform.dg2_g11 ? 38000 : 50000; - int num_groups = ARRAY_SIZE(display->bw.max); int i; - /* - * DG2 doesn't have SAGV or QGV points, just a constant max bandwidth - * that doesn't depend on the number of planes enabled. So fill all the - * plane group with constant bw information for uniformity with other - * platforms. DG2-G10 platforms have a constant 50 GB/s bandwidth, - * whereas DG2-G11 platforms have 38 GB/s. - */ - for (i = 0; i < num_groups; i++) { - struct intel_bw_info *bi = &display->bw.max[i]; + display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000; - bi->num_planes = 1; - /* Need only one dummy QGV point per group */ - bi->num_qgv_points = 1; - bi->deratedbw[0] = deratedbw; - } + /* Bandwidth does not depend on # of planes; set all groups the same */ + display->bw.max[0].num_planes = 1; + display->bw.max[0].num_qgv_points = 1; + for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) + display->bw.max[i] = display->bw.max[0]; display->sagv.status = I915_SAGV_NOT_CONTROLLED; } From 8965d70181d25b95e61f9e68b59c035b18aceb34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:50 +0300 Subject: [PATCH 050/186] drm/i915/bw: Initialize num_planes sensibly for the first plane group in TGL+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The way the TGL+ bw algorithm works is that 'num_planes' is really a maximum number of allowed planes (whereas in the ICL version it was more of a minimum), and the assumption is that the first plane group (max[0]) can be used with any number of planes (tgl_max_bw_index() always returns 0 at the end). To make things a bit less weird let's just set the first plane group's num_planes to some big number to indicate it has no real limit on the number of planes. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-4-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 887628144864..4b5db4ca7773 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -629,6 +629,8 @@ static int tgl_get_bw_info(struct intel_display *display, */ clperchgroup = 4 * (8 / num_channels) * qi.deinterleave; + display->bw.max[0].num_planes = U8_MAX; + for (i = 0; i < num_groups; i++) { struct intel_bw_info *bi = &display->bw.max[i]; struct intel_bw_info *bi_next; @@ -701,10 +703,10 @@ static void dg2_get_bw_info(struct intel_display *display) { int i; + display->bw.max[0].num_planes = U8_MAX; display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000; /* Bandwidth does not depend on # of planes; set all groups the same */ - display->bw.max[0].num_planes = 1; display->bw.max[0].num_qgv_points = 1; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) display->bw.max[i] = display->bw.max[0]; @@ -731,6 +733,8 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, peakbw = tgl_peakbw(num_channels, qi.channel_width, icl_sagv_max_dclk(&qi)); maxdebw = min(soc_bw_params->deprogbwlimit * 1000, peakbw * DEPROGBWPCLIMIT / 100); + display->bw.max[0].num_planes = U8_MAX; + for (i = 0; i < qi.num_points; i++) { const struct intel_qgv_point *sp = &qi.points[i]; int bw = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); @@ -745,7 +749,6 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, } /* Bandwidth does not depend on # of planes; set all groups the same */ - display->bw.max[0].num_planes = 1; display->bw.max[0].num_qgv_points = qi.num_points; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) display->bw.max[i] = display->bw.max[0]; @@ -808,7 +811,7 @@ static unsigned int tgl_max_bw_index(struct intel_display *display, return i; } - return 0; + return UINT_MAX; } static unsigned int adl_psf_bw(struct intel_display *display, From 2421dec89de5d1cb5e6872f020e68f2ae13e8721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:51 +0300 Subject: [PATCH 051/186] drm/i915/bw: Move 'bi_next' to tighter scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move 'bi_next' into the scope where it's actually used. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-5-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 4b5db4ca7773..940f23e7dd4e 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -633,14 +633,13 @@ static int tgl_get_bw_info(struct intel_display *display, for (i = 0; i < num_groups; i++) { struct intel_bw_info *bi = &display->bw.max[i]; - struct intel_bw_info *bi_next; int clpchgroup; int j; clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i; if (i < num_groups - 1) { - bi_next = &display->bw.max[i + 1]; + struct intel_bw_info *bi_next = &display->bw.max[i + 1]; if (clpchgroup < clperchgroup) bi_next->num_planes = (ipqdepth - clpchgroup) / clpchgroup; From 8b58b18bf720233a1b78e3c1c37299e37858903b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:52 +0300 Subject: [PATCH 052/186] drn/i915/bw: s/num_points/num_qgv_points/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename 'num_points' to 'num_qgv_points' to make it a bit more specific. We already have the 'num_psf_points' counterpart. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-6-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 940f23e7dd4e..19717a45aee8 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -59,7 +59,7 @@ struct intel_psf_gv_point { struct intel_qgv_info { struct intel_qgv_point points[I915_NUM_QGV_POINTS]; struct intel_psf_gv_point psf_points[I915_NUM_PSF_GV_POINTS]; - u8 num_points; + u8 num_qgv_points; u8 num_psf_points; u8 t_bl; u8 max_numchannels; @@ -252,7 +252,7 @@ static int icl_get_qgv_points(struct intel_display *display, { int i, ret; - qi->num_points = dram_info->num_qgv_points; + qi->num_qgv_points = dram_info->num_qgv_points; qi->num_psf_points = dram_info->num_psf_gv_points; if (DISPLAY_VER(display) >= 14) { @@ -324,10 +324,10 @@ static int icl_get_qgv_points(struct intel_display *display, } if (drm_WARN_ON(display->drm, - qi->num_points > ARRAY_SIZE(qi->points))) - qi->num_points = ARRAY_SIZE(qi->points); + qi->num_qgv_points > ARRAY_SIZE(qi->points))) + qi->num_qgv_points = ARRAY_SIZE(qi->points); - for (i = 0; i < qi->num_points; i++) { + for (i = 0; i < qi->num_qgv_points; i++) { struct intel_qgv_point *sp = &qi->points[i]; ret = intel_read_qgv_point_info(display, sp, i); @@ -373,7 +373,7 @@ static int icl_sagv_max_dclk(const struct intel_qgv_info *qi) u16 dclk = 0; int i; - for (i = 0; i < qi->num_points; i++) + for (i = 0; i < qi->num_qgv_points; i++) dclk = max(dclk, qi->points[i].dclk); return dclk; @@ -544,10 +544,10 @@ static int icl_get_bw_info(struct intel_display *display, clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i; bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1; - bi->num_qgv_points = qi.num_points; + bi->num_qgv_points = qi.num_qgv_points; bi->num_psf_gv_points = qi.num_psf_points; - for (j = 0; j < qi.num_points; j++) { + for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -574,7 +574,7 @@ static int icl_get_bw_info(struct intel_display *display, * SAGV point, but we can't send PCode commands to restrict it * as it will fail and pointless anyway. */ - if (qi.num_points == 1) + if (qi.num_qgv_points == 1) display->sagv.status = I915_SAGV_NOT_CONTROLLED; else display->sagv.status = I915_SAGV_ENABLED; @@ -647,10 +647,10 @@ static int tgl_get_bw_info(struct intel_display *display, bi_next->num_planes = 0; } - bi->num_qgv_points = qi.num_points; + bi->num_qgv_points = qi.num_qgv_points; bi->num_psf_gv_points = qi.num_psf_points; - for (j = 0; j < qi.num_points; j++) { + for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -690,7 +690,7 @@ static int tgl_get_bw_info(struct intel_display *display, * SAGV point, but we can't send PCode commands to restrict it * as it will fail and pointless anyway. */ - if (qi.num_points == 1) + if (qi.num_qgv_points == 1) display->sagv.status = I915_SAGV_NOT_CONTROLLED; else display->sagv.status = I915_SAGV_ENABLED; @@ -734,7 +734,7 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, display->bw.max[0].num_planes = U8_MAX; - for (i = 0; i < qi.num_points; i++) { + for (i = 0; i < qi.num_qgv_points; i++) { const struct intel_qgv_point *sp = &qi.points[i]; int bw = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); @@ -748,7 +748,7 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, } /* Bandwidth does not depend on # of planes; set all groups the same */ - display->bw.max[0].num_qgv_points = qi.num_points; + display->bw.max[0].num_qgv_points = qi.num_qgv_points; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) display->bw.max[i] = display->bw.max[0]; @@ -756,7 +756,7 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, * Xe2_HPD should always have exactly two QGV points representing * battery and plugged-in operation. */ - drm_WARN_ON(display->drm, qi.num_points != 2); + drm_WARN_ON(display->drm, qi.num_qgv_points != 2); display->sagv.status = I915_SAGV_ENABLED; return 0; From d8ffe49c04a9457fa6933f93c077f3a128bdfb37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:53 +0300 Subject: [PATCH 053/186] drm/i915/bw: Move num_{qgv,psf}_points out from the plane group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only have a single num_{qgv,psf}_points value, there is no need to replicate it in each plane group. And drop the somewhat misplaced comments about pcode behaviour from {icl,tgl}_max_bw_index() while at it. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-7-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 52 ++++++++----------- .../gpu/drm/i915/display/intel_display_core.h | 4 +- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 19717a45aee8..6495924d0be8 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -155,8 +155,8 @@ static int adls_pcode_read_psf_gv_point_info(struct intel_display *display, static u16 icl_qgv_points_mask(struct intel_display *display) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; u16 qgv_points = 0, psf_points = 0; /* @@ -536,6 +536,9 @@ static int icl_get_bw_info(struct intel_display *display, ipqdepth = min(ipqdepthpch, display_bw_params->displayrtids / num_channels); qi.deinterleave = DIV_ROUND_UP(num_channels, is_y_tile(display) ? 4 : 2); + display->bw.num_qgv_points = qi.num_qgv_points; + display->bw.num_psf_gv_points = qi.num_psf_points; + for (i = 0; i < num_groups; i++) { struct intel_bw_info *bi = &display->bw.max[i]; int clpchgroup; @@ -544,9 +547,6 @@ static int icl_get_bw_info(struct intel_display *display, clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i; bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1; - bi->num_qgv_points = qi.num_qgv_points; - bi->num_psf_gv_points = qi.num_psf_points; - for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -629,6 +629,9 @@ static int tgl_get_bw_info(struct intel_display *display, */ clperchgroup = 4 * (8 / num_channels) * qi.deinterleave; + display->bw.num_qgv_points = qi.num_qgv_points; + display->bw.num_psf_gv_points = qi.num_psf_points; + display->bw.max[0].num_planes = U8_MAX; for (i = 0; i < num_groups; i++) { @@ -647,9 +650,6 @@ static int tgl_get_bw_info(struct intel_display *display, bi_next->num_planes = 0; } - bi->num_qgv_points = qi.num_qgv_points; - bi->num_psf_gv_points = qi.num_psf_points; - for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -702,11 +702,12 @@ static void dg2_get_bw_info(struct intel_display *display) { int i; + display->bw.num_qgv_points = 1; + display->bw.max[0].num_planes = U8_MAX; display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000; /* Bandwidth does not depend on # of planes; set all groups the same */ - display->bw.max[0].num_qgv_points = 1; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) display->bw.max[i] = display->bw.max[0]; @@ -732,6 +733,8 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, peakbw = tgl_peakbw(num_channels, qi.channel_width, icl_sagv_max_dclk(&qi)); maxdebw = min(soc_bw_params->deprogbwlimit * 1000, peakbw * DEPROGBWPCLIMIT / 100); + display->bw.num_qgv_points = qi.num_qgv_points; + display->bw.max[0].num_planes = U8_MAX; for (i = 0; i < qi.num_qgv_points; i++) { @@ -748,7 +751,6 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, } /* Bandwidth does not depend on # of planes; set all groups the same */ - display->bw.max[0].num_qgv_points = qi.num_qgv_points; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) display->bw.max[i] = display->bw.max[0]; @@ -767,6 +769,9 @@ static unsigned int icl_max_bw_index(struct intel_display *display, { int i; + if (qgv_point >= display->bw.num_qgv_points) + return UINT_MAX; + /* * Let's return max bw for 0 planes */ @@ -776,13 +781,6 @@ static unsigned int icl_max_bw_index(struct intel_display *display, const struct intel_bw_info *bi = &display->bw.max[i]; - /* - * Pcode will not expose all QGV points when - * SAGV is forced to off/min/med/max. - */ - if (qgv_point >= bi->num_qgv_points) - return UINT_MAX; - if (num_planes >= bi->num_planes) return i; } @@ -795,17 +793,13 @@ static unsigned int tgl_max_bw_index(struct intel_display *display, { int i; + if (qgv_point >= display->bw.num_qgv_points) + return UINT_MAX; + for (i = ARRAY_SIZE(display->bw.max) - 1; i >= 0; i--) { const struct intel_bw_info *bi = &display->bw.max[i]; - /* - * Pcode will not expose all QGV points when - * SAGV is forced to off/min/med/max. - */ - if (qgv_point >= bi->num_qgv_points) - return UINT_MAX; - if (num_planes <= bi->num_planes) return i; } @@ -941,7 +935,7 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state) static unsigned int icl_max_bw_qgv_point_mask(struct intel_display *display, int num_active_planes) { - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; unsigned int max_bw_point = 0; unsigned int max_bw = 0; int i; @@ -977,7 +971,7 @@ static u16 icl_prepare_qgv_points_mask(struct intel_display *display, static unsigned int icl_max_bw_psf_gv_point_mask(struct intel_display *display) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; unsigned int max_bw_point_mask = 0; unsigned int max_bw = 0; int i; @@ -1082,7 +1076,7 @@ static int mtl_find_qgv_points(struct intel_display *display, struct intel_bw_state *new_bw_state) { unsigned int best_rate = UINT_MAX; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; unsigned int qgv_peak_bw = 0; int i; int ret; @@ -1153,8 +1147,8 @@ static int icl_find_qgv_points(struct intel_display *display, const struct intel_bw_state *old_bw_state, struct intel_bw_state *new_bw_state) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; u16 psf_points = 0; u16 qgv_points = 0; int i; diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 09ce25a6d4b1..f13fa810ccca 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -329,10 +329,10 @@ struct intel_display { unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; /* Peak BW for each QGV point */ unsigned int peakbw[I915_NUM_QGV_POINTS]; - u8 num_qgv_points; - u8 num_psf_gv_points; u8 num_planes; } max[6]; + u8 num_qgv_points; + u8 num_psf_gv_points; } bw; struct { From 9037f07dfc09769c2f64713ab4a8e0c069280097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:54 +0300 Subject: [PATCH 054/186] drm/i915/bw: Move psf_bw[] out from the plane group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PSF bandwidth doesn't depend on the number of planes, so there is no need to repeat the same information for each plane group. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-8-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 17 ++++++----------- .../gpu/drm/i915/display/intel_display_core.h | 4 ++-- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 6495924d0be8..ef86fac8e664 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -673,16 +673,14 @@ static int tgl_get_bw_info(struct intel_display *display, i, j, bi->num_planes, bi->deratedbw[j], bi->peakbw[j]); } + } - for (j = 0; j < qi.num_psf_points; j++) { - const struct intel_psf_gv_point *sp = &qi.psf_points[j]; + for (i = 0; i < qi.num_psf_points; i++) { + const struct intel_psf_gv_point *sp = &qi.psf_points[i]; - bi->psf_bw[j] = adl_calc_psf_bw(sp->clk); + display->bw.psf_bw[i] = adl_calc_psf_bw(sp->clk); - drm_dbg_kms(display->drm, - "BW%d / PSF GV %d: num_planes=%d bw=%u\n", - i, j, bi->num_planes, bi->psf_bw[j]); - } + drm_dbg_kms(display->drm, "PSF GV %d: bw=%u\n", i, display->bw.psf_bw[i]); } /* @@ -810,10 +808,7 @@ static unsigned int tgl_max_bw_index(struct intel_display *display, static unsigned int adl_psf_bw(struct intel_display *display, int psf_gv_point) { - const struct intel_bw_info *bi = - &display->bw.max[0]; - - return bi->psf_bw[psf_gv_point]; + return display->bw.psf_bw[psf_gv_point]; } static unsigned int icl_qgv_bw(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index f13fa810ccca..58cd0961031b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -325,12 +325,12 @@ struct intel_display { struct intel_bw_info { /* for each QGV point */ unsigned int deratedbw[I915_NUM_QGV_POINTS]; - /* for each PSF GV point */ - unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; /* Peak BW for each QGV point */ unsigned int peakbw[I915_NUM_QGV_POINTS]; u8 num_planes; } max[6]; + /* for each PSF GV point */ + unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; u8 num_qgv_points; u8 num_psf_gv_points; } bw; From db7a45983f23468b3ef15ce6bf7fd9549544fce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:55 +0300 Subject: [PATCH 055/186] drm/i915/bw: Move peakbw[] out from the plane group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The peak bandwidth doesn't depend on the number of planes, so there is no need to repeat the same information for each plane group. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-9-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 24 ++++++++++++------- .../gpu/drm/i915/display/intel_display_core.h | 4 ++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index ef86fac8e664..59df01b8ad7c 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -666,15 +666,21 @@ static int tgl_get_bw_info(struct intel_display *display, bi->deratedbw[j] = min(maxdebw, bw * (100 - soc_bw_params->derating) / 100); - bi->peakbw[j] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); drm_dbg_kms(display->drm, - "BW%d / QGV %d: num_planes=%d deratedbw=%u peakbw: %u\n", - i, j, bi->num_planes, bi->deratedbw[j], - bi->peakbw[j]); + "BW%d / QGV %d: num_planes=%d deratedbw=%u\n", + i, j, bi->num_planes, bi->deratedbw[j]); } } + for (i = 0; i < qi.num_qgv_points; i++) { + const struct intel_qgv_point *sp = &qi.points[i]; + + display->bw.peakbw[i] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); + + drm_dbg_kms(display->drm, "QGV %d: peakbw=%u\n", i, display->bw.peakbw[i]); + } + for (i = 0; i < qi.num_psf_points; i++) { const struct intel_psf_gv_point *sp = &qi.psf_points[i]; @@ -741,11 +747,11 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, display->bw.max[0].deratedbw[i] = min(maxdebw, (100 - soc_bw_params->derating) * bw / 100); - display->bw.max[0].peakbw[i] = bw; - drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw: %u\n", - i, display->bw.max[0].deratedbw[i], - display->bw.max[0].peakbw[i]); + display->bw.peakbw[i] = bw; + + drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw=%u\n", + i, display->bw.max[0].deratedbw[i], display->bw.peakbw[i]); } /* Bandwidth does not depend on # of planes; set all groups the same */ @@ -1110,7 +1116,7 @@ static int mtl_find_qgv_points(struct intel_display *display, if (max_data_rate - data_rate < best_rate) { best_rate = max_data_rate - data_rate; - qgv_peak_bw = display->bw.max[bw_index].peakbw[i]; + qgv_peak_bw = display->bw.peakbw[i]; } drm_dbg_kms(display->drm, "QGV point %d: max bw %d required %d qgv_peak_bw: %d\n", diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 58cd0961031b..3c17cac1eb97 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -325,12 +325,12 @@ struct intel_display { struct intel_bw_info { /* for each QGV point */ unsigned int deratedbw[I915_NUM_QGV_POINTS]; - /* Peak BW for each QGV point */ - unsigned int peakbw[I915_NUM_QGV_POINTS]; u8 num_planes; } max[6]; /* for each PSF GV point */ unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; + /* Peak BW for each QGV point */ + unsigned int peakbw[I915_NUM_QGV_POINTS]; u8 num_qgv_points; u8 num_psf_gv_points; } bw; From 10738a07c07b290ea8bb9a2fc16972251f4d7afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:56 +0300 Subject: [PATCH 056/186] drm/i915/bw: Print derated bandwidth numbers for DG2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While DG2 is using hardcoded numbers for the memory bandwidth stuff, let's still print them to aid in debugging as there are two different SKUs to consider with different bandwidth numbers. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-10-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 59df01b8ad7c..f4121223a58b 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -711,6 +711,10 @@ static void dg2_get_bw_info(struct intel_display *display) display->bw.max[0].num_planes = U8_MAX; display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000; + drm_dbg_kms(display->drm, + "QGV 0: deratedbw=%u\n", + display->bw.max[0].deratedbw[0]); + /* Bandwidth does not depend on # of planes; set all groups the same */ for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) display->bw.max[i] = display->bw.max[0]; From 7c04faaee60b774cd73172e6ae9bb52ff5e73bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:57 +0300 Subject: [PATCH 057/186] drm/i915/bw: Use icl_qgv_bw() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace a hand rolled copy of icl_qgv_bw() with the real thing. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-11-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index f4121223a58b..423cae2ff208 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -1106,14 +1106,8 @@ static int mtl_find_qgv_points(struct intel_display *display, * offered per plane group */ for (i = 0; i < num_qgv_points; i++) { - unsigned int bw_index = - tgl_max_bw_index(display, num_active_planes, i); - unsigned int max_data_rate; - - if (bw_index >= ARRAY_SIZE(display->bw.max)) - continue; - - max_data_rate = display->bw.max[bw_index].deratedbw[i]; + unsigned int max_data_rate = + icl_qgv_bw(display, num_active_planes, i); if (max_data_rate < data_rate) continue; From 88b30ff4bc52f959e46599b14ab16674e8ebfde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 28 May 2026 13:34:58 +0300 Subject: [PATCH 058/186] drm/i915/bw: Simplify the best max_data_rate search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason we're tracking the best max_data_rate as the difference between the required data_rate and max_data_rate. That's pointlessly complicated as we're just looking for the minimum max_data_rate that is greater or equal to data_rate. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260528103458.18069-12-ville.syrjala@linux.intel.com Reviewed-by: Vinod Govindapillai --- drivers/gpu/drm/i915/display/intel_bw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 423cae2ff208..41539fdfeac5 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -1112,8 +1112,8 @@ static int mtl_find_qgv_points(struct intel_display *display, if (max_data_rate < data_rate) continue; - if (max_data_rate - data_rate < best_rate) { - best_rate = max_data_rate - data_rate; + if (max_data_rate < best_rate) { + best_rate = max_data_rate; qgv_peak_bw = display->bw.peakbw[i]; } From e9e982ff338ebf30f7461b9db9a987e56bc22d3e Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:28 +0300 Subject: [PATCH 059/186] drm/i915/color: clean up variables in xelpd_program_plane_pre_csc_lut() Use plain int for counting. Initialize lut_size at declaration. Remove unnecessary lut_val initialization. Remove extra u32 v and just use lut_val. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/92d8a508ab18dfd33b6e5573e6edf433f1bbd321.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 458508bcf1f4..73ab879915c3 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3967,12 +3967,10 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, enum pipe pipe = to_intel_plane(state->plane)->pipe; enum plane_id plane = to_intel_plane(state->plane)->id; const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data; - u32 i, lut_size; - u32 lut_val = 1 << 24; + int i, lut_size = 128; + u32 lut_val; if (icl_is_hdr_plane(display, plane)) { - lut_size = 128; - intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), PLANE_PAL_PREC_AUTO_INCREMENT); @@ -3995,10 +3993,11 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, } while (i++ < 130); } else { for (i = 0; i < lut_size; i++) { - u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), v); + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } do { From d7f2ade828af1d02eb8bd48de8e693c2423577c7 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:29 +0300 Subject: [PATCH 060/186] drm/i915/color: clean up variables in xelpd_program_plane_post_csc_lut() Use plain int for counting. Initialize lut_size at declaration. Remove extra u32 v and just use lut_val. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/5b4ee3400084d8ccec77b81c8cbbd294394fcbc9.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 73ab879915c3..cf910baa69f3 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -4020,7 +4020,8 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, enum pipe pipe = to_intel_plane(state->plane)->pipe; enum plane_id plane = to_intel_plane(state->plane)->id; const struct drm_color_lut32 *post_csc_lut = plane_state->hw.gamma_lut->data; - u32 i, lut_size, lut_val; + int i, lut_size = 32; + u32 lut_val; if (icl_is_hdr_plane(display, plane)) { intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), @@ -4029,7 +4030,6 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), PLANE_PAL_PREC_AUTO_INCREMENT); if (post_csc_lut) { - lut_size = 32; for (i = 0; i < lut_size; i++) { lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); @@ -4046,12 +4046,12 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, } while (i++ < 34); } else { /*TODO: Add for segment 0 */ - lut_size = 32; for (i = 0; i < lut_size; i++) { - u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), v); + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } do { From bb2ab17e45d866fa8f13762908fd27905ebf7c0f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:30 +0300 Subject: [PATCH 061/186] drm/i915/color: reduce indent in xelpd_program_plane_pre_csc_lut() Return early for !icl_is_hdr_plane() to reduce indent. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/ba09d28e1dfb74ecd7c06c3554df485dbcca9a8a.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 67 +++++++++++----------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index cf910baa69f3..429eb6daa7d2 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3970,45 +3970,46 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, int i, lut_size = 128; u32 lut_val; - if (icl_is_hdr_plane(display, plane)) { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); + if (!icl_is_hdr_plane(display, plane)) + return; - if (pre_csc_lut) { - for (i = 0; i < lut_size; i++) { - lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } + if (pre_csc_lut) { + for (i = 0; i < lut_size; i++) { + lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); - /* Program the max register to clamp values > 1.0. */ - /* TODO: Restrict to 0x7ffffff */ - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } while (i++ < 130); - } else { - for (i = 0; i < lut_size; i++) { - lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); - - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 130); + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } - intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); + /* Program the max register to clamp values > 1.0. */ + /* TODO: Restrict to 0x7ffffff */ + do { + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); + } while (i++ < 130); + } else { + for (i = 0; i < lut_size; i++) { + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); + } + + do { + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + 1 << 24); + } while (i++ < 130); } + + intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); } static void From 242c6a5c9b1be10f511a86c40971b0795375b1e7 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:31 +0300 Subject: [PATCH 062/186] drm/i915/color: reduce indent in xelpd_program_plane_post_csc_lut() Return early for !icl_is_hdr_plane() to reduce indent. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/cb60ebb0c5fe67331425ba51280cea4ba111c577.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 75 +++++++++++----------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 429eb6daa7d2..5209a4cdd14d 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -4024,48 +4024,49 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, int i, lut_size = 32; u32 lut_val; - if (icl_is_hdr_plane(display, plane)) { - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); - /* TODO: Add macro */ - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); - if (post_csc_lut) { - for (i = 0; i < lut_size; i++) { - lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); + if (!icl_is_hdr_plane(display, plane)) + return; - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + /* TODO: Add macro */ + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + if (post_csc_lut) { + for (i = 0; i < lut_size; i++) { + lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); - /* Segment 2 - clamp to the last LUT value to prevent step discontinuity */ - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } while (i++ < 34); - } else { - /*TODO: Add for segment 0 */ - for (i = 0; i < lut_size; i++) { - lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); - - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 34); + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0); + /* Segment 2 - clamp to the last LUT value to prevent step discontinuity */ + do { + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); + } while (i++ < 34); + } else { + /*TODO: Add for segment 0 */ + for (i = 0; i < lut_size; i++) { + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); + } + + do { + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + 1 << 24); + } while (i++ < 34); } + + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0); } static void From 3850e082db0fbd60962d56e56043ee955c122871 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:32 +0300 Subject: [PATCH 063/186] drm/i915/color: join loops in xelpd_program_plane_pre_csc_lut() Use single for loops instead of two. Especially switching from a for-loop to a do-while-loop with the same loop index is confusing, and it's hard to figure out the end index. Define the end in terms of lut_size; there's three more entries after the first 128. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/6d4f8bb713a998c199606c079bed924458f04f54.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 27 ++++++++-------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 5209a4cdd14d..48f09c73e513 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3978,35 +3978,26 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, PLANE_PAL_PREC_AUTO_INCREMENT); if (pre_csc_lut) { - for (i = 0; i < lut_size; i++) { - lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); + for (i = 0; i < lut_size + 3; i++) { + if (i < lut_size) + lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); + /* else duplicate last lut_val */ intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), lut_val); } - - /* Program the max register to clamp values > 1.0. */ - /* TODO: Restrict to 0x7ffffff */ - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } while (i++ < 130); } else { - for (i = 0; i < lut_size; i++) { - lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + for (i = 0; i < lut_size + 3; i++) { + if (i < lut_size) + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + else + lut_val = 1 << 24; intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), lut_val); } - - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 130); } intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); From acfef63368a2c3aba05777045a6eb36ece93fcbe Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:33 +0300 Subject: [PATCH 064/186] drm/i915/color: join loops in xelpd_program_plane_post_csc_lut() Use single for loops instead of two. Especially switching from a for-loop to a do-while-loop with the same loop index is confusing, and it's hard to figure out the end index. Define the end in terms of lut_size; there's three more entries after the first 32. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/011336e9d57bba57e15d1aa64ae53a20c461ed62.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 26 ++++++++-------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 48f09c73e513..5c09c5dd361e 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -4024,35 +4024,27 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), PLANE_PAL_PREC_AUTO_INCREMENT); if (post_csc_lut) { - for (i = 0; i < lut_size; i++) { - lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); + for (i = 0; i < lut_size + 3; i++) { + if (i < lut_size) + lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); + /* else clamp to the last LUT value to prevent step discontinuity */ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), lut_val); } - - /* Segment 2 - clamp to the last LUT value to prevent step discontinuity */ - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } while (i++ < 34); } else { /*TODO: Add for segment 0 */ - for (i = 0; i < lut_size; i++) { - lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + for (i = 0; i < lut_size + 3; i++) { + if (i < lut_size) + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + else + lut_val = 1 << 24; intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), lut_val); } - - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 34); } intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); From bbc23003e00517144ce65901e5f74ea2c0ceb5d3 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:34 +0300 Subject: [PATCH 065/186] drm/i915/color: deduplicate loops in xelpd_program_plane_pre_csc_lut() Now that the pre_csc_lut and non-pre_csc_lut paths look similar, deduplicate the loops and just determine the value based on pre_csc_lut vs. not. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/b943121a358dab0b04c9766baba8295f12ae53fc.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 5c09c5dd361e..d705f745bd7d 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3977,27 +3977,21 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), PLANE_PAL_PREC_AUTO_INCREMENT); - if (pre_csc_lut) { - for (i = 0; i < lut_size + 3; i++) { + for (i = 0; i < lut_size + 3; i++) { + if (pre_csc_lut) { if (i < lut_size) lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); /* else duplicate last lut_val */ - - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - } else { - for (i = 0; i < lut_size + 3; i++) { + } else { if (i < lut_size) lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); else lut_val = 1 << 24; - - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); } + + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); From 5de2ad00a044d3fe85b46eb33a1d911d6eb88567 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jun 2026 18:14:35 +0300 Subject: [PATCH 066/186] drm/i915/color: deduplicate loops in xelpd_program_plane_post_csc_lut() Now that the pre_csc_lut and non-pre_csc_lut paths look similar, deduplicate the loops and just determine the value based on pre_csc_lut vs. not. Reviewed-by: Chaitanya Kumar Borah Link: https://patch.msgid.link/4b3f55002a1c5b7067ccd6688d802fc284410e03.1780499355.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index d705f745bd7d..87ced9f6ff40 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -4017,28 +4017,22 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, /* TODO: Add macro */ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), PLANE_PAL_PREC_AUTO_INCREMENT); - if (post_csc_lut) { - for (i = 0; i < lut_size + 3; i++) { + + for (i = 0; i < lut_size + 3; i++) { + if (post_csc_lut) { if (i < lut_size) lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); /* else clamp to the last LUT value to prevent step discontinuity */ - - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - } else { - /*TODO: Add for segment 0 */ - for (i = 0; i < lut_size + 3; i++) { + } else { if (i < lut_size) lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); else lut_val = 1 << 24; - - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); } + + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); From b7d51d65e4f12a48392d260613108ec262bc7774 Mon Sep 17 00:00:00 2001 From: Jerome Tollet Date: Wed, 20 May 2026 07:55:44 +0530 Subject: [PATCH 067/186] drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status HDMI 2.0 section 6.1.3.1 specifies that after enabling Scrambling_Enable and starting scrambled video transmission, the source should poll Scrambling_Status until it reads 1 or until a timeout of 200 ms expires. Add a polling step after enabling the HDMI port to check the scrambling status when HDMI scrambling is enabled. On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz (594 MHz) failing to come up correctly because the sink has not yet finished its scrambling setup. In practice, waiting for the scrambling status here fixes such sinks. While this synchronous polling is not itself explicitly required for correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it as the way for the source to verify that the TMDS link is functioning correctly with scrambling enabled. v3: - Add explicit HDMI 2.0 section reference in code comment - Clarify commit message around the observed sink fix v2: - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed delay Reported-by: Jerome Tollet Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868 Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/ Signed-off-by: Jerome Tollet Signed-off-by: Ankit Nautiyal Reviewed-by: Arun R Murthy Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 ++ drivers/gpu/drm/i915/display/intel_hdmi.c | 26 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_hdmi.h | 2 ++ 3 files changed, 30 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 6399b16405c8..2684e33b602d 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3504,6 +3504,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, } intel_ddi_buf_enable(encoder, buf_ctl); + + intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); } static void intel_ddi_enable(struct intel_atomic_state *state, diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 9076c2b176ec..b9d11fb8559d 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2728,6 +2728,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ drm_connector_attach_max_bpc_property(&connector->base, 8, 12); } +/* + * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after + * enabling Scrambling_Enable and starting scrambled video + * transmission, poll Scrambling_Status for up to 200 ms. + */ +void +intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector) +{ + struct intel_connector *connector = to_intel_connector(_connector); + struct intel_display *display = to_intel_display(crtc_state); + bool scrambling_enabled = false; + int ret; + + if (!crtc_state->hdmi_scrambling) + return; + + /* Poll for a max of 200 msec as per HDMI spec */ + ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), + scrambling_enabled, 1000, 200 * 1000, false); + if (ret) + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", + connector->base.base.id, connector->base.name); +} + /* * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup * @encoder: intel_encoder diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index be2fad57e4ad..0fa3661568e8 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, unsigned int type, void *frame, ssize_t len); +void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector); #endif /* __INTEL_HDMI_H__ */ From 485356a428b09d4ca864abfce4d3050eee8e16c7 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Tue, 9 Jun 2026 08:23:24 +0530 Subject: [PATCH 068/186] drm/i915/display: Handle VSYNC timing in LRR path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LRR already updates crtc_vtotal/crtc_vblank_end seamlessly. Extend the same handling to crtc_vsync_start/crtc_vsync_end so VSYNC timing changes are programmed and accepted via the LRR path instead of forcing a full modeset. v2: Add comment explaining why TRANS_VSYNC update is safe for DP LRR. (Ville) Signed-off-by: Ankit Nautiyal Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260609025325.1128543-2-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index bdf02b67c1d8..e2e4b00a8fa9 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -2816,6 +2816,16 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder), VBLANK_START(crtc_vblank_start - 1) | VBLANK_END(crtc_vblank_end - 1)); + + /* + * DP doesn't have vertical sync, so TRANS_VSYNC only affects + * the position of the vsync interrupt (and does so even when + * using the VRR timing generator!). Thus updating TRANS_VSYNC + * here seems fine even if it isn't double buffered. + */ + intel_de_write(display, TRANS_VSYNC(display, cpu_transcoder), + VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | + VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); /* * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal * bits are not required. Since the support for these bits is going to @@ -5189,9 +5199,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(name.crtc_vdisplay); \ if (!fastset || !allow_vblank_delay_fastset(current_config)) \ PIPE_CONF_CHECK_I(name.crtc_vblank_start); \ - PIPE_CONF_CHECK_I(name.crtc_vsync_start); \ - PIPE_CONF_CHECK_I(name.crtc_vsync_end); \ if (!fastset || !pipe_config->update_lrr) { \ + PIPE_CONF_CHECK_I(name.crtc_vsync_start); \ + PIPE_CONF_CHECK_I(name.crtc_vsync_end); \ PIPE_CONF_CHECK_I(name.crtc_vtotal); \ PIPE_CONF_CHECK_I(name.crtc_vblank_end); \ } \ @@ -5813,6 +5823,8 @@ static bool lrr_params_changed(const struct intel_crtc_state *old_crtc_state, return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start || old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end || + old_adjusted_mode->crtc_vsync_start != new_adjusted_mode->crtc_vsync_start || + old_adjusted_mode->crtc_vsync_end != new_adjusted_mode->crtc_vsync_end || old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal || old_crtc_state->set_context_latency != new_crtc_state->set_context_latency; } From b28c1929d75821b29c49c284ec3dc4d0e23c193f Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Tue, 9 Jun 2026 08:23:25 +0530 Subject: [PATCH 069/186] drm/i915/panel: Preserve Vtotal-Vsync distance while adjusting vtotal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we increase the vtotal to accommodate lower resfresh rate for fixed modes, adjust the vtotal-vsync distance also. v2: Rename vsync_*_diff to vsync_*_offset for clarity. (Ville) Signed-off-by: Ankit Nautiyal Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260609025325.1128543-3-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_panel.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 20c548eea6da..81fb349ece5f 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -237,11 +237,18 @@ int intel_panel_compute_config(struct intel_connector *connector, drm_mode_copy(adjusted_mode, fixed_mode); - if (is_vrr && fixed_mode_vrefresh != vrefresh) + if (is_vrr && fixed_mode_vrefresh != vrefresh) { + int vsync_start_offset = adjusted_mode->vtotal - adjusted_mode->vsync_start; + int vsync_end_offset = adjusted_mode->vtotal - adjusted_mode->vsync_end; + adjusted_mode->vtotal = DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000, adjusted_mode->htotal * vrefresh); + adjusted_mode->vsync_start = adjusted_mode->vtotal - vsync_start_offset; + adjusted_mode->vsync_end = adjusted_mode->vtotal - vsync_end_offset; + } + drm_mode_set_crtcinfo(adjusted_mode, 0); return 0; From d5005addb5f68e8a0edce249506757bdc9e3d8c8 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:53:10 +0800 Subject: [PATCH 070/186] drm/i915: clear CRTC color blob pointers after dropping refs intel_crtc_put_color_blobs() drops the CRTC color blob references, but leaves the corresponding pointers unchanged. This can matter in intel_crtc_prepare_cleared_state(), which frees the old CRTC hw state before calling intel_dp_tunnel_atomic_clear_stream_bw(). The latter can fail while looking up the DP tunnel group state, for example with -EDEADLK. If that happens, the function returns without completing the cleared state preparation. The failed atomic state will then be cleared by the atomic core and intel_crtc_free_hw_state() can be called again for the same state, dropping the same blob references again. Clear the blob pointers after dropping the references so repeated cleanup of the same CRTC hw state is safe. Fixes: fb69d0076e68 ("drm/i915/dp_tunnel: Fix error handling when clearing stream BW in atomic state") Suggested-by: Imre Deak Signed-off-by: Guangshuo Li Reviewed-by: Imre Deak Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260612035310.3013066-1-lgs201920130244@gmail.com --- drivers/gpu/drm/i915/display/intel_atomic.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 0e4f0678c53c..9d0d47c79dd1 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -288,6 +288,12 @@ static void intel_crtc_put_color_blobs(struct intel_crtc_state *crtc_state) drm_property_blob_put(crtc_state->pre_csc_lut); drm_property_blob_put(crtc_state->post_csc_lut); + + crtc_state->hw.degamma_lut = NULL; + crtc_state->hw.gamma_lut = NULL; + crtc_state->hw.ctm = NULL; + crtc_state->pre_csc_lut = NULL; + crtc_state->post_csc_lut = NULL; } void intel_crtc_free_hw_state(struct intel_crtc_state *crtc_state) From 28783a274e886dd6da61419be6020bd9d0384e9f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 12 Jun 2026 20:26:17 +0300 Subject: [PATCH 071/186] drm/i915/mtl+: Enable PPS before PLL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling PPS after a display port's PLL is enabled leads to PLL / DDI BUF timeouts during system resuming after a long (> 45 mins) suspended state, at least on some ARL and MTL laptops, either all or some of them also containing an Nvidia GPU. Enabling PPS first and then the PLL fixes the problem for all the reporters. A similar issue is seen when enabling an external DP output on PHY B (vs. PHY A in the above eDP cases), where this change will not have any effect (since no PPS is used in that case). There isn't any direct connection between PPS and PLL, so the fix for eDP works by some side-effect only. However Bspec does seem to require enabling PPS first, so let's do that. Further investigation continues on the actual root cause and a cure for external panels. Fixes: 1a7fad2aea74 ("drm/i915/cx0: Enable dpll framework for MTL+") Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16098 Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16064 Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16042 Cc: Mika Kahola Cc: stable@vger.kernel.org # v7.0+ Tested-by: Jouni Högander Tested-by: Marco Nenciarini Reviewed-by: Suraj Kandpal Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260612172617.3427027-1-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 2684e33b602d..25314ec65ae7 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2652,9 +2652,6 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, /* 3. Select Thunderbolt */ mtl_port_buf_ctl_io_selection(encoder); - /* 4. Enable Panel Power if PPS is required */ - intel_pps_on(intel_dp); - /* 5. Enable the port PLL */ intel_ddi_enable_clock(encoder, crtc_state); @@ -3710,6 +3707,14 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state, else if (display->platform.geminilake || display->platform.broxton) bxt_dpio_phy_set_lane_optim_mask(encoder, crtc_state->lane_lat_optim_mask); + + /* + * There is no direct connection between the PLL and PPS, however + * enabling PPS before PLL is required to avoid PLL/DDI BUF timeouts + * during system resume. Do that matching the Bspec order as well. + */ + if (DISPLAY_VER(display) >= 14) + intel_pps_on(&dig_port->dp); } static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder) From 5c1e93131268353ba02c41518386f942aee5e6f9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 18:28:37 +0300 Subject: [PATCH 072/186] drm/intel: drop driver include from mchbar_regs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headers under include/ aren't supposed to try to include headers from driver directories, such as i915_reg_defs.h. Remove it. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260615152837.1898991-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- include/drm/intel/mchbar_regs.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/drm/intel/mchbar_regs.h b/include/drm/intel/mchbar_regs.h index ca0d421be16c..66498ca5e40b 100644 --- a/include/drm/intel/mchbar_regs.h +++ b/include/drm/intel/mchbar_regs.h @@ -6,8 +6,6 @@ #ifndef __INTEL_MCHBAR_REGS__ #define __INTEL_MCHBAR_REGS__ -#include "i915_reg_defs.h" - /* * MCHBAR mirror. * From 21ebf55d694f4d521849d936e3bd8b6e599a85d1 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:18 +0530 Subject: [PATCH 073/186] drm/i915/cmtg: Add intel_cmtg_is_allowed() for CMTG CMTG is supported on transcoder A and transcoder B with EDP, so add a separate helper intel_cmtg_is_allowed() to check the prerequisites for enabling CMTG. CMTG will be enabled only in specific use cases such as PSR2, PR-ALPM, and LOBF, and will be used in conjunction with the DC3CO feature. DC3co will be enabled in a separate patch. Note: Use-case-specific checks and transcoder-port compatibility validation will be handled part of DC3co feature implementation. v2: - Remove separate flag for DC3co from crtc_state. [Uma, Dibin] v3: - Do not access power domain members directly. [Jani] v4: - Remove check for DC3co state now. if needed add Dc3co allow check later once Dc3co patches are merged. [Uma] Bspec: 68915 Reviewed-by: Uma Shankar Reviewed-by: Dibin Moolakadan Subrahmanian Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-2-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 14 +++++++++++++- drivers/gpu/drm/i915/display/intel_cmtg.h | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index e1fdc6fe9762..a279f3dcd1ec 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -4,7 +4,6 @@ */ #include -#include #include #include @@ -16,6 +15,7 @@ #include "intel_display_device.h" #include "intel_display_power.h" #include "intel_display_regs.h" +#include "intel_display_types.h" /** * DOC: Common Primary Timing Generator (CMTG) @@ -185,3 +185,15 @@ void intel_cmtg_sanitize(struct intel_display *display) intel_cmtg_disable(display, &cmtg_config); } + +bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + + if ((cpu_transcoder == TRANSCODER_A || cpu_transcoder == TRANSCODER_B) && + DISPLAY_VER(display) == 35 && intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) + return true; + + return false; +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index ba62199adaa2..ed540581738f 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -6,8 +6,12 @@ #ifndef __INTEL_CMTG_H__ #define __INTEL_CMTG_H__ +#include + struct intel_display; +struct intel_crtc_state; void intel_cmtg_sanitize(struct intel_display *display); +bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state); #endif /* __INTEL_CMTG_H__ */ From b5cbe1aefe9c77f57ede7361dad76854b3118966 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:19 +0530 Subject: [PATCH 074/186] drm/i915/cmtg: Set CMTG clock select Program the CMTG Clock Select register based on the transcoder used. v2: - Correct mask for PHY B. [Jani] - Use REG_FIELD_PREP() for enable value. [Dibin] - Extend cmtg clock select for xe3plpd. [Dibin] v3: - CMTG support removed for old platform. v4: - Optimize further with else-if. [Uma] - Correct CMTG_CLK_SEL_B_MASK. [Uma] v5: - Add transcoder-port compatibility check. [Dibin] Bspec: 69103 Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Signed-off-by: Dibin Moolakadan Subrahmanian Link: https://patch.msgid.link/20260615200339.885190-3-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 22 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + .../gpu/drm/i915/display/intel_cmtg_regs.h | 2 ++ drivers/gpu/drm/i915/display/intel_cx0_phy.c | 11 ++++++++++ 4 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index a279f3dcd1ec..fbc8a4f2b9cb 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -197,3 +197,25 @@ bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state) return false; } + +void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 clk_sel_clr = 0; + u32 clk_sel_set = 0; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + if (cpu_transcoder == TRANSCODER_A) { + clk_sel_clr = CMTG_CLK_SEL_A_MASK; + clk_sel_set = CMTG_CLK_SELECT_PHYA_ENABLE; + } else if (cpu_transcoder == TRANSCODER_B) { + clk_sel_clr = CMTG_CLK_SEL_B_MASK; + clk_sel_set = CMTG_CLK_SELECT_PHYB_ENABLE; + } + + if (clk_sel_set) + intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index ed540581738f..87092ce6d67b 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -11,6 +11,7 @@ struct intel_display; struct intel_crtc_state; +void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h index 945a35578284..4a80b88d88fd 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h @@ -10,8 +10,10 @@ #define CMTG_CLK_SEL _MMIO(0x46160) #define CMTG_CLK_SEL_A_MASK REG_GENMASK(31, 29) +#define CMTG_CLK_SELECT_PHYA_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0x4) #define CMTG_CLK_SEL_A_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0) #define CMTG_CLK_SEL_B_MASK REG_GENMASK(15, 13) +#define CMTG_CLK_SELECT_PHYB_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0x6) #define CMTG_CLK_SEL_B_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0) #define TRANS_CMTG_CTL_A _MMIO(0x6fa88) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 24a51ab21b55..452062417ce9 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -9,6 +9,7 @@ #include #include "intel_alpm.h" +#include "intel_cmtg.h" #include "intel_cx0_phy.h" #include "intel_cx0_phy_regs.h" #include "intel_display_regs.h" @@ -3418,10 +3419,20 @@ void intel_mtl_pll_enable(struct intel_encoder *encoder, void intel_mtl_pll_enable_clock(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { + struct intel_display *display = to_intel_display(encoder); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); if (intel_tc_port_in_tbt_alt_mode(dig_port)) intel_mtl_tbt_pll_enable_clock(encoder, crtc_state->port_clock); + + /* + * CMTG can be enabled only when the transcoder and port are compatible + * (transcoder A with port A, transcoder B with port B). + */ + if (HAS_LT_PHY(display) && + ((crtc_state->cpu_transcoder == TRANSCODER_A && encoder->port == PORT_A) || + (crtc_state->cpu_transcoder == TRANSCODER_B && encoder->port == PORT_B))) + intel_cmtg_set_clk_select(crtc_state); } /* From 789dda6429e0d49f4b6a614ab6617cfc4e63df9f Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:20 +0530 Subject: [PATCH 075/186] drm/i915/cmtg: Add CMTG transcoder offset in struct _device_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As all cmtg registers offset from base cmtg register is similar to normal transcoder register, so follow existing way of defining transcoder register for cmtg as well. Add base CMTG offset in struct _display_device_info which will be used to derive the actual register address for platform supporting CMTG. Bspec: 68989 Reviewed-by: Uma Shankar Suggested-by: Ville Syrjälä Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-4-animesh.manna@intel.com --- .../gpu/drm/i915/display/intel_display_device.c | 14 ++++++++++++++ .../gpu/drm/i915/display/intel_display_device.h | 2 +- .../gpu/drm/i915/display/intel_display_limits.h | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c index 69a9f782935c..f17fc2c68472 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.c +++ b/drivers/gpu/drm/i915/display/intel_display_device.c @@ -101,6 +101,8 @@ static const struct intel_display_device_info no_display = {}; #define TRANSCODER_EDP_OFFSET 0x6f000 #define TRANSCODER_DSI0_OFFSET 0x6b000 #define TRANSCODER_DSI1_OFFSET 0x6b800 +#define TRANSCODER_CMTG0_OFFSET 0x6F000 +#define TRANSCODER_CMTG1_OFFSET 0x6F100 #define CURSOR_A_OFFSET 0x70080 #define CURSOR_B_OFFSET 0x700c0 @@ -1352,6 +1354,18 @@ static const struct intel_display_device_info xe2_lpd_display = { BIT(INTEL_FBC_A) | BIT(INTEL_FBC_B) | BIT(INTEL_FBC_C) | BIT(INTEL_FBC_D), .__runtime_defaults.has_dbuf_overlap_detection = true, + .trans_offsets = { + [TRANSCODER_A] = TRANSCODER_A_OFFSET, + [TRANSCODER_B] = TRANSCODER_B_OFFSET, + [TRANSCODER_C] = TRANSCODER_C_OFFSET, + [TRANSCODER_D] = TRANSCODER_D_OFFSET, + [TRANSCODER_CMTG0] = TRANSCODER_CMTG0_OFFSET, + [TRANSCODER_CMTG1] = TRANSCODER_CMTG1_OFFSET, + }, + .__runtime_defaults.cpu_transcoder_mask = + BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | + BIT(TRANSCODER_C) | BIT(TRANSCODER_D) | + BIT(TRANSCODER_CMTG0) | BIT(TRANSCODER_CMTG1), }; static const struct intel_display_device_info wcl_display = { diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index 12e5a522a299..acb9ca87dda7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -292,7 +292,7 @@ struct intel_display_runtime_info { u32 rawclk_freq; u8 pipe_mask; - u8 cpu_transcoder_mask; + u16 cpu_transcoder_mask; u16 port_mask; u8 num_sprites[I915_MAX_PIPES]; diff --git a/drivers/gpu/drm/i915/display/intel_display_limits.h b/drivers/gpu/drm/i915/display/intel_display_limits.h index 453f7b720815..ea89473c177f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_limits.h +++ b/drivers/gpu/drm/i915/display/intel_display_limits.h @@ -45,6 +45,8 @@ enum transcoder { TRANSCODER_DSI_1, TRANSCODER_DSI_A = TRANSCODER_DSI_0, /* legacy DSI */ TRANSCODER_DSI_C = TRANSCODER_DSI_1, /* legacy DSI */ + TRANSCODER_CMTG0, + TRANSCODER_CMTG1, I915_MAX_TRANSCODERS }; From d671b9328d1f87390d43205772ab555b05ef41b2 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:21 +0530 Subject: [PATCH 076/186] drm/i915/display: Pass target transcoder to intel_set_transcoder_timings() Let intel_set_transcoder_timings() take the target transcoder as an explicit argument instead of always using crtc_state->cpu_transcoder. This makes the helper reusable for callers that need to program timings for a transcoder other than the CRTC's CPU transcoder. Update all existing callers to pass crtc_state->cpu_transcoder so there is no functional change. Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-5-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index e2e4b00a8fa9..dceb70cf5397 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -132,7 +132,8 @@ #include "vlv_dsi_pll.h" #include "vlv_dsi_regs.h" -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state); +static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder cpu_transcoder); static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state); static void hsw_set_transconf(const struct intel_crtc_state *crtc_state); static void bdw_set_pipe_misc(struct intel_dsb *dsb, @@ -1504,7 +1505,7 @@ static void ilk_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); ilk_set_pipeconf(crtc_state); } @@ -1635,7 +1636,7 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); if (cpu_transcoder != TRANSCODER_EDP) intel_de_write(display, TRANS_MULT(display, cpu_transcoder), @@ -2048,7 +2049,7 @@ static void i9xx_configure_cpu_transcoder(const struct intel_crtc_state *crtc_st &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); i9xx_set_pipeconf(crtc_state); } @@ -2664,12 +2665,12 @@ transcoder_has_vrr(const struct intel_crtc_state *crtc_state) return HAS_VRR(display) && !transcoder_is_dsi(cpu_transcoder); } -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state) +static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder cpu_transcoder) { struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum pipe pipe = crtc->pipe; - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; int vsyncshift = 0; From 0423aeb70d0a84867de8878735746ad60b62a083 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:22 +0530 Subject: [PATCH 077/186] drm/i915/display: Rename cpu_transcoder parameter to transcoder intel_set_transcoder_timings() now takes the target transcoder as an explicit argument rather than implicitly using crtc_state->cpu_transcoder, so the parameter name 'cpu_transcoder' is misleading. Rename it to plain 'transcoder' to reflect that any transcoder may be programmed. No functional change. Signed-off-by: Animesh Manna Reviewed-by: Uma Shankar Link: https://patch.msgid.link/20260615200339.885190-6-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index dceb70cf5397..c11def137711 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -133,7 +133,7 @@ #include "vlv_dsi_regs.h" static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, - enum transcoder cpu_transcoder); + enum transcoder transcoder); static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state); static void hsw_set_transconf(const struct intel_crtc_state *crtc_state); static void bdw_set_pipe_misc(struct intel_dsb *dsb, @@ -2666,7 +2666,7 @@ transcoder_has_vrr(const struct intel_crtc_state *crtc_state) } static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, - enum transcoder cpu_transcoder) + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); @@ -2675,7 +2675,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; int vsyncshift = 0; - drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder)); + drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder)); /* We need to be careful not to changed the adjusted mode, for otherwise * the hw state checker will get angry at the mismatch. */ @@ -2704,7 +2704,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta */ if (DISPLAY_VER(display) >= 13) { intel_de_write(display, - TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), + TRANS_SET_CONTEXT_LATENCY(display, transcoder), crtc_state->set_context_latency); /* @@ -2719,16 +2719,16 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta if (DISPLAY_VER(display) >= 4 && DISPLAY_VER(display) < 35) intel_de_write(display, - TRANS_VSYNCSHIFT(display, cpu_transcoder), + TRANS_VSYNCSHIFT(display, transcoder), vsyncshift); - intel_de_write(display, TRANS_HTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_HTOTAL(display, transcoder), HACTIVE(adjusted_mode->crtc_hdisplay - 1) | HTOTAL(adjusted_mode->crtc_htotal - 1)); - intel_de_write(display, TRANS_HBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_HBLANK(display, transcoder), HBLANK_START(adjusted_mode->crtc_hblank_start - 1) | HBLANK_END(adjusted_mode->crtc_hblank_end - 1)); - intel_de_write(display, TRANS_HSYNC(display, cpu_transcoder), + intel_de_write(display, TRANS_HSYNC(display, transcoder), HSYNC_START(adjusted_mode->crtc_hsync_start - 1) | HSYNC_END(adjusted_mode->crtc_hsync_end - 1)); @@ -2741,13 +2741,13 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta if (intel_vrr_always_use_vrr_tg(display)) crtc_vtotal = 1; - intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_VBLANK(display, transcoder), VBLANK_START(crtc_vblank_start - 1) | VBLANK_END(crtc_vblank_end - 1)); - intel_de_write(display, TRANS_VSYNC(display, cpu_transcoder), + intel_de_write(display, TRANS_VSYNC(display, transcoder), VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); @@ -2755,7 +2755,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta * programmed with the VTOTAL_EDP value. Same for VTOTAL_C. This is * documented on the DDI_FUNC_CTL register description, EDP Input Select * bits. */ - if (display->platform.haswell && cpu_transcoder == TRANSCODER_EDP && + if (display->platform.haswell && transcoder == TRANSCODER_EDP && (pipe == PIPE_B || pipe == PIPE_C)) intel_de_write(display, TRANS_VTOTAL(display, pipe), VACTIVE(crtc_vdisplay - 1) | @@ -2770,7 +2770,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta * followed by BE which DPRX devices are unable to handle. * https://groups.vesa.org/wg/DP/document/20494 */ - intel_de_write(display, DP_MIN_HBLANK_CTL(cpu_transcoder), + intel_de_write(display, DP_MIN_HBLANK_CTL(transcoder), crtc_state->min_hblank); } } From ccb4470c7f241f0161aacb26f37b272b659717a8 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:23 +0530 Subject: [PATCH 078/186] drm/i915/display: Skip DP_MIN_HBLANK_CTL programming for CMTG transcoders DP_MIN_HBLANK_CTL is a CPU transcoder register and must not be written for the CMTG transcoders. Skip the programming when the target transcoder is TRANSCODER_CMTG0 or TRANSCODER_CMTG1. Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-7-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index c11def137711..8f8e24565a6d 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -2761,7 +2761,9 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - if (DISPLAY_VER(display) >= 30) { + if (DISPLAY_VER(display) >= 30 && + transcoder != TRANSCODER_CMTG0 && + transcoder != TRANSCODER_CMTG1) { /* * Address issues for resolutions with high refresh rate that * have small Hblank, specifically where Hblank is smaller than From a0c780e6ef0df928d8bfbcfb6d791b3e08cf7c7f Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:24 +0530 Subject: [PATCH 079/186] drm/i915/display: Pass transcoder to intel_set_transcoder_timings_lrr() Make intel_set_transcoder_timings_lrr() take the target transcoder as an explicit parameter instead of always using crtc_state->cpu_transcoder. This allows the LRR timing programming sequence to be reused for other transcoders (e.g. CMTG). Move the intel_vrr_set_fixed_rr_timings() and intel_vrr_transcoder_enable() calls out of intel_set_transcoder_timings_lrr() and into its only caller intel_pipe_fastset(), so the helper now strictly programs the LRR timing registers for the given transcoder. No functional change intended. v2: - Add separate patch for renaming the cpu_transcoder variable. [Ville] Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-8-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 8f8e24565a6d..049038742517 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -2777,10 +2777,10 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta } } -static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state) +static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, + enum transcoder cpu_transcoder) { struct intel_display *display = to_intel_display(crtc_state); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; @@ -2845,9 +2845,6 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - - intel_vrr_set_fixed_rr_timings(crtc_state); - intel_vrr_transcoder_enable(crtc_state); } static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state) @@ -6694,8 +6691,11 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, intel_cpu_transcoder_set_m1_n1(crtc, new_crtc_state->cpu_transcoder, &new_crtc_state->dp_m_n); - if (new_crtc_state->update_lrr) - intel_set_transcoder_timings_lrr(new_crtc_state); + if (new_crtc_state->update_lrr) { + intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder); + intel_vrr_set_fixed_rr_timings(new_crtc_state); + intel_vrr_transcoder_enable(new_crtc_state); + } } static void commit_pipe_pre_planes(struct intel_atomic_state *state, From 61f1ed0d7119eb0702b9c3a49be9b9569feb26ff Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:25 +0530 Subject: [PATCH 080/186] drm/i915/display: Rename cpu_transcoder parameter to transcoder in LRR path intel_set_transcoder_timings_lrr() now takes the target transcoder as an explicit argument rather than implicitly using crtc_state->cpu_transcoder, so the parameter name 'cpu_transcoder' is misleading. Rename it to plain 'transcoder' to reflect that any transcoder may be programmed. No functional change. Signed-off-by: Animesh Manna Reviewed-by: Uma Shankar Link: https://patch.msgid.link/20260615200339.885190-9-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 049038742517..edb9acb1da5c 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -2778,13 +2778,13 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta } static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, - enum transcoder cpu_transcoder) + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; - drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder)); + drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder)); crtc_vdisplay = adjusted_mode->crtc_vdisplay; crtc_vtotal = adjusted_mode->crtc_vtotal; @@ -2799,7 +2799,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc if (DISPLAY_VER(display) >= 13) { intel_de_write(display, - TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), + TRANS_SET_CONTEXT_LATENCY(display, transcoder), crtc_state->set_context_latency); /* @@ -2816,7 +2816,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc * The hardware actually ignores TRANS_VBLANK.VBLANK_END in DP mode. * But let's write it anyway to keep the state checker happy. */ - intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_VBLANK(display, transcoder), VBLANK_START(crtc_vblank_start - 1) | VBLANK_END(crtc_vblank_end - 1)); @@ -2826,7 +2826,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc * using the VRR timing generator!). Thus updating TRANS_VSYNC * here seems fine even if it isn't double buffered. */ - intel_de_write(display, TRANS_VSYNC(display, cpu_transcoder), + intel_de_write(display, TRANS_VSYNC(display, transcoder), VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); /* @@ -2842,7 +2842,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc * The double buffer latch point for TRANS_VTOTAL * is the transcoder's undelayed vblank. */ - intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); } From 3269980141ea77813f17e5b7bd55420588e3a837 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:26 +0530 Subject: [PATCH 081/186] drm/i915/cmtg: Set timings for CMTG by using transcoder timing helpers Expose intel_set_transcoder_timings() & intel_set_transcoder_timings_lrr() so that they can program timings on any transcoder, and use them from a new intel_cmtg_set_timings() helper instead of duplicating the timing register write sequence for CMTG. intel_cmtg_set_timings() maps the CPU transcoder to the corresponding CMTG transcoder (TRANSCODER_A->TRANSCODER_CMTG0, TRANSCODER_B-> TRANSCODER_CMTG1) and calls the shared helper, gated by intel_cmtg_is_allowed(). It is invoked from hsw_configure_cpu_transcoder() for the full modeset path and from intel_pipe_fastset() for the LRR update path. v2: - Use sw state instead of reading directly from hardware. [Jani] - Move set_timing later after encoder enable. [Dibin] v3: - Replace id with trans. [Jani] - Program cmtg set_timing() along with primary transcoder timing. v4: - Use _MMIO_TRANS() for cmtg registers instead of direct multiplication. [Jani] v5: - Modify register definition approach and match existing transcoder definition. [Ville] v6: - Reuse transcoder timing helpers. [Ville] v7: - Introduce enum for set_timing_type. [Uma] - Add check for INVALID_TRANSCODER. [Uma] Bspec: 68989 Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-10-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 28 ++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cmtg.h | 6 +++++ drivers/gpu/drm/i915/display/intel_display.c | 13 ++++----- drivers/gpu/drm/i915/display/intel_display.h | 4 +++ 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index fbc8a4f2b9cb..cb1d69c17830 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -81,6 +81,18 @@ static void intel_cmtg_dump_config(struct intel_display *display, str_yes_no(cmtg_config->trans_b_secondary)); } +static inline enum transcoder to_cmtg_transcoder(enum transcoder cpu_transcoder) +{ + switch (cpu_transcoder) { + case TRANSCODER_A: + return TRANSCODER_CMTG0; + case TRANSCODER_B: + return TRANSCODER_CMTG1; + default: + return INVALID_TRANSCODER; + } +} + static bool intel_cmtg_transcoder_is_secondary(struct intel_display *display, enum transcoder trans) { @@ -219,3 +231,19 @@ void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state) if (clk_sel_set) intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); } + +void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type) +{ + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + + if (cmtg_transcoder == INVALID_TRANSCODER) + return; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + if (type == LRR) + intel_set_transcoder_timings_lrr(crtc_state, cmtg_transcoder); + else + intel_set_transcoder_timings(crtc_state, cmtg_transcoder); +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index 87092ce6d67b..e3c678019815 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -11,6 +11,12 @@ struct intel_display; struct intel_crtc_state; +enum set_timing_type { + MODESET = 0, + LRR +}; + +void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type); void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index edb9acb1da5c..8455ad878e66 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -60,6 +60,7 @@ #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" #include "intel_crtc.h" @@ -132,8 +133,6 @@ #include "vlv_dsi_pll.h" #include "vlv_dsi_regs.h" -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, - enum transcoder transcoder); static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state); static void hsw_set_transconf(const struct intel_crtc_state *crtc_state); static void bdw_set_pipe_misc(struct intel_dsb *dsb, @@ -1637,6 +1636,7 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta } intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); + intel_cmtg_set_timings(crtc_state, MODESET); if (cpu_transcoder != TRANSCODER_EDP) intel_de_write(display, TRANS_MULT(display, cpu_transcoder), @@ -2665,8 +2665,8 @@ transcoder_has_vrr(const struct intel_crtc_state *crtc_state) return HAS_VRR(display) && !transcoder_is_dsi(cpu_transcoder); } -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, - enum transcoder transcoder) +void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); @@ -2777,8 +2777,8 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta } } -static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, - enum transcoder transcoder) +void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; @@ -6693,6 +6693,7 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, if (new_crtc_state->update_lrr) { intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder); + intel_cmtg_set_timings(new_crtc_state, LRR); intel_vrr_set_fixed_rr_timings(new_crtc_state); intel_vrr_transcoder_enable(new_crtc_state); } diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 98b589e8360d..57ea4f2edf2a 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -424,6 +424,10 @@ void intel_set_m_n(struct intel_display *display, const struct intel_link_m_n *m_n, intel_reg_t data_m_reg, intel_reg_t data_n_reg, intel_reg_t link_m_reg, intel_reg_t link_n_reg); +void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); +void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); void intel_get_m_n(struct intel_display *display, struct intel_link_m_n *m_n, intel_reg_t data_m_reg, intel_reg_t data_n_reg, From d68559164b6e91cdadd3c029a2f4db6088923f9d Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:27 +0530 Subject: [PATCH 082/186] drm/i915/vrr: Pass transcoder to intel_vrr_set_fixed_rr_timings() Take the target transcoder as an explicit parameter so the helper can program VRR VMIN/VMAX/FLIPLINE registers for transcoders other than the crtc_state->cpu_transcoder (e.g. the CMTG transcoder). No functional change: all existing callers pass crtc_state->cpu_transcoder. v2: - Add separate patch for renaming the cpu_transcoder variable. [Ville] Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-11-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 2 +- drivers/gpu/drm/i915/display/intel_vrr.c | 9 +++++---- drivers/gpu/drm/i915/display/intel_vrr.h | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 8455ad878e66..8f6acddac6a9 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -6694,7 +6694,7 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, if (new_crtc_state->update_lrr) { intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder); intel_cmtg_set_timings(new_crtc_state, LRR); - intel_vrr_set_fixed_rr_timings(new_crtc_state); + intel_vrr_set_fixed_rr_timings(new_crtc_state, new_crtc_state->cpu_transcoder); intel_vrr_transcoder_enable(new_crtc_state); } } diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index e03b5daac5be..60a92e8b1094 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -9,6 +9,7 @@ #include "intel_alpm.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display_limits.h" #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_dmc.h" @@ -318,10 +319,10 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state) return intel_vrr_fixed_rr_hw_vtotal(crtc_state); } -void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state) +void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder cpu_transcoder) { struct intel_display *display = to_intel_display(crtc_state); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; if (!intel_vrr_possible(crtc_state)) return; @@ -645,7 +646,7 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) lower_32_bits(crtc_state->cmrr.cmrr_n)); } - intel_vrr_set_fixed_rr_timings(crtc_state); + intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder); if (!intel_vrr_always_use_vrr_tg(display)) intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), @@ -974,7 +975,7 @@ void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) intel_vrr_tg_disable(old_crtc_state); intel_vrr_disable_dc_balancing(old_crtc_state); - intel_vrr_set_fixed_rr_timings(old_crtc_state); + intel_vrr_set_fixed_rr_timings(old_crtc_state, old_crtc_state->cpu_transcoder); } void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h index 4f16ca4af91f..57c5e28378db 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.h +++ b/drivers/gpu/drm/i915/display/intel_vrr.h @@ -15,6 +15,7 @@ struct intel_crtc; struct intel_crtc_state; struct intel_dsb; struct intel_display; +enum transcoder; bool intel_vrr_is_capable(struct intel_connector *connector); bool intel_vrr_is_in_range(struct intel_connector *connector, int vrefresh); @@ -42,7 +43,8 @@ int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state); bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state); -void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state); +void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder cpu_transcoder); void intel_vrr_dcb_reset(const struct intel_crtc_state *old_crtc_state, struct intel_crtc *crtc); bool intel_vrr_always_use_vrr_tg(struct intel_display *display); From 0c37da7668d26027dc495a72941ac48f66bc5eaf Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:28 +0530 Subject: [PATCH 083/186] drm/i915/display: Rename cpu_transcoder parameter to transcoder in VRR fixed-rr path intel_vrr_set_fixed_rr_timings() now takes the target transcoder as an explicit argument rather than implicitly using crtc_state->cpu_transcoder, so the parameter name 'cpu_transcoder' is misleading. Rename it to plain 'transcoder' to reflect that any transcoder may be programmed. No functional change. Signed-off-by: Animesh Manna Reviewed-by: Uma Shankar Link: https://patch.msgid.link/20260615200339.885190-12-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_vrr.c | 8 ++++---- drivers/gpu/drm/i915/display/intel_vrr.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 60a92e8b1094..401a12aee700 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -320,18 +320,18 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state) } void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, - enum transcoder cpu_transcoder) + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); if (!intel_vrr_possible(crtc_state)) return; - intel_de_write(display, TRANS_VRR_VMIN(display, cpu_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, cpu_transcoder), + intel_de_write(display, TRANS_VRR_VMAX(display, transcoder), intel_vrr_fixed_rr_hw_vmax(crtc_state) - 1); - intel_de_write(display, TRANS_VRR_FLIPLINE(display, cpu_transcoder), + intel_de_write(display, TRANS_VRR_FLIPLINE(display, transcoder), intel_vrr_fixed_rr_hw_flipline(crtc_state) - 1); } diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h index 57c5e28378db..55e9c429f579 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.h +++ b/drivers/gpu/drm/i915/display/intel_vrr.h @@ -44,7 +44,7 @@ bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state); void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, - enum transcoder cpu_transcoder); + enum transcoder transcoder); void intel_vrr_dcb_reset(const struct intel_crtc_state *old_crtc_state, struct intel_crtc *crtc); bool intel_vrr_always_use_vrr_tg(struct intel_display *display); From 7b1c73d0f7250773536419a7b6982b8faf9ab31b Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:29 +0530 Subject: [PATCH 084/186] drm/i915/cmtg: Program VRR fixed-rate timings for CMTG transcoder Program the VRR registers of CMTG, as the VRR timing generator will always be enabled for NVL. Add intel_cmtg_set_vrr_timings() which mirrors the per-transcoder VRR VMIN/VMAX/FLIPLINE programming on the CMTG transcoder paired with the pipe's cpu_transcoder. Invoke it from intel_vrr_set_transcoder_timings() and from the LRR fastset path, right after the existing intel_vrr_set_fixed_rr_timings() calls, so the CMTG VRR timing registers stay in sync with the cpu_transcoder's. v2: Use sw state instead of reading from hardware. [Jani] v3: Program cmtg vrr timing registers along with vrr transcoder registers. v4: Reuse vrr timing programming helper. Bspec: 68989 Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-13-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 11 +++++++++++ drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + drivers/gpu/drm/i915/display/intel_display.c | 1 + drivers/gpu/drm/i915/display/intel_vrr.c | 2 ++ 4 files changed, 15 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index cb1d69c17830..c5fff66f0d9e 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -16,6 +16,7 @@ #include "intel_display_power.h" #include "intel_display_regs.h" #include "intel_display_types.h" +#include "intel_vrr.h" /** * DOC: Common Primary Timing Generator (CMTG) @@ -247,3 +248,13 @@ void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_ else intel_set_transcoder_timings(crtc_state, cmtg_transcoder); } + +void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state) +{ + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + intel_vrr_set_fixed_rr_timings(crtc_state, cmtg_transcoder); +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index e3c678019815..e64a26aa5a1c 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -16,6 +16,7 @@ enum set_timing_type { LRR }; +void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type); void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 8f6acddac6a9..3eb93853da9e 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -6695,6 +6695,7 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder); intel_cmtg_set_timings(new_crtc_state, LRR); intel_vrr_set_fixed_rr_timings(new_crtc_state, new_crtc_state->cpu_transcoder); + intel_cmtg_set_vrr_timings(new_crtc_state); intel_vrr_transcoder_enable(new_crtc_state); } } diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 401a12aee700..74094f4335f0 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -7,6 +7,7 @@ #include #include "intel_alpm.h" +#include "intel_cmtg.h" #include "intel_crtc.h" #include "intel_de.h" #include "intel_display_limits.h" @@ -647,6 +648,7 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) } intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder); + intel_cmtg_set_vrr_timings(crtc_state); if (!intel_vrr_always_use_vrr_tg(display)) intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), From 834ae6bb795ff85d6c701665a4bb04ca0eeedd5d Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:30 +0530 Subject: [PATCH 085/186] drm/i915/cmtg: Program VRR control register for CMTG transcoder Add intel_cmtg_set_vrr_ctl() to program TRANS_VRR_CTL for the CMTG transcoder. Purposefully avoid using the existing VRR enable path, as many of its operations are not needed for CMTG. v2: Use sw state instead of reading from hardware. [Jani] v3: Program cmtg vrr control register along with vrr transcoder registers. [R-b from Uma] v4: Split out from vrr timing registers programming. Bspec: 68989 Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-14-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 20 ++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + drivers/gpu/drm/i915/display/intel_vrr.c | 2 ++ 3 files changed, 23 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index c5fff66f0d9e..87198de8dfed 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -17,6 +17,7 @@ #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_vrr.h" +#include "intel_vrr_regs.h" /** * DOC: Common Primary Timing Generator (CMTG) @@ -258,3 +259,22 @@ void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state) intel_vrr_set_fixed_rr_timings(crtc_state, cmtg_transcoder); } + +void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + u32 vrr_ctl; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + vrr_ctl = VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN | + 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) + 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_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index e64a26aa5a1c..76ab908c6e61 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -17,6 +17,7 @@ enum set_timing_type { }; void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type); void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 74094f4335f0..cd380fe8fd01 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -933,6 +933,8 @@ static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state, vrr_ctl |= VRR_CTL_CMRR_ENABLE; intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl); + + intel_cmtg_set_vrr_ctl(crtc_state); } static void intel_vrr_tg_disable(const struct intel_crtc_state *old_crtc_state) From a50f027636f872ab94461aee6e4329d964c0049e Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:31 +0530 Subject: [PATCH 086/186] drm/i915/cmtg: Set link M/N for CMTG transcoder Program CMTG link M/N. Not much to reuse so add a separate function for CMTG. Bspec: 68989 Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-15-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 13 +++++++++++++ drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + drivers/gpu/drm/i915/display/intel_display.c | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 87198de8dfed..533e58d90bb6 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -278,3 +278,16 @@ void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state) intel_de_write(display, TRANS_VRR_CTL(display, cmtg_transcoder), vrr_ctl); } + +void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + const struct intel_link_m_n *m_n = &crtc_state->dp_m_n; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + intel_de_write(display, PIPE_LINK_M1(display, cmtg_transcoder), m_n->link_m); + intel_de_write(display, PIPE_LINK_N1(display, cmtg_transcoder), m_n->link_n); +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index 76ab908c6e61..de852fc2405d 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -16,6 +16,7 @@ enum set_timing_type { LRR }; +void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type); diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 3eb93853da9e..62dc2b414f3c 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -1635,6 +1635,7 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta &crtc_state->dp_m2_n2); } + intel_cmtg_set_m_n(crtc_state); intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); intel_cmtg_set_timings(crtc_state, MODESET); @@ -6687,9 +6688,11 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, display->platform.broadwell || display->platform.haswell) hsw_set_linetime_wm(new_crtc_state); - if (new_crtc_state->update_m_n) + if (new_crtc_state->update_m_n) { intel_cpu_transcoder_set_m1_n1(crtc, new_crtc_state->cpu_transcoder, &new_crtc_state->dp_m_n); + intel_cmtg_set_m_n(new_crtc_state); + } if (new_crtc_state->update_lrr) { intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder); From b6261ac5d589523e2a806639df88561655b268f3 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:32 +0530 Subject: [PATCH 087/186] drm/i915/cmtg: Add hook to enable CMTG with sync to port Add a hook to enable CMTG by programming CMTG CTL with Sync to Port. When CMTG starts running, the Sync to Port bit will be cleared. Add a wait to check its running status and trigger WARN_ON() on timeout. Bspec: 69088 Reviewed-by: Uma Shankar Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-16-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 27 ++++++++++++++++--- drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + .../gpu/drm/i915/display/intel_cmtg_regs.h | 7 +++-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 533e58d90bb6..129d31fd8176 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -117,11 +117,11 @@ static void intel_cmtg_get_config(struct intel_display *display, { u32 val; - val = intel_de_read(display, TRANS_CMTG_CTL_A); + val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_A)); cmtg_config->cmtg_a_enable = val & CMTG_ENABLE; if (intel_cmtg_has_cmtg_b(display)) { - val = intel_de_read(display, TRANS_CMTG_CTL_B); + val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_B)); cmtg_config->cmtg_b_enable = val & CMTG_ENABLE; } @@ -154,14 +154,14 @@ static void intel_cmtg_disable(struct intel_display *display, if (cmtg_config->cmtg_a_enable) { drm_dbg_kms(display->drm, "Disabling CMTG A\n"); - intel_de_rmw(display, TRANS_CMTG_CTL_A, CMTG_ENABLE, 0); + intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_A), CMTG_ENABLE, 0); clk_sel_clr |= CMTG_CLK_SEL_A_MASK; clk_sel_set |= CMTG_CLK_SEL_A_DISABLED; } if (cmtg_config->cmtg_b_enable) { drm_dbg_kms(display->drm, "Disabling CMTG B\n"); - intel_de_rmw(display, TRANS_CMTG_CTL_B, CMTG_ENABLE, 0); + intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_B), CMTG_ENABLE, 0); clk_sel_clr |= CMTG_CLK_SEL_B_MASK; clk_sel_set |= CMTG_CLK_SEL_B_DISABLED; } @@ -291,3 +291,22 @@ void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state) intel_de_write(display, PIPE_LINK_M1(display, cmtg_transcoder), m_n->link_m); intel_de_write(display, PIPE_LINK_N1(display, cmtg_transcoder), m_n->link_n); } + +void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 cmtg_ctl; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + cmtg_ctl = CMTG_SYNC_TO_PORT | CMTG_ENABLE; + + intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), 0, cmtg_ctl); + if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), + CMTG_SYNC_TO_PORT, 50)) { + drm_WARN(display->drm, 1, "CMTG: %s enable timeout\n", + transcoder_name(cpu_transcoder)); + } +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index de852fc2405d..7897422511d8 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -16,6 +16,7 @@ enum set_timing_type { LRR }; +void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h index 4a80b88d88fd..a93236bf7b75 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h @@ -16,8 +16,11 @@ #define CMTG_CLK_SELECT_PHYB_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0x6) #define CMTG_CLK_SEL_B_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0) -#define TRANS_CMTG_CTL_A _MMIO(0x6fa88) -#define TRANS_CMTG_CTL_B _MMIO(0x6fb88) +#define _TRANS_CMTG_CTL_A 0x6fa88 +#define _TRANS_CMTG_CTL_B 0x6fb88 +#define TRANS_CMTG_CTL(trans) _MMIO_TRANS((trans), \ + _TRANS_CMTG_CTL_A, _TRANS_CMTG_CTL_B) #define CMTG_ENABLE REG_BIT(31) +#define CMTG_SYNC_TO_PORT REG_BIT(29) #endif /* __INTEL_CMTG_REGS_H__ */ From 3c6b291c29e77225ec0f28b29b7a7976cc46aefc Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:33 +0530 Subject: [PATCH 088/186] drm/i915/cmtg: Add a hook to make eDP transcoder secondary Program DDI_FUNC_CTL2 to configure the eDP transcoder as secondary to the CMTG transcoder. v2: - Update commit header to be more clear. [Uma] Bspec: 68915 Reviewed-by: Uma Shankar Reviewed-by: Dibin Moolakadan Subrahmanian Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-17-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 14 ++++++++++++++ drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + drivers/gpu/drm/i915/display/intel_display_types.h | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 129d31fd8176..2347958e5f53 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -310,3 +310,17 @@ void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state) transcoder_name(cpu_transcoder)); } } + +void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE); + crtc->cmtg.enabled = true; + drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder)); +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index 7897422511d8..d759cf7e5ae2 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -16,6 +16,7 @@ enum set_timing_type { LRR }; +void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state); void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index aa4772a1c208..6cd102a3b610 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1574,6 +1574,10 @@ struct intel_crtc { #endif bool vblank_psr_notify; + + struct { + bool enabled; + } cmtg; }; struct intel_plane_error { From 3bb44e8d421a1134bb8a9e13061dddd038c0d193 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 01:33:34 +0530 Subject: [PATCH 089/186] drm/i915/cmtg: Modify existing hook to disable CMTG Earlier cmtg_disable() used to disable all instances of CMTG which cannot handle individual request for specific CMTG instance. Introduce cmtg_disable_all() which will disable all cmtg instances and cmtg_disable() only disable specific instance. v2: - Use intel_de_rmw to simplify. [Uma] v3: - Add a code comment describing when cpu_transcoder should be used instead of cmtg_transcoder. [Uma] Signed-off-by: Dibin Moolakadan Subrahmanian Signed-off-by: Animesh Manna Reviewed-by: Uma Shankar Link: https://patch.msgid.link/20260615200339.885190-18-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 46 +++++++++++++++++-- drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + .../gpu/drm/i915/display/intel_cmtg_regs.h | 1 + 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 2347958e5f53..ea39daded18a 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -138,8 +138,8 @@ static bool intel_cmtg_disable_requires_modeset(struct intel_display *display, return cmtg_config->trans_a_secondary || cmtg_config->trans_b_secondary; } -static void intel_cmtg_disable(struct intel_display *display, - struct intel_cmtg_config *cmtg_config) +static void intel_cmtg_disable_all(struct intel_display *display, + struct intel_cmtg_config *cmtg_config) { u32 clk_sel_clr = 0; u32 clk_sel_set = 0; @@ -170,6 +170,46 @@ static void intel_cmtg_disable(struct intel_display *display, intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); } +void intel_cmtg_disable(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + u32 clk_sel_clr = 0; + + if (!crtc->cmtg.enabled) + return; + + crtc->cmtg.enabled = false; + intel_de_rmw(display, TRANS_VRR_CTL(display, cmtg_transcoder), + VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN, 0); + + /* + * Use cpu_transcoder for: + * 1. Exclusive CMTG registers that do not use the standard transcoder offset + * (e.g., TRANS_CMTG_CTL, CMTG_CLK_SEL). + * 2. Registers shared between the eDP and CMTG transcoders. + * (e.g., TRANS_DDI_FUNC_CTL2). + */ + + intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), + CMTG_SECONDARY_MODE, 0); + + intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_ENABLE, 0); + + if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_STATE, 50)) { + drm_WARN(display->drm, 1, "CMTG: %s disable timeout\n", + transcoder_name(cpu_transcoder)); + return; + } + + clk_sel_clr = cpu_transcoder == TRANSCODER_A ? CMTG_CLK_SEL_A_MASK : CMTG_CLK_SEL_B_MASK; + intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, 0); + + drm_dbg_kms(display->drm, "CMTG: %s disabled\n", transcoder_name(cpu_transcoder)); +} + /* * Read out CMTG configuration and, on platforms that allow disabling it without * a modeset, do it. @@ -197,7 +237,7 @@ void intel_cmtg_sanitize(struct intel_display *display) if (intel_cmtg_disable_requires_modeset(display, &cmtg_config)) return; - intel_cmtg_disable(display, &cmtg_config); + intel_cmtg_disable_all(display, &cmtg_config); } bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index d759cf7e5ae2..1b59deb38f2f 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -16,6 +16,7 @@ enum set_timing_type { LRR }; +void intel_cmtg_disable(const struct intel_crtc_state *crtc_state); void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state); void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h index a93236bf7b75..240a02cd4a3a 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h @@ -22,5 +22,6 @@ _TRANS_CMTG_CTL_A, _TRANS_CMTG_CTL_B) #define CMTG_ENABLE REG_BIT(31) #define CMTG_SYNC_TO_PORT REG_BIT(29) +#define CMTG_STATE REG_BIT(23) #endif /* __INTEL_CMTG_REGS_H__ */ From 9570e945f2b6c9cc39b707022185110d957d4499 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 01:33:35 +0530 Subject: [PATCH 090/186] drm/i915/cmtg: Add CMTG HWGB programming Program CMTG guardband to generate the Lower/Upper and early entry guardband indicators to the DMC for DC3co control. v2: - Specify the unit for DC3CO entry/exit latency. [Uma] - Add code comment for default line_time_us. [Uma] Bspec: 75253 Reviewed-by: Uma Shankar Signed-off-by: Dibin Moolakadan Subrahmanian Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-19-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 32 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cmtg.h | 1 + .../gpu/drm/i915/display/intel_cmtg_regs.h | 8 +++++ 3 files changed, 41 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index ea39daded18a..89df0167f667 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -364,3 +364,35 @@ void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) crtc->cmtg.enabled = true; drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder)); } + +/* Bspec: 75253 */ +#define DC3CO_ENTRY_LATENCY_US 55 +#define DC3CO_EXIT_LATENCY_US 40 + +void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 breakeven_gb; + u32 dc5_exit_latency; + u32 line_time_us = 75; /* Max default initialization value */ + u32 val; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + if (crtc_state->linetime) + line_time_us = DIV_ROUND_UP(crtc_state->linetime, 8); + + /* Break Even Guardband - DC3co Entry Latency / linetime */ + breakeven_gb = DIV_ROUND_UP(DC3CO_ENTRY_LATENCY_US, line_time_us); + + /* DC5 Exit Latency - DC3co Exit Latency / linetime */ + dc5_exit_latency = DIV_ROUND_UP(DC3CO_EXIT_LATENCY_US, line_time_us); + + val = REG_FIELD_PREP(CMTG_HW_GB_BREAKEVEN_MASK, breakeven_gb) | + REG_FIELD_PREP(CMTG_HW_GB_DC5_EXIT_LATENCY_MASK, dc5_exit_latency) | + REG_FIELD_PREP(CMTG_HW_GB_UP_LW_BG_DIFF_MASK, 1); + + intel_de_write(display, CMTG_HW_GB(cpu_transcoder), val); +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index 1b59deb38f2f..b2b68b38b7e3 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -26,5 +26,6 @@ void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_ void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state); #endif /* __INTEL_CMTG_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h index 240a02cd4a3a..a4a2a2fe6b66 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h @@ -24,4 +24,12 @@ #define CMTG_SYNC_TO_PORT REG_BIT(29) #define CMTG_STATE REG_BIT(23) +#define _CMTG_HW_GB_A 0x6fa8c +#define _CMTG_HW_GB_B 0x6fb8c +#define CMTG_HW_GB(trans) _MMIO_TRANS((trans), \ + _CMTG_HW_GB_A, _CMTG_HW_GB_B) +#define CMTG_HW_GB_BREAKEVEN_MASK REG_GENMASK(11, 0) +#define CMTG_HW_GB_DC5_EXIT_LATENCY_MASK REG_GENMASK(27, 16) +#define CMTG_HW_GB_UP_LW_BG_DIFF_MASK REG_GENMASK(31, 28) + #endif /* __INTEL_CMTG_REGS_H__ */ From 81c7da958f4707de13bde3687fc45094325a5636 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 01:33:36 +0530 Subject: [PATCH 091/186] drm/i915/cmtg: Add CMTG scan line programming Enable the hardware based guardband calculations which allows DC3co to remain enabled when timings are changing from one fixed refresh rate to another fixed refresh rate. Bspec: 75253 Reviewed-by: Uma Shankar Signed-off-by: Dibin Moolakadan Subrahmanian Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260615200339.885190-20-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 2 ++ drivers/gpu/drm/i915/display/intel_cmtg_regs.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 89df0167f667..96c7608144b9 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -195,6 +195,7 @@ void intel_cmtg_disable(const struct intel_crtc_state *crtc_state) intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), CMTG_SECONDARY_MODE, 0); + intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), CMTG_HW_GB_ENABLE, 0); intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_ENABLE, 0); @@ -361,6 +362,7 @@ void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) return; intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE); + intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), 0, CMTG_HW_GB_ENABLE); crtc->cmtg.enabled = true; drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder)); } diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h index a4a2a2fe6b66..18dcb665df04 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h @@ -32,4 +32,10 @@ #define CMTG_HW_GB_DC5_EXIT_LATENCY_MASK REG_GENMASK(27, 16) #define CMTG_HW_GB_UP_LW_BG_DIFF_MASK REG_GENMASK(31, 28) +#define _CMTG_SCANLINE_GB1_A 0x456A0 +#define _CMTG_SCANLINE_GB1_B 0x456C0 +#define CMTG_SCANLINE_GB1(trans) _MMIO_TRANS((trans), \ + _CMTG_SCANLINE_GB1_A, _CMTG_SCANLINE_GB1_B) +#define CMTG_HW_GB_ENABLE REG_BIT(31) + #endif /* __INTEL_CMTG_REGS_H__ */ From c588a324183b08db37697158213596b14866337e Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:37 +0530 Subject: [PATCH 092/186] drm/i915/cmtg: Add trigger to enable/disable cmtg Enable CMTG with fixed refresh rate mode and with dynamic dc state enabled. Disable CMTG with transcoder disable or if there is a transition to vrr mode from fixed refresh rate mode. v2: - Move the enabled flag update to avoid issue in the disable timeout path. [Uma] v3: - Introduce intel_cmtg_program() rather calling multiple cmtg functions. [Dibin] - Set clock select before cmtg disable as can lost during dc6 entry. [Dibin] - Disable cmtg interrupt in crtc-disable(). [Dibin] - Got R-b from Uma. v4: - Simplify the code further by moving for_each_new_intel_crtc_in_state inside intel_cmtg.c. Signed-off-by: Animesh Manna Reviewed-by: Uma Shankar Link: https://patch.msgid.link/20260615200339.885190-21-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 37 +++++++++++++------- drivers/gpu/drm/i915/display/intel_cmtg.h | 5 ++- drivers/gpu/drm/i915/display/intel_display.c | 14 ++++++++ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 96c7608144b9..8684d2ec2f83 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -12,6 +12,7 @@ #include "intel_cmtg_regs.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display.h" #include "intel_display_device.h" #include "intel_display_power.h" #include "intel_display_regs.h" @@ -333,15 +334,12 @@ void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state) intel_de_write(display, PIPE_LINK_N1(display, cmtg_transcoder), m_n->link_n); } -void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state) +static void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; u32 cmtg_ctl; - if (!intel_cmtg_is_allowed(crtc_state)) - return; - cmtg_ctl = CMTG_SYNC_TO_PORT | CMTG_ENABLE; intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), 0, cmtg_ctl); @@ -352,15 +350,12 @@ void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state) } } -void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) +static void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; - if (!intel_cmtg_is_allowed(crtc_state)) - return; - intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE); intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), 0, CMTG_HW_GB_ENABLE); crtc->cmtg.enabled = true; @@ -371,7 +366,7 @@ void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) #define DC3CO_ENTRY_LATENCY_US 55 #define DC3CO_EXIT_LATENCY_US 40 -void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) +static void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; @@ -380,9 +375,6 @@ void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) u32 line_time_us = 75; /* Max default initialization value */ u32 val; - if (!intel_cmtg_is_allowed(crtc_state)) - return; - if (crtc_state->linetime) line_time_us = DIV_ROUND_UP(crtc_state->linetime, 8); @@ -398,3 +390,24 @@ void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) intel_de_write(display, CMTG_HW_GB(cpu_transcoder), val); } + +void intel_cmtg_program(struct intel_atomic_state *state) +{ + struct intel_crtc *crtc; + struct intel_crtc_state *new_crtc_state; + + for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) { + bool modeset = intel_crtc_needs_modeset(new_crtc_state); + + if (!intel_cmtg_is_allowed(new_crtc_state)) + continue; + /* + * TODO: CMTG needs to be restored on DC6 exit. + */ + if (modeset && new_crtc_state->hw.active && !crtc->cmtg.enabled) { + intel_cmtg_enable_sync(new_crtc_state); + intel_cmtg_set_hwgb(new_crtc_state); + intel_cmtg_enable_ddi(new_crtc_state); + } + } +} diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index b2b68b38b7e3..a08cb2dcee67 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -8,6 +8,7 @@ #include +struct intel_atomic_state; struct intel_display; struct intel_crtc_state; @@ -17,8 +18,6 @@ enum set_timing_type { }; void intel_cmtg_disable(const struct intel_crtc_state *crtc_state); -void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state); -void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state); @@ -26,6 +25,6 @@ void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_ void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state); -void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state); +void intel_cmtg_program(struct intel_atomic_state *state); #endif /* __INTEL_CMTG_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 62dc2b414f3c..e76aa6c8dab6 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -1790,6 +1790,10 @@ static void hsw_crtc_disable(struct intel_atomic_state *state, intel_atomic_get_old_crtc_state(state, crtc); struct intel_crtc *pipe_crtc; + if (crtc->cmtg.enabled) { + intel_cmtg_set_clk_select(old_crtc_state); + intel_cmtg_disable(old_crtc_state); + } /* * FIXME collapse everything to one hook. * Need care with mst->ddi interactions. @@ -6898,6 +6902,11 @@ static void intel_update_crtc(struct intel_atomic_state *state, if (intel_crtc_needs_fastset(new_crtc_state) && old_crtc_state->inherited) intel_crtc_arm_fifo_underrun(crtc, new_crtc_state); + + if (crtc->cmtg.enabled && (intel_crtc_vrr_enabling(state, crtc))) { + intel_cmtg_set_clk_select(new_crtc_state); + intel_cmtg_disable(new_crtc_state); + } } static void intel_old_crtc_state_disables(struct intel_atomic_state *state, @@ -7567,6 +7576,11 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) /* FIXME probably need to sequence this properly */ intel_program_dpkgc_latency(state); + /* + * TODO: DC3co entry condition need to be checked before calling CMTG functions. + */ + intel_cmtg_program(state); + intel_wait_for_vblank_workers(state); /* FIXME: We should call drm_atomic_helper_commit_hw_done() here From f9360ef0547de02d8e93aa39a9f2c837b73f5291 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:38 +0530 Subject: [PATCH 093/186] drm/i915/cmtg: Restore CMTG after DC6 exit Restore CMTG registers after DC6 exit, as they lose their values in the low-power state. v2: Introduce intel_cmtg_restore() instead of calling multiple cmtg functions. [Uma] Signed-off-by: Animesh Manna Reviewed-by: Uma Shankar Link: https://patch.msgid.link/20260615200339.885190-22-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 21 +++++++++++++---- .../drm/i915/display/intel_display_power.c | 23 +++++++++++++++++++ .../drm/i915/display/intel_display_power.h | 2 ++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 8684d2ec2f83..ae59d7e755f3 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -391,20 +391,33 @@ static void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) intel_de_write(display, CMTG_HW_GB(cpu_transcoder), val); } +static void intel_cmtg_restore(const struct intel_crtc_state *crtc_state) +{ + intel_cmtg_set_clk_select(crtc_state); + intel_cmtg_set_timings(crtc_state, MODESET); + intel_cmtg_set_vrr_timings(crtc_state); + intel_cmtg_set_vrr_ctl(crtc_state); + intel_cmtg_set_m_n(crtc_state); +} + void intel_cmtg_program(struct intel_atomic_state *state) { + struct intel_display *display = to_intel_display(state); struct intel_crtc *crtc; struct intel_crtc_state *new_crtc_state; + bool dc3co_to_dc6 = intel_display_power_get_and_reset_dc3co_to_dc6(display); for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) { bool modeset = intel_crtc_needs_modeset(new_crtc_state); if (!intel_cmtg_is_allowed(new_crtc_state)) continue; - /* - * TODO: CMTG needs to be restored on DC6 exit. - */ - if (modeset && new_crtc_state->hw.active && !crtc->cmtg.enabled) { + + if ((modeset || dc3co_to_dc6) && + new_crtc_state->hw.active && !crtc->cmtg.enabled) { + if (dc3co_to_dc6) + intel_cmtg_restore(new_crtc_state); + intel_cmtg_enable_sync(new_crtc_state); intel_cmtg_set_hwgb(new_crtc_state); intel_cmtg_enable_ddi(new_crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 2e51dfcd5dce..9783257651d2 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -285,6 +285,19 @@ sanitize_target_dc_state(struct intel_display *display, return target_dc_state; } +bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display) +{ + struct i915_power_domains *power_domains = &display->power.domains; + bool ret; + + mutex_lock(&power_domains->lock); + ret = power_domains->dc3co_to_dc6; + power_domains->dc3co_to_dc6 = false; + mutex_unlock(&power_domains->lock); + + return ret; +} + /** * intel_display_power_set_target_dc_state - Set target dc state. * @display: display device @@ -300,6 +313,7 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, struct i915_power_well *power_well; bool dc_off_enabled; struct i915_power_domains *power_domains = &display->power.domains; + u32 old_target_dc_state; mutex_lock(&power_domains->lock); power_well = lookup_power_well(display, SKL_DISP_DC_OFF); @@ -320,8 +334,17 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, if (!dc_off_enabled) intel_power_well_enable(display, power_well); + old_target_dc_state = power_domains->target_dc_state; power_domains->target_dc_state = state; + /* + * CMTG must be restored explicitly after DC6 exit. The dc3co_to_dc6 + * flag helps CMTG determine whether restoration is required. + */ + if (old_target_dc_state == DC_STATE_EN_DC3CO && + power_domains->target_dc_state == DC_STATE_EN_UPTO_DC6) + power_domains->dc3co_to_dc6 = true; + if (!dc_off_enabled) intel_power_well_disable(display, power_well); diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index 56dc89eed3f8..b9c9b68072af 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -138,6 +138,7 @@ struct i915_power_domains { */ bool initializing; bool display_core_suspended; + bool dc3co_to_dc6; int power_well_count; u32 dc_state; @@ -179,6 +180,7 @@ void intel_display_power_sanitize_state(struct intel_display *display); void intel_display_power_suspend_late(struct intel_display *display, bool s2idle); void intel_display_power_resume_early(struct intel_display *display); +bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display); void intel_display_power_set_target_dc_state(struct intel_display *display, u32 state); u32 intel_display_power_get_current_dc_state(struct intel_display *display); From 481fe84b7361b3079ce279f7f93f39b9ee88fea9 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Tue, 16 Jun 2026 01:33:39 +0530 Subject: [PATCH 094/186] drm/i915/cmtg: Add CMTG interrupt handling Add support for the CMTG vblank interrupt, which is delivered through the DE port interrupt block. Enable/disable the interrupt via the DE port IMR around CMTG enable/disable, and dispatch the CMTG_VBLANK_{A,B} bits to the corresponding pipe vblank handler in the gen8 DE IRQ handler. Wired up for DISPLAY_VER 35. The CMTG interrupt is not enabled via IER today because CMTG is brought up together with the eDP transcoder; this can be revisited later. v2: - Use consistent DC3co check as used in earlier patches. [Uma] - Use else-if instead of separate if block. [Uma] - Merge mask and unmask function as it is similar. [Uma] - Modify DISPLAY_VER() check. [Uma] v3: - Enable only vblank interrupt. [Dibin] v4: - Keep irq related code to intel_display_irq.c. [Jani, Uma] Signed-off-by: Animesh Manna Reviewed-by: Uma Shankar Link: https://patch.msgid.link/20260615200339.885190-23-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 24 ++++++++++++++++++- .../gpu/drm/i915/display/intel_display_irq.c | 19 +++++++++++++++ .../gpu/drm/i915/display/intel_display_irq.h | 2 ++ .../gpu/drm/i915/display/intel_display_regs.h | 2 ++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index ae59d7e755f3..6da28c185080 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -14,6 +14,7 @@ #include "intel_de.h" #include "intel_display.h" #include "intel_display_device.h" +#include "intel_display_irq.h" #include "intel_display_power.h" #include "intel_display_regs.h" #include "intel_display_types.h" @@ -177,7 +178,7 @@ void intel_cmtg_disable(const struct intel_crtc_state *crtc_state) struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); - u32 clk_sel_clr = 0; + u32 clk_sel_clr = 0, interrupt_mask = 0; if (!crtc->cmtg.enabled) return; @@ -210,6 +211,13 @@ void intel_cmtg_disable(const struct intel_crtc_state *crtc_state) intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, 0); drm_dbg_kms(display->drm, "CMTG: %s disabled\n", transcoder_name(cpu_transcoder)); + + if (cpu_transcoder == TRANSCODER_A) + interrupt_mask = CMTG_VBLANK_A; + else if (cpu_transcoder == TRANSCODER_B) + interrupt_mask = CMTG_VBLANK_B; + + intel_display_irq_port_interrupt_mask(display, interrupt_mask, true); } /* @@ -355,11 +363,25 @@ static void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 interrupt_mask = 0; intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE); intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), 0, CMTG_HW_GB_ENABLE); crtc->cmtg.enabled = true; drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder)); + + /* + * TODO: Currently cmtg is enabled along with eDP transcoder so cmtg + * interrupt is not enabled through IER, need to do some fine + * tuning in future. + */ + + if (cpu_transcoder == TRANSCODER_A) + interrupt_mask = CMTG_VBLANK_A; + else if (cpu_transcoder == TRANSCODER_B) + interrupt_mask = CMTG_VBLANK_B; + + intel_display_irq_port_interrupt_mask(display, interrupt_mask, false); } /* Bspec: 75253 */ diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index 4a821b0674fd..bcb0ee22fb56 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1469,6 +1469,18 @@ static void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl) found = true; } + if (DISPLAY_VER(display) == 35) { + if (iir & CMTG_VBLANK_A) { + intel_handle_vblank(display, PIPE_A); + found = true; + } + + if (iir & CMTG_VBLANK_B) { + intel_handle_vblank(display, PIPE_B); + found = true; + } + } + if (DISPLAY_VER(display) >= 11) { u32 te_trigger = iir & (DSI0_TE | DSI1_TE); @@ -2666,3 +2678,10 @@ void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *s drm_printf(p, "DERRMR: 0x%08x\n", snapshot->derrmr); drm_printf(p, "ERR_INT: 0x%08x\n", snapshot->err_int); } + +void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask) +{ + spin_lock_irq(&display->irq.lock); + bdw_update_port_irq(display, bits, mask ? 0 : bits); + spin_unlock_irq(&display->irq.lock); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.h b/drivers/gpu/drm/i915/display/intel_display_irq.h index a1227cee885a..84446bf53401 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.h +++ b/drivers/gpu/drm/i915/display/intel_display_irq.h @@ -82,4 +82,6 @@ void i915gm_irq_cstate_wa(struct intel_display *display, bool enable); struct intel_display_irq_snapshot *intel_display_irq_snapshot_capture(struct intel_display *display); void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *snapshot, struct drm_printer *p); +void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask); + #endif /* __INTEL_DISPLAY_IRQ_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 4321f8b529da..fe851fe39222 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -1458,6 +1458,8 @@ #define GEN9_AUX_CHANNEL_B (1 << 25) #define DSI1_TE (1 << 24) #define DSI0_TE (1 << 23) +#define CMTG_VBLANK_B (1 << 17) +#define CMTG_VBLANK_A (1 << 14) #define GEN8_DE_PORT_HOTPLUG(hpd_pin) REG_BIT(3 + _HPD_PIN_DDI(hpd_pin)) #define BXT_DE_PORT_HOTPLUG_MASK (GEN8_DE_PORT_HOTPLUG(HPD_PORT_A) | \ GEN8_DE_PORT_HOTPLUG(HPD_PORT_B) | \ From 4aa633b8e7061bb54c4125c9aa57e1e42fe4da76 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:39 +0530 Subject: [PATCH 095/186] drm/i915/display: Remove TGL DC3CO support Remove all Tiger Lake DC3CO-related functions from intel_psr.c and intel_display_power_well.c, as the feature is not enabled and not used. Also remove the TGL/DG1 DC3CO count debugfs entry from intel_dmc.c, as DC3CO is not active on those platforms. A new debugfs entry for Xe3LP will be added in a subsequent patch. Remove the unused dc3co_exitline field from struct intel_psr and struct intel_crtc_state, along with the corresponding EXITLINE register read in intel_psr_get_config(). Changes in v2: - Squash "Remove unused PSR dc3co_exitline field" and "Remove unused dc3co_exitline from crtc_state" into this patch Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-2-dibin.moolakadan.subrahmanian@intel.com --- .../i915/display/intel_display_power_well.c | 25 --- .../drm/i915/display/intel_display_types.h | 3 - drivers/gpu/drm/i915/display/intel_dmc.c | 6 - drivers/gpu/drm/i915/display/intel_psr.c | 171 ------------------ 4 files changed, 205 deletions(-) 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 04bd0dde5bed..2f0d0a77c1a2 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -866,23 +866,6 @@ void gen9_set_dc_state(struct intel_display *display, u32 state) power_domains->dc_state = val & mask; } -static void tgl_enable_dc3co(struct intel_display *display) -{ - drm_dbg_kms(display->drm, "Enabling DC3CO\n"); - gen9_set_dc_state(display, DC_STATE_EN_DC3CO); -} - -static void tgl_disable_dc3co(struct intel_display *display) -{ - drm_dbg_kms(display->drm, "Disabling DC3CO\n"); - intel_de_rmw(display, DC_STATE_EN, DC_STATE_DC3CO_STATUS, 0); - gen9_set_dc_state(display, DC_STATE_DISABLE); - /* - * Delay of 200us DC3CO Exit time B.Spec 49196 - */ - usleep_range(200, 210); -} - static void assert_can_enable_dc5(struct intel_display *display) { enum i915_power_well_id high_pg; @@ -1061,11 +1044,6 @@ void gen9_disable_dc_states(struct intel_display *display) struct intel_cdclk_config cdclk_config = {}; u32 old_state = power_domains->dc_state; - if (power_domains->target_dc_state == DC_STATE_EN_DC3CO) { - tgl_disable_dc3co(display); - return; - } - if (HAS_DISPLAY(display)) { intel_dmc_wl_get_noreg(display); gen9_set_dc_state(display, DC_STATE_DISABLE); @@ -1114,9 +1092,6 @@ static void gen9_dc_off_power_well_disable(struct intel_display *display, return; switch (power_domains->target_dc_state) { - case DC_STATE_EN_DC3CO: - tgl_enable_dc3co(display); - break; case DC_STATE_EN_UPTO_DC6: skl_enable_dc6(display); break; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 6cd102a3b610..f18b13f3e99e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1186,7 +1186,6 @@ struct intel_crtc_state { bool pkg_c_latency_used; /* Only used for state verification. */ enum intel_panel_replay_dsc_support panel_replay_dsc_support; - u32 dc3co_exitline; u16 su_y_granularity; u8 active_non_psr_pipes; u8 entry_setup_frames; @@ -1782,9 +1781,7 @@ struct intel_psr { bool source_panel_replay_support; bool sink_panel_replay_support; bool panel_replay_enabled; - u32 dc3co_exitline; u32 dc3co_exit_delay; - struct delayed_work dc3co_work; u8 entry_setup_frames; u8 io_wake_lines; diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 481fb65b7110..4785001644f5 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -1647,19 +1647,13 @@ static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused) DMC_VERSION_MINOR(dmc->version)); if (DISPLAY_VER(display) >= 12) { - intel_reg_t dc3co_reg; - if (display->platform.dgfx || DISPLAY_VER(display) >= 14) { - dc3co_reg = DG1_DMC_DEBUG3; dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; } else { - dc3co_reg = TGL_DMC_DEBUG3; dc5_reg = TGL_DMC_DEBUG_DC5_COUNT; dc6_reg = TGL_DMC_DEBUG_DC6_COUNT; } - seq_printf(m, "DC3CO count: %d\n", - intel_de_read(display, dc3co_reg)); } else { dc5_reg = display->platform.broxton ? BXT_DMC_DC3_DC5_COUNT : SKL_DMC_DC3_DC5_COUNT; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index e138982dc91f..b7344f2b865e 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -88,22 +88,6 @@ * issues the self-refresh re-enable code is done from a work queue, which * must be correctly synchronized/cancelled when shutting down the pipe." * - * DC3CO (DC3 clock off) - * - * On top of PSR2, GEN12 adds a intermediate power savings state that turns - * clock off automatically during PSR2 idle state. - * The smaller overhead of DC3co entry/exit vs. the overhead of PSR2 deep sleep - * entry/exit allows the HW to enter a low-power state even when page flipping - * periodically (for instance a 30fps video playback scenario). - * - * Every time a flips occurs PSR2 will get out of deep sleep state(if it was), - * so DC3CO is enabled and tgl_dc3co_disable_work is schedule to run after 6 - * frames, if no other flip occurs and the function above is executed, DC3CO is - * disabled and PSR2 is configured to enter deep sleep, resetting again in case - * of another flip. - * Front buffer modifications do not trigger DC3CO activation on purpose as it - * would bring a lot of complexity and most of the moderns systems will only - * use page flips. */ /* @@ -1220,108 +1204,6 @@ static void psr2_program_idle_frames(struct intel_dp *intel_dp, EDP_PSR2_IDLE_FRAMES(idle_frames)); } -static void tgl_psr2_enable_dc3co(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - - psr2_program_idle_frames(intel_dp, 0); - intel_display_power_set_target_dc_state(display, DC_STATE_EN_DC3CO); -} - -static void tgl_psr2_disable_dc3co(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - - intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6); - psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp)); -} - -static void tgl_dc3co_disable_work(struct work_struct *work) -{ - struct intel_dp *intel_dp = - container_of(work, typeof(*intel_dp), psr.dc3co_work.work); - - mutex_lock(&intel_dp->psr.lock); - /* If delayed work is pending, it is not idle */ - if (delayed_work_pending(&intel_dp->psr.dc3co_work)) - goto unlock; - - tgl_psr2_disable_dc3co(intel_dp); -unlock: - mutex_unlock(&intel_dp->psr.lock); -} - -static void tgl_disallow_dc3co_on_psr2_exit(struct intel_dp *intel_dp) -{ - if (!intel_dp->psr.dc3co_exitline) - return; - - cancel_delayed_work(&intel_dp->psr.dc3co_work); - /* Before PSR2 exit disallow dc3co*/ - tgl_psr2_disable_dc3co(intel_dp); -} - -static bool -dc3co_is_pipe_port_compatible(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; - enum port port = dig_port->base.port; - - if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) - return pipe <= PIPE_B && port <= PORT_B; - else - return pipe == PIPE_A && port == PORT_A; -} - -static void -tgl_dc3co_exitline_compute_config(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(intel_dp); - const u32 crtc_vdisplay = crtc_state->uapi.adjusted_mode.crtc_vdisplay; - struct i915_power_domains *power_domains = &display->power.domains; - u32 exit_scanlines; - - /* - * FIXME: Due to the changed sequence of activating/deactivating DC3CO, - * disable DC3CO until the changed dc3co activating/deactivating sequence - * is applied. B.Specs:49196 - */ - return; - - /* - * DMC's DC3CO exit mechanism has an issue with Selective Fecth - * TODO: when the issue is addressed, this restriction should be removed. - */ - if (crtc_state->enable_psr2_sel_fetch) - return; - - if (!(power_domains->allowed_dc_mask & DC_STATE_EN_DC3CO)) - return; - - if (!dc3co_is_pipe_port_compatible(intel_dp, crtc_state)) - return; - - /* Wa_16011303918:adl-p */ - if (intel_display_wa(display, INTEL_DISPLAY_WA_16011303918)) - return; - - /* - * DC3CO Exit time 200us B.Spec 49196 - * PSR2 transcoder Early Exit scanlines = ROUNDUP(200 / line time) + 1 - */ - exit_scanlines = - intel_usecs_to_scanlines(&crtc_state->uapi.adjusted_mode, 200) + 1; - - if (drm_WARN_ON(display->drm, exit_scanlines > crtc_vdisplay)) - return; - - crtc_state->dc3co_exitline = crtc_vdisplay - exit_scanlines; -} - static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) { @@ -1697,8 +1579,6 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, return false; } - tgl_dc3co_exitline_compute_config(intel_dp, crtc_state); - return true; } @@ -2013,12 +1893,6 @@ void intel_psr_get_config(struct intel_encoder *encoder, } pipe_config->enable_psr2_su_region_et = intel_dp->psr.su_region_et_enabled; - - if (DISPLAY_VER(display) >= 12) { - val = intel_de_read(display, - TRANS_EXITLINE(display, cpu_transcoder)); - pipe_config->dc3co_exitline = REG_FIELD_GET(EXITLINE_MASK, val); - } unlock: mutex_unlock(&intel_dp->psr.lock); } @@ -2146,16 +2020,6 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, psr_irq_control(intel_dp); - /* - * TODO: if future platforms supports DC3CO in more than one - * transcoder, EXITLINE will need to be unset when disabling PSR - */ - if (intel_dp->psr.dc3co_exitline) - intel_de_rmw(display, - TRANS_EXITLINE(display, cpu_transcoder), - EXITLINE_MASK, - intel_dp->psr.dc3co_exitline << EXITLINE_SHIFT | EXITLINE_ENABLE); - if (HAS_PSR_HW_TRACKING(display) && HAS_PSR2_SEL_FETCH(display)) intel_de_rmw(display, CHICKEN_PAR1_1, IGNORE_PSR2_HW_TRACKING, intel_dp->psr.psr2_sel_fetch_enabled ? @@ -2255,7 +2119,6 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, /* DC5/DC6 requires at least 6 idle frames */ val = usecs_to_jiffies(intel_get_frame_time_us(crtc_state) * 6); intel_dp->psr.dc3co_exit_delay = val; - intel_dp->psr.dc3co_exitline = crtc_state->dc3co_exitline; intel_dp->psr.psr2_sel_fetch_enabled = crtc_state->enable_psr2_sel_fetch; intel_dp->psr.su_region_et_enabled = crtc_state->enable_psr2_su_region_et; intel_dp->psr.psr2_sel_fetch_cff_enabled = false; @@ -2334,8 +2197,6 @@ static void intel_psr_exit(struct intel_dp *intel_dp) intel_de_rmw(display, TRANS_DP2_CTL(intel_dp->psr.transcoder), TRANS_DP2_PANEL_REPLAY_ENABLE, 0); } else if (intel_dp->psr.sel_update_enabled) { - tgl_disallow_dc3co_on_psr2_exit(intel_dp); - val = intel_de_rmw(display, EDP_PSR2_CTL(display, cpu_transcoder), EDP_PSR2_ENABLE, 0); @@ -2477,7 +2338,6 @@ void intel_psr_disable(struct intel_dp *intel_dp, mutex_unlock(&intel_dp->psr.lock); cancel_work_sync(&intel_dp->psr.work); - cancel_delayed_work_sync(&intel_dp->psr.dc3co_work); } /** @@ -2508,7 +2368,6 @@ void intel_psr_pause(struct intel_dp *intel_dp) mutex_unlock(&psr->lock); cancel_work_sync(&psr->work); - cancel_delayed_work_sync(&psr->dc3co_work); } /** @@ -3659,34 +3518,6 @@ void intel_psr_invalidate(struct intel_display *display, mutex_unlock(&intel_dp->psr.lock); } } -/* - * When we will be completely rely on PSR2 S/W tracking in future, - * intel_psr_flush() will invalidate and flush the PSR for ORIGIN_FLIP - * event also therefore tgl_dc3co_flush_locked() require to be changed - * accordingly in future. - */ -static void -tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits, - enum fb_op_origin origin) -{ - struct intel_display *display = to_intel_display(intel_dp); - - if (!intel_dp->psr.dc3co_exitline || !intel_dp->psr.sel_update_enabled || - !intel_dp->psr.active) - return; - - /* - * At every frontbuffer flush flip event modified delay of delayed work, - * when delayed work schedules that means display has been idle. - */ - if (!(frontbuffer_bits & - INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe))) - return; - - tgl_psr2_enable_dc3co(intel_dp); - mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, - intel_dp->psr.dc3co_exit_delay); -} static void _psr_flush_handle(struct intel_dp *intel_dp) { @@ -3773,7 +3604,6 @@ void intel_psr_flush(struct intel_display *display, if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { - tgl_dc3co_flush_locked(intel_dp, frontbuffer_bits, origin); goto unlock; } @@ -3832,7 +3662,6 @@ void intel_psr_init(struct intel_dp *intel_dp) intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link; INIT_WORK(&intel_dp->psr.work, intel_psr_work); - INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work); mutex_init(&intel_dp->psr.lock); } From 28750a28dac2c8a0042258ba37865319c2837068 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:40 +0530 Subject: [PATCH 096/186] drm/i915/display: Switch DC3CO enable from standalone bit to DC level encoding On platforms prior to xe3, DC3CO was controlled via a standalone enable bit. Starting with xe3, DC3CO is encoded as part of the existing DC_STATE_EN_UPTO_DC* field. No functional change, as DC3CO is not enabled on platforms prior to xe3. Changes in v2: - Update commit header (Uma Shankar) Changes in v3: - Update bit mask to reflect DC3CO (Manna Animesh) Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-3-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display_power.c | 8 ++++---- drivers/gpu/drm/i915/display/intel_display_power_well.c | 6 +++--- drivers/gpu/drm/i915/display/intel_display_regs.h | 4 ++-- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 9783257651d2..e313d719fea1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -267,7 +267,7 @@ sanitize_target_dc_state(struct intel_display *display, static const u32 states[] = { DC_STATE_EN_UPTO_DC6, DC_STATE_EN_UPTO_DC5, - DC_STATE_EN_DC3CO, + DC_STATE_EN_UPTO_DC3CO, DC_STATE_DISABLE, }; int i; @@ -341,7 +341,7 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, * CMTG must be restored explicitly after DC6 exit. The dc3co_to_dc6 * flag helps CMTG determine whether restoration is required. */ - if (old_target_dc_state == DC_STATE_EN_DC3CO && + if (old_target_dc_state == DC_STATE_EN_UPTO_DC3CO && power_domains->target_dc_state == DC_STATE_EN_UPTO_DC6) power_domains->dc3co_to_dc6 = true; @@ -1022,10 +1022,10 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc) switch (requested_dc) { case 4: - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6; + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6; break; case 3: - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC5; + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC5; break; case 2: mask |= DC_STATE_EN_UPTO_DC6; 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 2f0d0a77c1a2..71ea2ecc8c88 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -772,7 +772,7 @@ static u32 gen9_dc_mask(struct intel_display *display) mask = DC_STATE_EN_UPTO_DC5; if (DISPLAY_VER(display) >= 12) - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6 + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; else if (DISPLAY_VER(display) == 11) mask |= DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; @@ -1022,8 +1022,8 @@ static void bxt_verify_dpio_phy_power_wells(struct intel_display *display) static bool gen9_dc_off_power_well_enabled(struct intel_display *display, struct i915_power_well *power_well) { - return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_DC3CO) == 0 && - (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0); + return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO) == 0 && + (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK) == 0); } static void gen9_assert_dbuf_enabled(struct intel_display *display) diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index fe851fe39222..01f6a88fd1a7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -3072,14 +3072,14 @@ enum skl_power_gate { /* GEN9 DC */ #define DC_STATE_EN _MMIO(0x45504) #define DC_STATE_DISABLE 0 -#define DC_STATE_EN_DC3CO REG_BIT(30) #define DC_STATE_DC3CO_STATUS REG_BIT(29) #define HOLD_PHY_CLKREQ_PG1_LATCH REG_BIT(21) #define HOLD_PHY_PG1_LATCH REG_BIT(20) #define DC_STATE_EN_UPTO_DC5 (1 << 0) #define DC_STATE_EN_DC9 (1 << 3) #define DC_STATE_EN_UPTO_DC6 (2 << 0) -#define DC_STATE_EN_UPTO_DC5_DC6_MASK 0x3 +#define DC_STATE_EN_UPTO_DC3CO (3 << 0) +#define DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK 0x3 #define DC_STATE_DEBUG _MMIO(0x45520) #define DC_STATE_DEBUG_MASK_CORES (1 << 0) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index b007343721e1..ab4e0e9573df 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -267,7 +267,7 @@ static bool intel_dmc_wl_check_range(struct intel_display *display, * the DMC and requires a DC exit for proper access. */ switch (dc_state) { - case DC_STATE_EN_DC3CO: + case DC_STATE_EN_UPTO_DC3CO: ranges = xe3lpd_dc3co_dmc_ranges; break; case DC_STATE_EN_UPTO_DC5: From 76e5a222fd8de8c85b95c1cc70f6e601f9c8d558 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:41 +0530 Subject: [PATCH 097/186] drm/i915/display: Use FIELD_PREP() for DC state enable bits Replace open-coded shifts with REG_GENMASK() and REG_FIELD_PREP() for the DC state enable field. Suggested-by: Jani Nikula Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-4-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display_regs.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 01f6a88fd1a7..2255d9d31ca4 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -3075,11 +3075,12 @@ enum skl_power_gate { #define DC_STATE_DC3CO_STATUS REG_BIT(29) #define HOLD_PHY_CLKREQ_PG1_LATCH REG_BIT(21) #define HOLD_PHY_PG1_LATCH REG_BIT(20) -#define DC_STATE_EN_UPTO_DC5 (1 << 0) #define DC_STATE_EN_DC9 (1 << 3) -#define DC_STATE_EN_UPTO_DC6 (2 << 0) -#define DC_STATE_EN_UPTO_DC3CO (3 << 0) -#define DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK 0x3 +#define DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK REG_GENMASK(1, 0) +#define DC_STATE_EN_DISABLE REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 0) +#define DC_STATE_EN_UPTO_DC5 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 1) +#define DC_STATE_EN_UPTO_DC6 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 2) +#define DC_STATE_EN_UPTO_DC3CO REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 3) #define DC_STATE_DEBUG _MMIO(0x45520) #define DC_STATE_DEBUG_MASK_CORES (1 << 0) From 864c879292c884ba11b083fef6cca7c5fd7ad308 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:42 +0530 Subject: [PATCH 098/186] drm/i915/display: Add DC3CO DC_STATE enable/disable support Add DC3CO handling to the dc_off power well sequencing and disable the DMC wakelock when exiting DC3CO. Changes in v2: - Call assert_can_enable_dc3co() before dc3co enable (Manna Animesh) BSpec: 75253 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-5-dibin.moolakadan.subrahmanian@intel.com --- .../i915/display/intel_display_power_well.c | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) 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 71ea2ecc8c88..9c8ea14a5cff 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -866,6 +866,24 @@ void gen9_set_dc_state(struct intel_display *display, u32 state) power_domains->dc_state = val & mask; } +static void assert_can_enable_dc3co(struct intel_display *display) +{ + drm_WARN_ONCE(display->drm, + (intel_de_read(display, DC_STATE_EN) & + DC_STATE_EN_UPTO_DC3CO), + "DC3CO already programmed to be enabled.\n"); + + assert_main_dmc_loaded(display); +} + +static void xe3lpd_enable_dc3co(struct intel_display *display) +{ + assert_can_enable_dc3co(display); + drm_dbg_kms(display->drm, "Enabling DC3CO\n"); + intel_dmc_wl_enable(display, DC_STATE_EN_UPTO_DC3CO); + gen9_set_dc_state(display, DC_STATE_EN_UPTO_DC3CO); +} + static void assert_can_enable_dc5(struct intel_display *display) { enum i915_power_well_id high_pg; @@ -1054,9 +1072,13 @@ void gen9_disable_dc_states(struct intel_display *display) } if (old_state == DC_STATE_EN_UPTO_DC5 || - old_state == DC_STATE_EN_UPTO_DC6) + old_state == DC_STATE_EN_UPTO_DC6 || + old_state == DC_STATE_EN_UPTO_DC3CO) intel_dmc_wl_disable(display); + if (old_state == DC_STATE_EN_UPTO_DC3CO) + return; + intel_cdclk_get_cdclk(display, &cdclk_config); /* Can't read out voltage_level so can't use intel_cdclk_changed() */ drm_WARN_ON(display->drm, @@ -1092,6 +1114,9 @@ static void gen9_dc_off_power_well_disable(struct intel_display *display, return; switch (power_domains->target_dc_state) { + case DC_STATE_EN_UPTO_DC3CO: + xe3lpd_enable_dc3co(display); + break; case DC_STATE_EN_UPTO_DC6: skl_enable_dc6(display); break; From 5465dcd74b0f6abc58077e2b962098e87f05b83f Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:43 +0530 Subject: [PATCH 099/186] drm/i915/display: Add HAS_DC3CO() macro Add HAS_DC3CO() to identify platforms supporting DC3CO. DC3CO is supported from display version 35 onwards. BSpec: 75253 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-6-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display_device.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index acb9ca87dda7..f77b3da2cff5 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -159,6 +159,7 @@ struct intel_display_platforms { #define HAS_CUR_FBC(__display) (!HAS_GMCH(__display) && IS_DISPLAY_VER(__display, 7, 13)) #define HAS_D12_PLANE_MINIMIZATION(__display) ((__display)->platform.rocketlake || (__display)->platform.alderlake_s) #define HAS_DBUF_OVERLAP_DETECTION(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dbuf_overlap_detection) +#define HAS_DC3CO(__display) (DISPLAY_VER(__display) >= 35) #define HAS_DDI(__display) (DISPLAY_INFO(__display)->has_ddi) #define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0) #define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc) From 2706ab80359707d781cbade2b0a82d84e0999754 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:44 +0530 Subject: [PATCH 100/186] drm/i915/display: Add DC3CO support check Add intel_display_power_dc3co_supported() helper to query DC3CO support from allowed_dc_mask. Changes in v2: - Add HAS_DC3CO() check to intel_display_power_dc3co_supported(). Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-7-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display_power.c | 10 ++++++++++ drivers/gpu/drm/i915/display/intel_display_power.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index e313d719fea1..9e66f9a4fcdc 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -381,6 +381,16 @@ u32 intel_display_power_get_current_dc_state(struct intel_display *display) return current_dc_state; } +bool intel_display_power_dc3co_supported(struct intel_display *display) +{ + struct i915_power_domains *power_domains = &display->power.domains; + + if (!HAS_DC3CO(display)) + return false; + + return (power_domains->allowed_dc_mask & DC_STATE_EN_UPTO_DC3CO) == DC_STATE_EN_UPTO_DC3CO; +} + static void __async_put_domains_mask(struct i915_power_domains *power_domains, struct intel_power_domain_mask *mask) { diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index b9c9b68072af..41b4be9018b4 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -184,6 +184,7 @@ bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *displa void intel_display_power_set_target_dc_state(struct intel_display *display, u32 state); u32 intel_display_power_get_current_dc_state(struct intel_display *display); +bool intel_display_power_dc3co_supported(struct intel_display *display); void intel_display_power_runtime_suspend(struct intel_display *display); void intel_display_power_runtime_resume(struct intel_display *display); From 7741043a459a5401d102c38a26ab1686b35ecca0 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:45 +0530 Subject: [PATCH 101/186] drm/i915/psr: Add psr2 deep sleep helper API Add intel_psr2_in_deep_sleep() to check whether PSR2 is currently in DEEP_SLEEP state. Will be used in subsequent patches. Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-8-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 21 +++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_psr.h | 1 + 2 files changed, 22 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index b7344f2b865e..932aff386023 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -2219,6 +2219,27 @@ static void intel_psr_exit(struct intel_dp *intel_dp) intel_dp->psr.active = false; } +bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + enum transcoder cpu_transcoder; + bool in_deep_sleep = false; + u32 val; + + mutex_lock(&intel_dp->psr.lock); + + if (!intel_dp->psr.enabled || !intel_dp->psr.sel_update_enabled) + goto out; + + cpu_transcoder = intel_dp->psr.transcoder; + val = intel_de_read(display, EDP_PSR2_STATUS(display, cpu_transcoder)); + in_deep_sleep = (val & EDP_PSR2_STATUS_STATE_MASK) == + EDP_PSR2_STATUS_STATE_DEEP_SLEEP; +out: + mutex_unlock(&intel_dp->psr.lock); + return in_deep_sleep; +} + static void intel_psr_wait_exit_locked(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h index 29723e63888f..d545fdaa0de7 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.h +++ b/drivers/gpu/drm/i915/display/intel_psr.h @@ -87,5 +87,6 @@ void intel_psr_compute_config_late(struct intel_dp *intel_dp, int intel_psr_min_guardband(struct intel_crtc_state *crtc_state); bool intel_psr_use_trans_push(const struct intel_crtc_state *crtc_state); bool intel_psr_pr_async_video_timing_supported(struct intel_dp *intel_dp); +bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp); #endif /* __INTEL_PSR_H__ */ From d75ea53314db7a8265e5235031b487766779d73c Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:46 +0530 Subject: [PATCH 102/186] drm/i915/display: Add DC3CO compute and set target state in commit tail Compute if dc3co is allowed in intel_atomic_commit_tail() based on pipe/port constraints and runtime triggers and store result in display->power.dc3co. When DC3CO can be enabled, request DC_STATE_EN_UPTO_DC3CO and reduce the DC entry delay. Otherwise, retain the existing delay and set default DC_STATE_EN_UPTO_DC6. Changes in v2: - Move DC3CO compute logic from intel_atomic_check() to intel_atomic_commit_tail as it is not advisable to change persistent state in atomic check (Jani Nikula) - Add psr2 deep sleep check in dc3co compute. - Move allowed computation logic inside dc3co update (Jani Nikula). - Add dc3co support check in dc3co allowed function (Jani Nikula) - Move all dc3co functions to intel_display_power.c and rename functions accordingly (Jani Nikula) - Clean up dc3co/dc6 power async delay in intel_atomic_commit_tail() (Jani Nikula) Changes in v3: - Remove debug print from intel_display_power_dc3co_update() BSpec: 75253 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-9-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 14 +- .../gpu/drm/i915/display/intel_display_core.h | 2 + .../drm/i915/display/intel_display_power.c | 133 ++++++++++++++++++ .../drm/i915/display/intel_display_power.h | 37 +++++ 4 files changed, 181 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index e76aa6c8dab6..ddcf2d2054b7 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -7465,6 +7465,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) struct intel_crtc *crtc; struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {}; struct ref_tracker *wakeref = NULL; + int power_async_delay; for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) intel_atomic_dsb_prepare(state, crtc); @@ -7573,6 +7574,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) /* Now enable the clocks, plane, pipe, and connectors that we set up. */ display->modeset.funcs->commit_modeset_enables(state); + intel_display_power_dc3co_compute(state); + /* FIXME probably need to sequence this properly */ intel_program_dpkgc_latency(state); @@ -7674,11 +7677,12 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) */ intel_uncore_arm_unclaimed_mmio_detection(uncore); } - /* - * Delay re-enabling DC states by 17 ms to avoid the off->on->off - * toggling overhead at and above 60 FPS. - */ - intel_display_power_put_async_delay(display, POWER_DOMAIN_DC_OFF, wakeref, 17); + + power_async_delay = intel_display_power_select_target_dc_state(state); + + intel_display_power_put_async_delay(display, + POWER_DOMAIN_DC_OFF, wakeref, power_async_delay); + intel_display_rpm_put(display, state->wakeref); /* diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 3c17cac1eb97..17f7d3abdb9c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -538,6 +538,8 @@ struct intel_display { struct { struct i915_power_domains domains; + /* DC3CO state */ + struct intel_dc3co_state dc3co; /* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */ u32 chv_phy_control; diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 9e66f9a4fcdc..2ee38ba1fb2c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -14,7 +14,9 @@ #include "intel_cdclk.h" #include "intel_clock_gating.h" #include "intel_combo_phy.h" +#include "intel_crtc.h" #include "intel_de.h" +#include "intel_display.h" #include "intel_display_power.h" #include "intel_display_power_map.h" #include "intel_display_power_well.h" @@ -30,6 +32,8 @@ #include "intel_pch_refclk.h" #include "intel_pmdemand.h" #include "intel_pps_regs.h" +#include "intel_psr.h" +#include "intel_psr_regs.h" #include "intel_snps_phy.h" #include "skl_watermark.h" #include "skl_watermark_regs.h" @@ -391,6 +395,134 @@ bool intel_display_power_dc3co_supported(struct intel_display *display) return (power_domains->allowed_dc_mask & DC_STATE_EN_UPTO_DC3CO) == DC_STATE_EN_UPTO_DC3CO; } +bool intel_display_power_dc3co_allowed(struct intel_display *display) +{ + struct intel_dc3co_state *dc3co = &display->power.dc3co; + bool allowed; + + if (!intel_display_power_dc3co_supported(display)) + return false; + + mutex_lock(&dc3co->lock); + allowed = dc3co->allowed; + mutex_unlock(&dc3co->lock); + + return allowed; +} + +void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger) +{ + struct intel_dc3co_state *dc3co = &display->power.dc3co; + + if (!intel_display_power_dc3co_supported(display)) + return; + + mutex_lock(&dc3co->lock); + dc3co->trigger = trigger; + dc3co->allowed = !!trigger; + mutex_unlock(&dc3co->lock); +} + +static bool intel_dc3co_port_pipe_compatible(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; + enum port port = dig_port->base.port; + int num_pipes = intel_crtc_num_joined_pipes(crtc_state); + + /* Need to follow 1:1 mapping because of CMTG restriction */ + if (DISPLAY_VER(to_intel_display(crtc_state)) == 35) + return num_pipes == 1 && + ((pipe == PIPE_A && port == PORT_A) || + (pipe == PIPE_B && port == PORT_B)); + else + return num_pipes == 1 && pipe <= PIPE_B && port <= PORT_B; +} + +void intel_display_power_dc3co_compute(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + struct intel_crtc *crtc; + struct intel_crtc_state *crtc_state; + struct intel_encoder *encoder; + struct intel_dp *intel_dp; + u8 active_pipes = 0; + enum pipe pipe; + u32 trigger = DC3CO_TRIGGER_NONE; + + if (!intel_display_power_dc3co_supported(display)) + return; + + for_each_intel_crtc(display, crtc) + active_pipes |= crtc->active ? BIT(crtc->pipe) : 0; + + active_pipes = intel_calc_active_pipes(state, active_pipes); + + if (hweight8(active_pipes) != 1) + goto done; + + pipe = ffs(active_pipes) - 1; + crtc = intel_crtc_for_pipe(display, pipe); + + crtc_state = to_intel_crtc_state(crtc->base.state); + + for_each_intel_encoder_mask(display->drm, encoder, + crtc_state->uapi.encoder_mask) { + if (encoder->type != INTEL_OUTPUT_EDP) + goto done; + + intel_dp = enc_to_intel_dp(encoder); + + if (!intel_dc3co_port_pipe_compatible(intel_dp, crtc_state)) + goto done; + + if (intel_psr2_in_deep_sleep(intel_dp)) + goto done; + } + + if (crtc_state->has_lobf) + trigger |= DC3CO_TRIGGER_LOBF; + if (crtc_state->has_panel_replay && intel_dp->as_sdp_supported) + trigger |= DC3CO_TRIGGER_PANEL_REPLAY; + if (crtc_state->has_sel_update) + trigger |= DC3CO_TRIGGER_PSR2; + +done: + intel_display_power_dc3co_update(display, trigger); +} + +/* + * Select the target DC state for this commit and return the async-put delay + * to use when releasing the DC_OFF reference. + * + * Picks DC_STATE_EN_UPTO_DC3CO when DC3CO can be enabled + * otherwise falls back to default DC state of DC_STATE_EN_UPTO_DC6. + * The chosen target is programmed via intel_display_power_set_target_dc_state(). + * + * Returns the async-put delay (in ms) to use when releasing the DC_OFF + * reference: DC3CO_PUT_ASYNC_DELAY_MS when DC3CO was selected, otherwise + * DC6_PUT_ASYNC_DELAY_MS. + */ +int intel_display_power_select_target_dc_state(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + u32 target_dc_state; + + if (!intel_display_power_dc3co_supported(display)) + return DC6_PUT_ASYNC_DELAY_MS; + + if (intel_display_power_dc3co_allowed(display)) + target_dc_state = DC_STATE_EN_UPTO_DC3CO; + else + target_dc_state = DC_STATE_EN_UPTO_DC6; + + intel_display_power_set_target_dc_state(display, target_dc_state); + + return target_dc_state == DC_STATE_EN_UPTO_DC3CO ? + DC3CO_PUT_ASYNC_DELAY_MS : DC6_PUT_ASYNC_DELAY_MS; +} + static void __async_put_domains_mask(struct i915_power_domains *power_domains, struct intel_power_domain_mask *mask) { @@ -1070,6 +1202,7 @@ int intel_display_power_init(struct intel_display *display) sanitize_target_dc_state(display, DC_STATE_EN_UPTO_DC6); mutex_init(&power_domains->lock); + mutex_init(&display->power.dc3co.lock); INIT_DELAYED_WORK(&power_domains->async_put_work, intel_display_power_put_async_work); diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index 41b4be9018b4..546af67b680b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -9,9 +9,12 @@ #include #include +#include "intel_display_limits.h" + enum aux_ch; enum port; struct i915_power_well; +struct intel_atomic_state; struct intel_display; struct intel_encoder; struct ref_tracker; @@ -131,6 +134,36 @@ struct intel_power_domain_mask { DECLARE_BITMAP(bits, POWER_DOMAIN_NUM); }; +/* + * DC3CO enabling triggers (bitmask). + * DC3CO may be enabled when at least one of these triggers is active. + * Additional constraints may still apply. + */ +#define DC3CO_TRIGGER_NONE (0) +#define DC3CO_TRIGGER_PSR2 BIT(0) +#define DC3CO_TRIGGER_LOBF BIT(1) +#define DC3CO_TRIGGER_PANEL_REPLAY BIT(2) +#define DC3CO_TRIGGER_ALL (DC3CO_TRIGGER_PSR2 | \ + DC3CO_TRIGGER_LOBF | \ + DC3CO_TRIGGER_PANEL_REPLAY) + +/* + * Delay to re-enable DC5/DC6 states by 17 ms to avoid the off->on->off + * toggling overhead at and above 60 FPS. + */ +#define DC6_PUT_ASYNC_DELAY_MS 17 +/* + * Use minimal re-enable delay to allow DC3CO entry on + * the next idle frame. + */ +#define DC3CO_PUT_ASYNC_DELAY_MS 1 + +struct intel_dc3co_state { + struct mutex lock; /* protects allowed and trigger fields */ + bool allowed; /* DC3CO compute result */ + u32 trigger; /* Bitmask of active DC3CO triggers */ +}; + struct i915_power_domains { /* * Power wells needed for initialization at driver init and suspend @@ -185,6 +218,10 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, u32 state); u32 intel_display_power_get_current_dc_state(struct intel_display *display); bool intel_display_power_dc3co_supported(struct intel_display *display); +void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger); +bool intel_display_power_dc3co_allowed(struct intel_display *display); +void intel_display_power_dc3co_compute(struct intel_atomic_state *state); +int intel_display_power_select_target_dc_state(struct intel_atomic_state *state); void intel_display_power_runtime_suspend(struct intel_display *display); void intel_display_power_runtime_resume(struct intel_display *display); From eda862176c4e8edd4beebac1b8df4726fcc6719e Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:47 +0530 Subject: [PATCH 103/186] drm/i915/display: Store DC3CO eligibility in PSR state Store DC3CO eligibility in intel_dp->psr during intel_psr_post_plane_update() so PSR configuration can take DC3CO into account. This will be used to control PSR2 parameters such as idle frames. Changes in v2: - Use intel_display_power_dc3co_allowed(display) instead of intel_dc3co_allowed(state) Changes in v3: - Update psr.dc3co_eligible before intel_psr_enable_locked() call (sashiko) Changes in v4: - rename eligible to allowed (Jani Nikula) Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-10-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display_types.h | 2 ++ drivers/gpu/drm/i915/display/intel_psr.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index f18b13f3e99e..30feed50a2d1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1776,6 +1776,8 @@ struct intel_psr { ktime_t last_exit; bool sink_not_reliable; bool irq_aux_error; + /* DC3CO allowed used to control PSR configuration */ + bool dc3co_allowed; u16 su_w_granularity; u16 su_y_granularity; bool source_panel_replay_support; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 932aff386023..0f4263885416 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -2330,6 +2330,7 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) intel_dp->psr.psr2_sel_fetch_cff_enabled = false; intel_dp->psr.active_non_psr_pipes = 0; intel_dp->psr.pkg_c_latency_used = 0; + intel_dp->psr.dc3co_allowed = false; } /** @@ -3120,10 +3121,13 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_encoder *encoder; + bool dc3co_allowed; if (!crtc_state->has_psr) return; + dc3co_allowed = intel_display_power_dc3co_allowed(display); + verify_panel_replay_dsc_state(crtc_state); for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, @@ -3151,6 +3155,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, keep_disabled = true; } + intel_dp->psr.dc3co_allowed = dc3co_allowed; + if (!psr->enabled && !keep_disabled) intel_psr_enable_locked(intel_dp, crtc_state); else if (psr->enabled && !crtc_state->wm_level_disabled) From c525b2f81da88f79483667edde2f9c67b619ea75 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:48 +0530 Subject: [PATCH 104/186] drm/i915/display: PSR2: Set idle_frames to 0 for DC3CO Force idle_frames to 0 when DC3CO is eligible. Changes in v2: - Extend existing Wa_16025596647 condition instead of adding a new if block (Uma Shankar) BSpec: 75253 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-11-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 0f4263885416..091da8341b0f 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1082,10 +1082,11 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) u32 psr_val = 0; u8 idle_frames; - /* Wa_16025596647 */ - if ((DISPLAY_VER(display) == 20 || - IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && - is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used) + /* DC3CO / Wa_16025596647 */ + if (intel_dp->psr.dc3co_allowed || + ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used)) idle_frames = 0; else idle_frames = psr_compute_idle_frames(intel_dp); From 645a651450586223640ca92355a671505671d48d Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:49 +0530 Subject: [PATCH 105/186] drm/i915/display: Enable DC3CO idle protocol in ALPM Add PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL bit definition and set it when DC3CO is allowed. Changes in v2: - Squash "Define DC3CO idle protocol bit in PR_ALPM_CTL" into this patch (Uma Shankar) - Use intel_display_power_dc3co_allowed(display) instead of intel_dc3co_allowed(state) Changes in v3: - check only intel_display_power_dc3co_allowed() before wiriting PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL (Jani Nikula) BSpec: 75253 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-12-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_alpm.c | 5 +++++ drivers/gpu/drm/i915/display/intel_psr_regs.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_alpm.c b/drivers/gpu/drm/i915/display/intel_alpm.c index c6963ea420cc..9b6248548f64 100644 --- a/drivers/gpu/drm/i915/display/intel_alpm.c +++ b/drivers/gpu/drm/i915/display/intel_alpm.c @@ -407,6 +407,11 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp, if (crtc_state->disable_as_sdp_when_pr_active) pr_alpm_ctl |= PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE; + if (intel_display_power_dc3co_allowed(display)) + pr_alpm_ctl |= PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL; + else + pr_alpm_ctl &= ~PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL; + intel_de_write(display, PR_ALPM_CTL(display, cpu_transcoder), pr_alpm_ctl); } diff --git a/drivers/gpu/drm/i915/display/intel_psr_regs.h b/drivers/gpu/drm/i915/display/intel_psr_regs.h index 8afbf5a38335..16a9e3af198d 100644 --- a/drivers/gpu/drm/i915/display/intel_psr_regs.h +++ b/drivers/gpu/drm/i915/display/intel_psr_regs.h @@ -268,6 +268,7 @@ #define _PR_ALPM_CTL_A 0x60948 #define PR_ALPM_CTL(dev_priv, tran) _MMIO_TRANS2(dev_priv, tran, _PR_ALPM_CTL_A) +#define PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL BIT(7) #define PR_ALPM_CTL_ALLOW_LINK_OFF_BETWEEN_AS_SDP_AND_SU BIT(6) #define PR_ALPM_CTL_RFB_UPDATE_CONTROL BIT(5) #define PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE BIT(4) From d49e3c5c811986873df3ee2b69c2b939392b2a35 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:50 +0530 Subject: [PATCH 106/186] drm/i915/display: PSR Add delayed work to exit DC3CO For DC3CO, idle_frames is programmed to 0, so PSR does not enter deep sleep. Add delayed work to schedule DC3CO exit after an idle duration derived from frame time (minimum equivalent of 6 frames). The work is re-armed from the PSR flush path on relevant frontbuffer activity. Once the display remains idle, DC3CO is disabled, idle frames are reprogrammed to their normal value, and DC6 is enabled to allow deeper power savings. Changes in v2: - Squash "PSR set idle frames while exit from DC3CO" into this patch (Uma Shankar) - Add cancel_delayed_work() in intel_psr_disable_locked() before clearing dc3co_eligible (Uma Shankar) Changes in v3: - Re-arm cancelled DC3CO work in psr resume - Schedule DC3CO work from intel_psr_post_plane_update(). This is to make sure DC3CO work scheduling will happen even without psr flush, which may be a valid scenario. Changes in v4: - Remove unused function parameter enum fb_op_origin origin that is never used in the function body. (Animesh) Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-13-dibin.moolakadan.subrahmanian@intel.com --- .../drm/i915/display/intel_display_types.h | 2 + drivers/gpu/drm/i915/display/intel_psr.c | 61 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 30feed50a2d1..ebd00922bf3c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1778,6 +1778,8 @@ struct intel_psr { bool irq_aux_error; /* DC3CO allowed used to control PSR configuration */ bool dc3co_allowed; + /* DC3CO disable work */ + struct delayed_work dc3co_work; u16 su_w_granularity; u16 su_y_granularity; bool source_panel_replay_support; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 091da8341b0f..911afb9cb24e 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1774,6 +1774,50 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp, !crtc_state->has_sel_update); } +static void psr2_dc3co_disable_locked(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (intel_dp->psr.dc3co_allowed) { + intel_dp->psr.dc3co_allowed = false; + intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6); + psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp)); + } +} + +static void psr2_dc3co_disable_work(struct work_struct *work) +{ + struct intel_dp *intel_dp = + container_of(work, typeof(*intel_dp), psr.dc3co_work.work); + + mutex_lock(&intel_dp->psr.lock); + psr2_dc3co_disable_locked(intel_dp); + mutex_unlock(&intel_dp->psr.lock); +} + +static void +psr2_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (!intel_dp->psr.dc3co_allowed) + return; + + if (!intel_dp->psr.sel_update_enabled || + !intel_dp->psr.active) + return; + /* + * At every frontbuffer flush flip event modified delay of delayed work, + * when delayed work schedules that means display has been idle. + */ + if (!(frontbuffer_bits & + INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe))) + return; + + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); +} + static void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) @@ -2331,6 +2375,7 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) intel_dp->psr.psr2_sel_fetch_cff_enabled = false; intel_dp->psr.active_non_psr_pipes = 0; intel_dp->psr.pkg_c_latency_used = 0; + cancel_delayed_work(&intel_dp->psr.dc3co_work); intel_dp->psr.dc3co_allowed = false; } @@ -2361,6 +2406,7 @@ void intel_psr_disable(struct intel_dp *intel_dp, mutex_unlock(&intel_dp->psr.lock); cancel_work_sync(&intel_dp->psr.work); + cancel_delayed_work_sync(&intel_dp->psr.dc3co_work); } /** @@ -2391,6 +2437,7 @@ void intel_psr_pause(struct intel_dp *intel_dp) mutex_unlock(&psr->lock); cancel_work_sync(&psr->work); + cancel_delayed_work_sync(&psr->dc3co_work); } /** @@ -2417,8 +2464,13 @@ void intel_psr_resume(struct intel_dp *intel_dp) goto out; } - if (--intel_dp->psr.pause_counter == 0) + if (--intel_dp->psr.pause_counter == 0) { intel_psr_activate(intel_dp); + /* re-arm cancelled dc3co work from pause */ + if (intel_dp->psr.dc3co_allowed) + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); + } out: mutex_unlock(&psr->lock); @@ -3174,6 +3226,11 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, */ intel_dp->psr.busy_frontbuffer_bits = 0; + if (intel_dp->psr.dc3co_allowed) { + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); + } + mutex_unlock(&psr->lock); } } @@ -3632,6 +3689,7 @@ void intel_psr_flush(struct intel_display *display, if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { + psr2_dc3co_flush_locked(intel_dp, frontbuffer_bits); goto unlock; } @@ -3690,6 +3748,7 @@ void intel_psr_init(struct intel_dp *intel_dp) intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link; INIT_WORK(&intel_dp->psr.work, intel_psr_work); + INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, psr2_dc3co_disable_work); mutex_init(&intel_dp->psr.lock); } From 3f0f34860689bb8ef0adea2a708b4936fd27d114 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:51 +0530 Subject: [PATCH 107/186] drm/i915/display: Add helper to enable DC counter Add xe3lpd_enable_dc_count() to enable the DC_COUNT_EN register. Also define DC_STATE_DC3CO_RESIDENCY to read DC3CO residency. Needed to retrieve DC residency for DC3CO. v2: - Add a guard to xe3lpd_enable_dc_count() instead of relying on caller. [Animesh] Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-14-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display_power_well.c | 8 ++++++++ drivers/gpu/drm/i915/display/intel_display_power_well.h | 1 + drivers/gpu/drm/i915/display/intel_display_regs.h | 5 +++++ drivers/gpu/drm/i915/display/intel_dmc.c | 2 ++ 4 files changed, 16 insertions(+) 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 9c8ea14a5cff..3a93d7378309 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -866,6 +866,14 @@ void gen9_set_dc_state(struct intel_display *display, u32 state) power_domains->dc_state = val & mask; } +void xe3lpd_enable_dc_count(struct intel_display *display) +{ + if (DISPLAY_VER(display) < 35) + return; + + intel_de_write(display, DC_COUNT_EN, DC_COUNT_EN_COUNTER_ENABLE); +} + static void assert_can_enable_dc3co(struct intel_display *display) { drm_WARN_ONCE(display->drm, diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.h b/drivers/gpu/drm/i915/display/intel_display_power_well.h index 8f5524da2d06..0ce64b894436 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.h +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.h @@ -159,6 +159,7 @@ void gen9_set_dc_state(struct intel_display *display, u32 state); void gen9_disable_dc_states(struct intel_display *display); void bxt_enable_dc9(struct intel_display *display); void bxt_disable_dc9(struct intel_display *display); +void xe3lpd_enable_dc_count(struct intel_display *display); extern const struct i915_power_well_ops i9xx_always_on_power_well_ops; extern const struct i915_power_well_ops chv_pipe_power_well_ops; diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 2255d9d31ca4..329909e3f70a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -3086,6 +3086,11 @@ enum skl_power_gate { #define DC_STATE_DEBUG_MASK_CORES (1 << 0) #define DC_STATE_DEBUG_MASK_MEMORY_UP (1 << 1) +#define DC_COUNT_EN _MMIO(0x457B4) +#define DC_COUNT_EN_COUNTER_ENABLE REG_BIT(31) + +#define DC_STATE_DC3CO_RESIDENCY _MMIO(0x457B8) + #define D_COMP_BDW _MMIO(0x138144) /* Pipe WM_LINETIME - watermark line time */ diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 4785001644f5..23e4e87576cb 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -941,6 +941,8 @@ void intel_dmc_load_program(struct intel_display *display) gen9_set_dc_state_debugmask(display); + xe3lpd_enable_dc_count(display); + pipedmc_clock_gating_wa(display, false); } From c90ff0493d154db99dffae5e4d696fd19ac20fc4 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:52 +0530 Subject: [PATCH 108/186] drm/i915/display: Add DC3CO count and residency in dmc debugfs Expose DC3CO count and residency for xe3lp platforms via debugfs. Changes in v2: - Keep dc5_reg register initialization to avoid any invalid access (sashiko) Changes in v3: - Change XE3P_DMC_DC3CO_COUNT address to lower case (Manna Animesh). Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-15-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_dmc.c | 9 ++++++++- drivers/gpu/drm/i915/display/intel_dmc_regs.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 23e4e87576cb..11f5dbf91e68 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -1649,7 +1649,14 @@ static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused) DMC_VERSION_MINOR(dmc->version)); if (DISPLAY_VER(display) >= 12) { - if (display->platform.dgfx || DISPLAY_VER(display) >= 14) { + if (DISPLAY_VER(display) >= 35) { + dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; + seq_printf(m, "DC3CO count: %d\n", + intel_de_read(display, XE3P_DMC_DC3CO_COUNT)); + + seq_printf(m, "DC3CO residency: %d\n", + intel_de_read(display, DC_STATE_DC3CO_RESIDENCY)); + } else if (display->platform.dgfx || DISPLAY_VER(display) >= 14) { dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; } else { dc5_reg = TGL_DMC_DEBUG_DC5_COUNT; diff --git a/drivers/gpu/drm/i915/display/intel_dmc_regs.h b/drivers/gpu/drm/i915/display/intel_dmc_regs.h index 38e342b45af0..6b7978fb8986 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_regs.h +++ b/drivers/gpu/drm/i915/display/intel_dmc_regs.h @@ -531,6 +531,8 @@ enum pipedmc_event_id { #define TGL_DMC_DEBUG3 _MMIO(0x101090) #define DG1_DMC_DEBUG3 _MMIO(0x13415c) +#define XE3P_DMC_DC3CO_COUNT _MMIO(0x8f05c) + #define DMC_WAKELOCK_CFG _MMIO(0x8F1B0) #define DMC_WAKELOCK_CFG_ENABLE REG_BIT(31) #define DMC_WAKELOCK1_CTL _MMIO(0x8F140) From f7336de9924407d5070cea1365c2231b9355e75b Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:53 +0530 Subject: [PATCH 109/186] drm/i915/display: Guard CMTG function calls Check if DC3CO is allowed before calling CMTG functions in intel_atomic_commit_tail() as CMTG is only used by DC3CO DC state. Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-16-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index ddcf2d2054b7..3b17ce669ac5 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -7579,10 +7579,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) /* FIXME probably need to sequence this properly */ intel_program_dpkgc_latency(state); - /* - * TODO: DC3co entry condition need to be checked before calling CMTG functions. - */ - intel_cmtg_program(state); + if (intel_display_power_dc3co_allowed(display)) + intel_cmtg_program(state); intel_wait_for_vblank_workers(state); From fe12dfaef7f7b531c0d2eaad0c1c10af121d347e Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Tue, 16 Jun 2026 21:51:54 +0530 Subject: [PATCH 110/186] drm/i915/display: Enable DC3CO DC state Enable DC3CO mask in get_allowed_dc_mask(). Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Uma Shankar Reviewed-by: Animesh Manna Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260616162154.2630995-17-dibin.moolakadan.subrahmanian@intel.com --- drivers/gpu/drm/i915/display/intel_display_power.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 2ee38ba1fb2c..dc3b31200353 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -1121,7 +1121,9 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc) if (!HAS_DISPLAY(display)) return 0; - if (DISPLAY_VER(display) >= 20) + if (DISPLAY_VER(display) >= 35) + max_dc = 4; + else if (DISPLAY_VER(display) >= 20) max_dc = 2; else if (display->platform.dg2) max_dc = 1; From 94c0b93dc2a592560d41ca5be372f679fbcb5774 Mon Sep 17 00:00:00 2001 From: Dibin Moolakadan Subrahmanian Date: Fri, 5 Jun 2026 21:23:59 +0530 Subject: [PATCH 111/186] drm/i915/display: Mask RO bits in gen9_write_dc_state() The DC_STATE_EN register has read-only status bits that are set by hardware on some platforms. These bits may cause the read-back verification loop in gen9_write_dc_state() to spuriously retry. Mask the RO bits from the read-back comparison to prevent unnecessary retries. Changes in v2: - Rename patch from "drm/i915/display: Use rmw in gen9_write_dc_state() to preserve non-DC bits" to "drm/i915/display: Mask RO bits in gen9_write_dc_state()" - Mask only RO bits rather than masking all non DC state bits in DC_STATE_EN. As the register has also some clear-on-write flags, like 'Display DC*CO State Status DSI'(Imre Deak) Changes in v3: - Limit ro mask to read-back comparison. Changes in v4: - Add bit definitions (Jani Nikula) BSpec: 49437,69115 Signed-off-by: Dibin Moolakadan Subrahmanian Reviewed-by: Imre Deak Signed-off-by: Animesh Manna Link: https://patch.msgid.link/20260605155359.4116572-1-dibin.moolakadan.subrahmanian@intel.com --- .../i915/display/intel_display_power_well.c | 24 +++++++++++++++---- .../gpu/drm/i915/display/intel_display_regs.h | 4 ++++ 2 files changed, 24 insertions(+), 4 deletions(-) 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 3a93d7378309..02cb4d800e23 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -726,12 +726,28 @@ static void assert_can_disable_dc9(struct intel_display *display) */ } +static u32 dc_state_ro_mask(struct intel_display *display) +{ + if (DISPLAY_VER(display) >= 20) + return DC_STATE_EN_CSR_MASK_CMTG_1 | DC_STATE_EN_CSR_MASK_CMTG_0; + else if (DISPLAY_VER(display) >= 13 && !display->platform.dg2) + return DC_STATE_EN_CSR_MASK_CMTG_0; + + return 0; +} + static void gen9_write_dc_state(struct intel_display *display, u32 state) { int rewrites = 0; int rereads = 0; u32 v; + /* + * Mask out RO status bits from read-back comparison. + * HW may set these bits independently, so exclude them + * to prevent the verify loop from retrying due to RO bits mismatch. + */ + u32 ro_mask = dc_state_ro_mask(display); intel_de_write(display, DC_STATE_EN, state); @@ -743,7 +759,7 @@ static void gen9_write_dc_state(struct intel_display *display, do { v = intel_de_read(display, DC_STATE_EN); - if (v != state) { + if ((v & ~ro_mask) != (state & ~ro_mask)) { intel_de_write(display, DC_STATE_EN, state); rewrites++; rereads = 0; @@ -753,10 +769,10 @@ static void gen9_write_dc_state(struct intel_display *display, } while (rewrites < 100); - if (v != state) + if ((v & ~ro_mask) != (state & ~ro_mask)) drm_err(display->drm, - "Writing dc state to 0x%x failed, now 0x%x\n", - state, v); + "Writing dc state to 0x%x failed, now 0x%x (ro_mask=0x%x)\n", + state, v, ro_mask); /* Most of the times we need one retry, avoid spam */ if (rewrites > 1) diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 329909e3f70a..bb7329c8964c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -3081,6 +3081,10 @@ enum skl_power_gate { #define DC_STATE_EN_UPTO_DC5 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 1) #define DC_STATE_EN_UPTO_DC6 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 2) #define DC_STATE_EN_UPTO_DC3CO REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 3) +/* display version 20+ */ +#define DC_STATE_EN_CSR_MASK_CMTG_1 REG_BIT(11) +/* display version 13+, except dg2 */ +#define DC_STATE_EN_CSR_MASK_CMTG_0 REG_BIT(10) #define DC_STATE_DEBUG _MMIO(0x45520) #define DC_STATE_DEBUG_MASK_CORES (1 << 0) From f26a8df8dff9d6e675649b76e03553f43395ed9d Mon Sep 17 00:00:00 2001 From: Mitul Golani Date: Wed, 17 Jun 2026 10:28:50 +0530 Subject: [PATCH 112/186] drm/i915/display: Program TRANS_VTOTAL from mode vtotal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are monitors being sensitive to MSA and end up blanking out when we override Vtotal, DP transcoder uses TRANS_VTOTAL to derive MSA VTotal. Avoid overriding crtc_vtotal to 1 on platform which supports VRR Timing generator and always program VTOTAL from mode timing in transcoder timing paths. --v2: - Remove write to crtc_state->hw.adjusted_mode.crtc_vtotal during intel_vrr_get_config. (Ankit) - Fix merge conflicts. Bspec: 70001 Cc: Ankit Nautiyal Cc: Ville Syrjälä Cc: Suraj Kandpal Signed-off-by: Mitul Golani Reviewed-by: Suraj Kandpal Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260617045850.862100-1-mitulkumar.ajitkumar.golani@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 17 ----------------- drivers/gpu/drm/i915/display/intel_vrr.c | 10 ---------- 2 files changed, 27 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 3b17ce669ac5..805066b02aaa 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -2737,15 +2737,6 @@ void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, HSYNC_START(adjusted_mode->crtc_hsync_start - 1) | HSYNC_END(adjusted_mode->crtc_hsync_end - 1)); - /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not required. Since the support for these bits is going to - * be deprecated in upcoming platforms, avoid writing these bits for the - * platforms that do not use legacy Timing Generator. - */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_vtotal = 1; - intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); @@ -2834,14 +2825,6 @@ void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, intel_de_write(display, TRANS_VSYNC(display, transcoder), VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); - /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not required. Since the support for these bits is going to - * be deprecated in upcoming platforms, avoid writing these bits for the - * platforms that do not use legacy Timing Generator. - */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_vtotal = 1; /* * The double buffer latch point for TRANS_VTOTAL diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index cd380fe8fd01..5d9b11185296 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -1102,16 +1102,6 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display); } - /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not filled. Since for these platforms TRAN_VMIN is always - * filled with crtc_vtotal, use TRAN_VRR_VMIN to get the vtotal for - * adjusted_mode. - */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_state->hw.adjusted_mode.crtc_vtotal = - intel_vrr_vmin_vtotal(crtc_state); - if (HAS_AS_SDP(display)) { trans_vrr_vsync = intel_de_read(display, From 09f102ba4a2ef41c7b67822e0e1094425b26f3a1 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Wed, 17 Jun 2026 09:48:08 +0530 Subject: [PATCH 113/186] drm/i915/cx0: Remove unnecessary hdmi link rate function declaration intel_cx0_phy_check_hdmi_link_rate does not exist but is still declared. Remove it. Signed-off-by: Suraj Kandpal Reviewed-by: Mitul Golani Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260617041807.162927-1-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 1428e7a5a318..95c20eb4b4b7 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -60,7 +60,6 @@ void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder, u8 lane_mask, u8 state); -int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); void intel_cx0_setup_powerdown(struct intel_encoder *encoder); bool intel_cx0_is_hdmi_frl(u32 clock); u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr); From 047071a90cc6a50f14dd8f6fb0578e5b2c36a938 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Tue, 16 Jun 2026 13:36:26 +0530 Subject: [PATCH 114/186] drm/i915/alpm: Move the check for PSR and Fixed RR in compute_config_late With optimized guardband, we need to take into account LOBF requirements via intel_alpm_lobf_min_guardband(). Currently, we set has_lobf based not only on whether it is supported, but also on whether PSR/fixed RR are ON. Since these features can switch ON and OFF seamlessly, this may cause LOBF to change, resulting in a change in guardband requirements, and eventually to a full modeset. To avoid this, always account for LOBF if it is supported, in the encoder->compute_config() stage. For this, allow has_lobf to be set irrespective of PSR/Fixed RR. Later, in the encoder->compute_config_late() stage, use the PSR/Fixed RR checks to set has_lobf to the final value. Move the PSR/Fixed RR checks from intel_alpm_lobf_compute_config() to intel_alpm_lobf_compute_config_late(), where we already account for other LOBF constraints. v2: Reset has_lobf if psr or VRR is ON in intel_alpm_lobf_compute_config_late(). (Animesh) Signed-off-by: Ankit Nautiyal Reviewed-by: Animesh Manna Link: https://patch.msgid.link/20260616080627.2136659-1-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_alpm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_alpm.c b/drivers/gpu/drm/i915/display/intel_alpm.c index 9b6248548f64..f1383764b702 100644 --- a/drivers/gpu/drm/i915/display/intel_alpm.c +++ b/drivers/gpu/drm/i915/display/intel_alpm.c @@ -291,7 +291,9 @@ void intel_alpm_lobf_compute_config_late(struct intel_dp *intel_dp, if (!crtc_state->has_lobf) return; - if (!intel_alpm_lobf_is_window1_sufficient(crtc_state)) { + if (crtc_state->has_psr || + !intel_vrr_is_fixed_rr(crtc_state) || + !intel_alpm_lobf_is_window1_sufficient(crtc_state)) { crtc_state->has_lobf = false; return; } @@ -343,11 +345,7 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp, if (!intel_dp->as_sdp_supported) return; - if (crtc_state->has_psr) - return; - - if (!intel_vrr_always_use_vrr_tg(display) || - !intel_vrr_is_fixed_rr(crtc_state)) + if (!intel_vrr_always_use_vrr_tg(display)) return; if (!(intel_alpm_aux_wake_supported(intel_dp) || From fd3ccb032d34dddf0dfc3e9409f59c7bc54630eb Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 8 Jun 2026 13:24:27 +0100 Subject: [PATCH 115/186] drm/i915/display: make read-only array rates static const Don't populate the read-only array rates the stack at run time, instead make it static const. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20260608122427.48375-1-colin.i.king@gmail.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index b9d11fb8559d..beca0ff5a5b4 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -71,7 +71,7 @@ bool intel_hdmi_is_frl(u32 clock) { - u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 }; + static const u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 }; int i; for (i = 0; i < ARRAY_SIZE(rates); i++) From 3f9de66f8acbf8ff45a91b4920605ed10c6b7c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 12 Jun 2026 20:36:48 +0300 Subject: [PATCH 116/186] drm/i915/cdclk: Fix up CDCLK_FREQ_DECIMAL without a full PLL re-enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GOP (and even Bspec on some platforms) is a bit inconsistent on what the CDCLK_FREQ_DECIMAL divider should be. Currently any mismatch there causes a full CDCLK PLL disable+re-enable, which we really don't want to do if any displays are currently active. Let's instead just reprogram CDCLK_FREQ_DECIMAL when that is the only thing amiss. For any other (more serious) mismatch we still punt to the full PLL reprogramming. We also need to tweak the bxt_cdclk_cd2x_pipe() stuff a bit to consistently select pipe==NONE since we have no idea which pipes are enabled at this point. Since we're not actually changing the CDCLK frequency here we don't need to sync the update to any pipe. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16209 Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260612173653.7830-2-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_cdclk.c | 41 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 189ae2d3cfc9..7bc9b956554b 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1256,9 +1256,22 @@ static void skl_sanitize_cdclk(struct intel_display *display) cdctl = intel_de_read(display, CDCLK_CTL); expected = (cdctl & CDCLK_FREQ_SEL_MASK) | skl_cdclk_decimal(display->cdclk.hw.cdclk); - if (cdctl == expected) - /* All well; nothing to sanitize */ - return; + + if (cdctl != expected) { + cdctl &= ~CDCLK_FREQ_DECIMAL_MASK; + cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; + + if (cdctl != expected) + goto sanitize; + + drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", + intel_de_read(display, CDCLK_CTL), expected); + + intel_de_write(display, CDCLK_CTL, expected); + } + + /* All well; nothing to sanitize */ + return; sanitize: drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); @@ -2354,11 +2367,25 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * (PIPE_NONE). */ cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); - expected &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); - if (cdctl == expected) - /* All well; nothing to sanitize */ - return; + if (cdctl != expected) { + if (DISPLAY_VER(display) < 20) { + cdctl &= ~CDCLK_FREQ_DECIMAL_MASK; + cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; + } + + if (cdctl != expected) + goto sanitize; + + drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", + intel_de_read(display, CDCLK_CTL), expected); + + intel_de_write(display, CDCLK_CTL, expected); + } + + /* All well; nothing to sanitize */ + return; sanitize: drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); From 795e90164a3b898645ec8faa620786e201231f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 12 Jun 2026 20:36:49 +0300 Subject: [PATCH 117/186] drm/i915/cdclk: Print the reason for the CDCLK sanitization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make debugging a bit easier by printing out the specific reason for the CDCLK sanitization. Currently one is forced to guess what is actually happening. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260612173653.7830-3-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_cdclk.c | 37 +++++++++++++++------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 7bc9b956554b..b612ab6f462a 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1229,23 +1229,28 @@ static void skl_set_cdclk(struct intel_display *display, static void skl_sanitize_cdclk(struct intel_display *display) { - u32 cdctl, expected; + u32 cdctl, expected, swf18; /* * check if the pre-os initialized the display * There is SWF18 scratchpad register defined which is set by the * pre-os which can be used by the OS drivers to check the status */ - if ((intel_de_read(display, SWF_ILK(0x18)) & 0x00FFFFFF) == 0) + swf18 = intel_de_read(display, SWF_ILK(0x18)); + if ((swf18 & 0x00FFFFFF) == 0) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to SWF18 0x%x\n", swf18); goto sanitize; + } intel_update_cdclk(display); intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK"); /* Is PLL enabled and locked ? */ if (display->cdclk.hw.vco == 0 || - display->cdclk.hw.cdclk == display->cdclk.hw.bypass) + display->cdclk.hw.cdclk == display->cdclk.hw.bypass) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n"); goto sanitize; + } /* DPLL okay; verify the cdclock * @@ -1261,8 +1266,11 @@ static void skl_sanitize_cdclk(struct intel_display *display) cdctl &= ~CDCLK_FREQ_DECIMAL_MASK; cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; - if (cdctl != expected) + if (cdctl != expected) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x)\n", + intel_de_read(display, CDCLK_CTL), expected); goto sanitize; + } drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", intel_de_read(display, CDCLK_CTL), expected); @@ -1274,8 +1282,6 @@ static void skl_sanitize_cdclk(struct intel_display *display) return; sanitize: - drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); - /* force cdclk programming */ display->cdclk.hw.cdclk = 0; /* force full PLL disable + enable */ @@ -2340,18 +2346,24 @@ static void bxt_sanitize_cdclk(struct intel_display *display) intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK"); if (display->cdclk.hw.vco == 0 || - display->cdclk.hw.cdclk == display->cdclk.hw.bypass) + display->cdclk.hw.cdclk == display->cdclk.hw.bypass) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n"); goto sanitize; + } /* Make sure this is a legal cdclk value for the platform */ cdclk = bxt_calc_cdclk(display, display->cdclk.hw.cdclk); - if (cdclk != display->cdclk.hw.cdclk) + if (cdclk != display->cdclk.hw.cdclk) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad CDCLK frequency\n"); goto sanitize; + } /* Make sure the VCO is correct for the cdclk */ vco = bxt_calc_cdclk_pll_vco(display, cdclk); - if (vco != display->cdclk.hw.vco) + if (vco != display->cdclk.hw.vco) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad VCO frequency\n"); goto sanitize; + } /* * Some BIOS versions leave an incorrect decimal frequency value and @@ -2375,8 +2387,11 @@ static void bxt_sanitize_cdclk(struct intel_display *display) cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; } - if (cdctl != expected) + if (cdctl != expected) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x\n", + intel_de_read(display, CDCLK_CTL), expected); goto sanitize; + } drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", intel_de_read(display, CDCLK_CTL), expected); @@ -2388,8 +2403,6 @@ static void bxt_sanitize_cdclk(struct intel_display *display) return; sanitize: - drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); - /* force cdclk programming */ display->cdclk.hw.cdclk = 0; From aa8faabadc59005c4c087f6e3642b5c6a05c2c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 12 Jun 2026 20:36:50 +0300 Subject: [PATCH 118/186] drm/i915/cdclk: Clean up CDCLK_CTL defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the modern REG_BIT/REG_GENMASK stuff to define the CDCLK_CTL bits. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260612173653.7830-4-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- .../gpu/drm/i915/display/intel_display_regs.h | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index bb7329c8964c..7088ab7ea030 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -2771,7 +2771,7 @@ enum skl_power_gate { #define CDCLK_CTL _MMIO(0x46000) #define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) #define CDCLK_FREQ_450_432 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 0) -#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1) +#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1) #define CDCLK_FREQ_337_308 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 2) #define CDCLK_FREQ_675_617 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 3) #define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) @@ -2782,15 +2782,18 @@ enum skl_power_gate { #define BXT_CDCLK_CD2X_DIV_SEL_1_5 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 1) #define BXT_CDCLK_CD2X_DIV_SEL_2 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 2) #define BXT_CDCLK_CD2X_DIV_SEL_4 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 3) -#define BXT_CDCLK_CD2X_PIPE(pipe) ((pipe) << 20) -#define CDCLK_DIVMUX_CD_OVERRIDE (1 << 19) -#define BXT_CDCLK_CD2X_PIPE_NONE BXT_CDCLK_CD2X_PIPE(3) -#define ICL_CDCLK_CD2X_PIPE(pipe) (_PICK(pipe, 0, 2, 6) << 19) -#define ICL_CDCLK_CD2X_PIPE_NONE (7 << 19) -#define TGL_CDCLK_CD2X_PIPE(pipe) BXT_CDCLK_CD2X_PIPE(pipe) -#define TGL_CDCLK_CD2X_PIPE_NONE ICL_CDCLK_CD2X_PIPE_NONE -#define BXT_CDCLK_SSA_PRECHARGE_ENABLE (1 << 16) -#define CDCLK_FREQ_DECIMAL_MASK (0x7ff) +#define BXT_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 20) +#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) +#define ICL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, _PICK((pipe), 0, 1, 3) << 1) +#define ICL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, 7) +#define TGL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) +#define TGL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(TGL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1) +#define TGL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(TGL_CDCLK_CD2X_PIPE_MASK, 7) +#define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) +#define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) +#define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) /* CDCLK_SQUASH_CTL */ #define CDCLK_SQUASH_CTL _MMIO(0x46008) From 4985069ad4478a0cd9456259ceb8981d1fbded27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 12 Jun 2026 20:36:51 +0300 Subject: [PATCH 119/186] drm/i915/cdclk: Document CDCLK_CTL bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document which CDCLK_CTL bits are relevant for which platforms. Saves me from having to look this up in the spec every time. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260612173653.7830-5-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- .../gpu/drm/i915/display/intel_display_regs.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 7088ab7ea030..6c6a7fafb145 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -2769,31 +2769,31 @@ enum skl_power_gate { */ /* CDCLK_CTL */ #define CDCLK_CTL _MMIO(0x46000) -#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) +#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) /* skl */ #define CDCLK_FREQ_450_432 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 0) #define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1) #define CDCLK_FREQ_337_308 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 2) #define CDCLK_FREQ_675_617 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 3) -#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) +#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) /* lnl+ */ #define MDCLK_SOURCE_SEL_CD2XCLK REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 0) #define MDCLK_SOURCE_SEL_CDCLK_PLL REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 1) -#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) +#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) /* bxt+ */ #define BXT_CDCLK_CD2X_DIV_SEL_1 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 0) #define BXT_CDCLK_CD2X_DIV_SEL_1_5 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 1) #define BXT_CDCLK_CD2X_DIV_SEL_2 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 2) #define BXT_CDCLK_CD2X_DIV_SEL_4 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 3) -#define BXT_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 20) +#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) +#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl */ #define ICL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, _PICK((pipe), 0, 1, 3) << 1) #define ICL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, 7) -#define TGL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) +#define TGL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* tgl+ */ #define TGL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(TGL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1) #define TGL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(TGL_CDCLK_CD2X_PIPE_MASK, 7) -#define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) -#define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) -#define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) +#define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) /* pre-icl */ +#define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) /* bxt/glk */ +#define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) /* pre-lnl */ /* CDCLK_SQUASH_CTL */ #define CDCLK_SQUASH_CTL _MMIO(0x46008) From 67d77d9472c668577dd156c22b649f64957b2c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 12 Jun 2026 20:36:52 +0300 Subject: [PATCH 120/186] drm/i915/cdclk: Introduce bxt_cdclk_cd2x_pipe_mask() and use it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently bxt_sanitize_cdclk() assumes that bxt_cdclk_cd2x_pipe(INVALID_PIPE) gives us the full mask for for the relevant bitfield. While that is true, it does make the code a bit confusing to read. Introduce bxt_cdclk_cd2x_pipe_mask() to make the situation a bit less confusing. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260612173653.7830-6-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_cdclk.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index b612ab6f462a..8db79758187d 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1944,6 +1944,16 @@ static void adlp_cdclk_pll_crawl(struct intel_display *display, int vco) display->cdclk.hw.vco = vco; } +static u32 bxt_cdclk_cd2x_pipe_mask(struct intel_display *display) +{ + if (DISPLAY_VER(display) >= 12) + return TGL_CDCLK_CD2X_PIPE_MASK; + else if (DISPLAY_VER(display) >= 11) + return ICL_CDCLK_CD2X_PIPE_MASK; + else + return BXT_CDCLK_CD2X_PIPE_MASK; +} + static u32 bxt_cdclk_cd2x_pipe(struct intel_display *display, enum pipe pipe) { if (DISPLAY_VER(display) >= 12) { @@ -2378,7 +2388,7 @@ 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(display, INVALID_PIPE); + cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); if (cdctl != expected) { From 8984b402e02ffe8792f6debe07af55e668f4a617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 12 Jun 2026 20:36:53 +0300 Subject: [PATCH 121/186] drm/i915/cdclk: Use the TGL+ CD2x pipe select bits also on ICL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out both CDCLK_CTL pipe select 0b110 (what Bspec lists for pipe C on ICL) and 0b100 (what BSpec lists for pipe C on TGL+) actually select pipe C on ICL. So we can get rid of the weird ICL special case and just use the simpler TGL+ definition of the pipe select bits. This was reverse engineered with a hacked up intel_display_poller. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260612173653.7830-7-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_cdclk.c | 11 ++--------- drivers/gpu/drm/i915/display/intel_display_regs.h | 7 ++----- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 8db79758187d..d3c5e3438d19 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1946,9 +1946,7 @@ static void adlp_cdclk_pll_crawl(struct intel_display *display, int vco) static u32 bxt_cdclk_cd2x_pipe_mask(struct intel_display *display) { - if (DISPLAY_VER(display) >= 12) - return TGL_CDCLK_CD2X_PIPE_MASK; - else if (DISPLAY_VER(display) >= 11) + if (DISPLAY_VER(display) >= 11) return ICL_CDCLK_CD2X_PIPE_MASK; else return BXT_CDCLK_CD2X_PIPE_MASK; @@ -1956,12 +1954,7 @@ static u32 bxt_cdclk_cd2x_pipe_mask(struct intel_display *display) static u32 bxt_cdclk_cd2x_pipe(struct intel_display *display, enum pipe pipe) { - if (DISPLAY_VER(display) >= 12) { - if (pipe == INVALID_PIPE) - return TGL_CDCLK_CD2X_PIPE_NONE; - else - return TGL_CDCLK_CD2X_PIPE(pipe); - } else if (DISPLAY_VER(display) >= 11) { + if (DISPLAY_VER(display) >= 11) { if (pipe == INVALID_PIPE) return ICL_CDCLK_CD2X_PIPE_NONE; else diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 6c6a7fafb145..39e50423132f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -2785,12 +2785,9 @@ 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(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, _PICK((pipe), 0, 1, 3) << 1) +#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl+ */ +#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 TGL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* tgl+ */ -#define TGL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(TGL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1) -#define TGL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(TGL_CDCLK_CD2X_PIPE_MASK, 7) #define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) /* pre-icl */ #define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) /* bxt/glk */ #define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) /* pre-lnl */ From f7140c7cef3037f5d3757bb5db0f4c6aee4a240f Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Mon, 8 Jun 2026 18:07:09 +0530 Subject: [PATCH 122/186] drm/i915/dsb: shift delayed-vblank DSL wait start by one scanline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In intel_dsb_wait_for_delayed_vblank() the VRR path issues a WAIT_DSL_OUT(safe_window_start, vmin_safe_window_end) followed by a WAIT_USEC for SCL+1 scanlines to land on the delayed vblank. Experimentally, DSB appears to observe a slightly stale PIPEDSL value. When the actual scanline has just reached safe_window_start, the DSB may still see a value at or before that boundary when WAIT_DSL_OUT is evaluated, causing the wait to complete immediately. As a result, the subsequent WAIT_USEC executes too early, and the flip-done interrupt fires roughly one frame ahead of the delayed vblank. Shift the scanline start back by one to ensure the wait window is entered reliably. v2: Replace explicit one-scanline delay with scanline boundary adjustment (safe_window_start - 1). (Ville) Signed-off-by: Ankit Nautiyal Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260608123711.1121908-2-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_dsb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index fec8a56e21ea..07dd6318d9cc 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -902,9 +902,17 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, * the hardware itself guarantees that we're SCL lines * away from the delayed vblank, and we won't be inside * the vmin safe window so this extra wait does nothing. + * + * Experimentally, DSB may observe a slightly stale + * PIPEDSL value. When the actual scanline has just reached + * safe_window_start, WAIT_DSL_OUT may complete immediately + * due to the stale value. + * + * Shift the start back by one scanline to ensure the wait + * window is entered reliably. */ intel_dsb_wait_scanline_out(state, dsb, - intel_vrr_safe_window_start(crtc_state), + intel_vrr_safe_window_start(crtc_state) - 1, intel_vrr_vmin_safe_window_end(crtc_state)); /* * When the push is sent during vblank it will trigger From 4a68c7516c57a3d03b406c8aaa073c62149aad68 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Wed, 17 Jun 2026 10:44:17 +0530 Subject: [PATCH 123/186] drm/i915/dsb: Use safe window path when VRR TG is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the VRR timing generator is always used, the hardware behaves as VRR-active regardless of crtc_state->vrr.enable. The DSB paths that depend on the VRR safe window therefore need to follow the VRR code paths in that case too: - dsb_chicken(): program the SAFE_WINDOW chicken bits, - intel_dsb_vblank_evade(): use vmin/vmax vblank starts for the wait window, - intel_dsb_wait_for_delayed_vblank(): wait inside the vmin safe window before the scanline-based delayed vblank wait. Introduce helper pre_commit_use_safe_window() and use it in the three sites v2: Instead of modifying pre_commit_is_vrr_active() use a new helper and use it only in the required places. (Ville). v3: -Keep using pre_commit_is_vrr_active() for DCB path. (Ville) -Add a separate check for fixed RR with VRR TG and use fixed mode vblank start there. (Ville) Signed-off-by: Ankit Nautiyal Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260617051417.2223526-1-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_dsb.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index 07dd6318d9cc..d9a270362a82 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -210,6 +210,18 @@ static int dsb_scanline_to_hw(struct intel_atomic_state *state, return (scanline + vtotal - intel_crtc_scanline_offset(crtc_state)) % vtotal; } +static +bool pre_commit_use_safe_window(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct intel_display *display = to_intel_display(crtc->base.dev); + + if (intel_vrr_always_use_vrr_tg(display)) + return true; + + return pre_commit_is_vrr_active(state, crtc); +} + /* * Bspec suggests that we should always set DSB_SKIP_WAITS_EN. We have approach * different from what is explained in Bspec on how flip is considered being @@ -229,7 +241,7 @@ static u32 dsb_chicken(struct intel_atomic_state *state, u32 chicken = intel_psr_use_trans_push(new_crtc_state) ? DSB_SKIP_WAITS_EN : 0; - if (pre_commit_is_vrr_active(state, crtc)) + if (pre_commit_use_safe_window(state, crtc)) chicken |= DSB_CTRL_WAIT_SAFE_WINDOW | DSB_CTRL_NO_WAIT_VBLANK | DSB_INST_WAIT_SAFE_WINDOW | @@ -798,6 +810,12 @@ void intel_dsb_vblank_evade(struct intel_atomic_state *state, end = intel_vrr_vmax_vblank_start(crtc_state); start = end - vblank_delay - latency; intel_dsb_wait_scanline_out(state, dsb, start, end); + } else if (pre_commit_use_safe_window(state, crtc)) { + int vblank_delay = crtc_state->set_context_latency; + + end = intel_mode_vblank_start(&crtc_state->hw.adjusted_mode); + start = end - vblank_delay - latency; + intel_dsb_wait_scanline_out(state, dsb, start, end); } else { int vblank_delay = intel_mode_vblank_delay(&crtc_state->hw.adjusted_mode); @@ -891,7 +909,7 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, &crtc_state->hw.adjusted_mode; int wait_scanlines; - if (pre_commit_is_vrr_active(state, crtc)) { + if (pre_commit_use_safe_window(state, crtc)) { /* * If the push happened before the vmin decision boundary * we don't know how far we are from the undelayed vblank. From de28bb67e245dbd7f8ea1c5621e1a30e19001283 Mon Sep 17 00:00:00 2001 From: Vinod Govindapillai Date: Tue, 16 Jun 2026 21:41:31 +0300 Subject: [PATCH 124/186] drm/i915/display: update to the BW buddy configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bspec has been updated for xe2_lpd+ platforms on how to handle the bw buddy programming in case no matching memory configuration is found w.r.t the current page mask table. The recommendation is to keep the default settings for the related registers as it is without explicitly disabling the bw buddy. v2: removed extra explanation v3: fixed typo in commit message Bspec: 68871 Suggested-by: Ville Syrjala Signed-off-by: Vinod Govindapillai Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260616184131.295013-1-vinod.govindapillai@intel.com --- drivers/gpu/drm/i915/display/intel_display_power.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index dc3b31200353..8e312f5b9f6d 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -1802,9 +1802,12 @@ static void tgl_bw_buddy_init(struct intel_display *display) if (table[config].page_mask == 0) { drm_dbg_kms(display->drm, "Unknown memory configuration; disabling address buddy logic.\n"); - for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) - intel_de_write(display, BW_BUDDY_CTL(i), - BW_BUDDY_DISABLE); + + if (DISPLAY_VER(display) < 20) { + for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) + intel_de_write(display, BW_BUDDY_CTL(i), + BW_BUDDY_DISABLE); + } } else { for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) { intel_de_write(display, BW_BUDDY_PAGE_MASK(i), From d711dafc4ae8ca3839ce0bb5f9420f4feca0586d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:04 +0300 Subject: [PATCH 125/186] drm/i915/panic: split out i915_gem_panic.[ch] The panic handling is a bit special and isolated part of i915_gem_pages.c. Split it out to i915_gem_panic.[ch]. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/f2093946e723aa27e856987d13692eb0308a4d85.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/gem/i915_gem_object.h | 7 -- drivers/gpu/drm/i915/gem/i915_gem_pages.c | 128 ------------------- drivers/gpu/drm/i915/gem/i915_gem_panic.c | 135 +++++++++++++++++++++ drivers/gpu/drm/i915/gem/i915_gem_panic.h | 18 +++ drivers/gpu/drm/i915/i915_panic.c | 2 +- 6 files changed, 155 insertions(+), 136 deletions(-) create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_panic.c create mode 100644 drivers/gpu/drm/i915/gem/i915_gem_panic.h diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 07802a7f4ce5..091b6647c383 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -167,6 +167,7 @@ gem-y += \ gem/i915_gem_object.o \ gem/i915_gem_object_frontbuffer.o \ gem/i915_gem_pages.o \ + gem/i915_gem_panic.o \ gem/i915_gem_phys.o \ gem/i915_gem_pm.o \ gem/i915_gem_region.o \ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h index 8878539c10ed..2c5d20e4dbaf 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h @@ -17,8 +17,6 @@ #include "i915_vma_types.h" enum intel_region_id; -struct drm_scanout_buffer; -struct intel_panic; #define obj_to_i915(obj__) to_i915((obj__)->base.dev) @@ -693,11 +691,6 @@ i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj) int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj); int i915_gem_object_truncate(struct drm_i915_gem_object *obj); -struct intel_panic *i915_gem_object_alloc_panic(void); -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling); -void i915_gem_object_panic_finish(struct intel_panic *panic); - /** * i915_gem_object_pin_map - return a contiguous mapping of the entire object * @obj: the object to map into kernel address space diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c index df35bdb755e4..59e3d4de7d3c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c @@ -6,11 +6,8 @@ #include #include -#include #include -#include "display/intel_fb.h" -#include "display/intel_display_types.h" #include "gt/intel_gt.h" #include "gt/intel_tlb.h" @@ -359,131 +356,6 @@ static void *i915_gem_object_map_pfn(struct drm_i915_gem_object *obj, return vaddr ?: ERR_PTR(-ENOMEM); } -struct intel_panic { - struct page **pages; - int page; - void *vaddr; -}; - -static void i915_panic_kunmap(struct intel_panic *panic) -{ - if (panic->vaddr) { - drm_clflush_virt_range(panic->vaddr, PAGE_SIZE); - kunmap_local(panic->vaddr); - panic->vaddr = NULL; - } -} - -static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj) -{ - unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i; - struct page *page; - struct page **pages; - struct sgt_iter iter; - - /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */ - pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC); - if (!pages) - return NULL; - - i = 0; - for_each_sgt_page(page, iter, obj->mm.pages) - pages[i++] = page; - return pages; -} - -static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, - unsigned int y, u32 color) -{ - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - unsigned int offset = fb->panic_tiling(sb->width, x, y); - - iosys_map_wr(&sb->map[0], offset, u32, color); -} - -/* - * The scanout buffer pages are not mapped, so for each pixel, - * use kmap_local_page_try_from_panic() to map the page, and write the pixel. - * Try to keep the map from the previous pixel, to avoid too much map/unmap. - */ -static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, - unsigned int y, u32 color) -{ - unsigned int new_page; - unsigned int offset; - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; - - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); - else - offset = y * sb->pitch[0] + x * sb->format->cpp[0]; - - new_page = offset >> PAGE_SHIFT; - offset = offset % PAGE_SIZE; - if (new_page != panic->page) { - i915_panic_kunmap(panic); - panic->page = new_page; - panic->vaddr = - kmap_local_page_try_from_panic(panic->pages[panic->page]); - } - if (panic->vaddr) { - u32 *pix = panic->vaddr + offset; - *pix = color; - } -} - -struct intel_panic *i915_gem_object_alloc_panic(void) -{ - struct intel_panic *panic; - - panic = kzalloc_obj(*panic); - - return panic; -} - -/* - * Setup the gem framebuffer for drm_panic access. - * Use current vaddr if it exists, or setup a list of pages. - * pfn is not supported yet. - */ -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling) -{ - enum i915_map_type has_type; - struct drm_i915_gem_object *obj = to_intel_bo(_obj); - void *ptr; - - ptr = page_unpack_bits(obj->mm.mapping, &has_type); - if (ptr) { - if (i915_gem_object_has_iomem(obj)) - iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr); - else - iosys_map_set_vaddr(&sb->map[0], ptr); - - if (panic_tiling) - sb->set_pixel = i915_gem_object_panic_map_set_pixel; - return 0; - } - if (i915_gem_object_has_struct_page(obj)) { - panic->pages = i915_gem_object_panic_pages(obj); - if (!panic->pages) - return -ENOMEM; - panic->page = -1; - sb->set_pixel = i915_gem_object_panic_page_set_pixel; - return 0; - } - return -EOPNOTSUPP; -} - -void i915_gem_object_panic_finish(struct intel_panic *panic) -{ - i915_panic_kunmap(panic); - panic->page = -1; - kfree(panic->pages); - panic->pages = NULL; -} - /* get, pin, and map the pages of the object into kernel space */ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj, enum i915_map_type type) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c new file mode 100644 index 000000000000..7407c5668c71 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +/* Copyright © 2026 Intel Corporation */ + +#include +#include + +#include "display/intel_fb.h" +#include "display/intel_display_types.h" +#include "i915_gem_object.h" +#include "i915_gem_panic.h" + +struct intel_panic { + struct page **pages; + int page; + void *vaddr; +}; + +static void i915_panic_kunmap(struct intel_panic *panic) +{ + if (panic->vaddr) { + drm_clflush_virt_range(panic->vaddr, PAGE_SIZE); + kunmap_local(panic->vaddr); + panic->vaddr = NULL; + } +} + +static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj) +{ + unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i; + struct page *page; + struct page **pages; + struct sgt_iter iter; + + /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */ + pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC); + if (!pages) + return NULL; + + i = 0; + for_each_sgt_page(page, iter, obj->mm.pages) + pages[i++] = page; + return pages; +} + +static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, + unsigned int y, u32 color) +{ + struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; + unsigned int offset = fb->panic_tiling(sb->width, x, y); + + iosys_map_wr(&sb->map[0], offset, u32, color); +} + +/* + * The scanout buffer pages are not mapped, so for each pixel, + * use kmap_local_page_try_from_panic() to map the page, and write the pixel. + * Try to keep the map from the previous pixel, to avoid too much map/unmap. + */ +static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, + unsigned int y, u32 color) +{ + unsigned int new_page; + unsigned int offset; + struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; + struct intel_panic *panic = fb->panic; + + if (fb->panic_tiling) + offset = fb->panic_tiling(sb->width, x, y); + else + offset = y * sb->pitch[0] + x * sb->format->cpp[0]; + + new_page = offset >> PAGE_SHIFT; + offset = offset % PAGE_SIZE; + if (new_page != panic->page) { + i915_panic_kunmap(panic); + panic->page = new_page; + panic->vaddr = + kmap_local_page_try_from_panic(panic->pages[panic->page]); + } + if (panic->vaddr) { + u32 *pix = panic->vaddr + offset; + *pix = color; + } +} + +struct intel_panic *i915_gem_object_alloc_panic(void) +{ + struct intel_panic *panic; + + panic = kzalloc_obj(*panic); + + return panic; +} + +/* + * Setup the gem framebuffer for drm_panic access. + * Use current vaddr if it exists, or setup a list of pages. + * pfn is not supported yet. + */ +int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *_obj, bool panic_tiling) +{ + enum i915_map_type has_type; + struct drm_i915_gem_object *obj = to_intel_bo(_obj); + void *ptr; + + ptr = page_unpack_bits(obj->mm.mapping, &has_type); + if (ptr) { + if (i915_gem_object_has_iomem(obj)) + iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr); + else + iosys_map_set_vaddr(&sb->map[0], ptr); + + if (panic_tiling) + sb->set_pixel = i915_gem_object_panic_map_set_pixel; + return 0; + } + if (i915_gem_object_has_struct_page(obj)) { + panic->pages = i915_gem_object_panic_pages(obj); + if (!panic->pages) + return -ENOMEM; + panic->page = -1; + sb->set_pixel = i915_gem_object_panic_page_set_pixel; + return 0; + } + return -EOPNOTSUPP; +} + +void i915_gem_object_panic_finish(struct intel_panic *panic) +{ + i915_panic_kunmap(panic); + panic->page = -1; + kfree(panic->pages); + panic->pages = NULL; +} diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.h b/drivers/gpu/drm/i915/gem/i915_gem_panic.h new file mode 100644 index 000000000000..91ab6722d37c --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __I915_GEM_PANIC_H__ +#define __I915_GEM_PANIC_H__ + +#include + +struct drm_gem_object; +struct drm_scanout_buffer; +struct intel_panic; + +struct intel_panic *i915_gem_object_alloc_panic(void); +int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *_obj, bool panic_tiling); +void i915_gem_object_panic_finish(struct intel_panic *panic); + +#endif /* __I915_GEM_PANIC_H__ */ diff --git a/drivers/gpu/drm/i915/i915_panic.c b/drivers/gpu/drm/i915/i915_panic.c index 728be077e8e8..412db72797d8 100644 --- a/drivers/gpu/drm/i915/i915_panic.c +++ b/drivers/gpu/drm/i915/i915_panic.c @@ -6,7 +6,7 @@ #include "display/intel_display_types.h" #include "display/intel_fb.h" -#include "gem/i915_gem_object.h" +#include "gem/i915_gem_panic.h" #include "i915_panic.h" From 8430d20cad53c4df9a3b87b612b605367ce26630 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:05 +0300 Subject: [PATCH 126/186] drm/i915/panic: squash i915_panic.c into i915_gem_panic.c Having two small files for panic handling is a bit too much. Merge i915_panic.c into i915_gem_panic.c. This is just code movement, cleanups will follow. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/4c294d6402e003040b934d94a0b14bd42704e21f.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/Makefile | 3 +- drivers/gpu/drm/i915/gem/i915_gem_panic.c | 33 ++++++++++++++++++--- drivers/gpu/drm/i915/gem/i915_gem_panic.h | 9 +----- drivers/gpu/drm/i915/i915_driver.c | 2 +- drivers/gpu/drm/i915/i915_panic.c | 35 ----------------------- drivers/gpu/drm/i915/i915_panic.h | 9 ------ 6 files changed, 32 insertions(+), 59 deletions(-) delete mode 100644 drivers/gpu/drm/i915/i915_panic.c delete mode 100644 drivers/gpu/drm/i915/i915_panic.h diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 091b6647c383..1fd7a1a5f315 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -83,8 +83,7 @@ i915-y += \ i915_fb_pin.o \ i915_hdcp_gsc.o \ i915_initial_plane.o \ - i915_overlay.o \ - i915_panic.o + i915_overlay.o # "Graphics Technology" (aka we talk to the gpu) gt-y += \ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c index 7407c5668c71..14ea45dcdd8f 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_panic.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -3,6 +3,7 @@ #include #include +#include #include "display/intel_fb.h" #include "display/intel_display_types.h" @@ -83,7 +84,7 @@ static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, } } -struct intel_panic *i915_gem_object_alloc_panic(void) +static struct intel_panic *i915_gem_object_alloc_panic(void) { struct intel_panic *panic; @@ -97,8 +98,8 @@ struct intel_panic *i915_gem_object_alloc_panic(void) * Use current vaddr if it exists, or setup a list of pages. * pfn is not supported yet. */ -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling) +static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *_obj, bool panic_tiling) { enum i915_map_type has_type; struct drm_i915_gem_object *obj = to_intel_bo(_obj); @@ -126,10 +127,34 @@ int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_bu return -EOPNOTSUPP; } -void i915_gem_object_panic_finish(struct intel_panic *panic) +static void i915_gem_object_panic_finish(struct intel_panic *panic) { i915_panic_kunmap(panic); panic->page = -1; kfree(panic->pages); panic->pages = NULL; } + +static struct intel_panic *intel_panic_alloc(void) +{ + return i915_gem_object_alloc_panic(); +} + +static int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +{ + struct intel_framebuffer *fb = sb->private; + struct drm_gem_object *obj = intel_fb_bo(&fb->base); + + return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling); +} + +static void intel_panic_finish(struct intel_panic *panic) +{ + return i915_gem_object_panic_finish(panic); +} + +const struct intel_display_panic_interface i915_display_panic_interface = { + .alloc = intel_panic_alloc, + .setup = intel_panic_setup, + .finish = intel_panic_finish, +}; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.h b/drivers/gpu/drm/i915/gem/i915_gem_panic.h index 91ab6722d37c..82c3aca6f1f3 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_panic.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.h @@ -6,13 +6,6 @@ #include -struct drm_gem_object; -struct drm_scanout_buffer; -struct intel_panic; - -struct intel_panic *i915_gem_object_alloc_panic(void); -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling); -void i915_gem_object_panic_finish(struct intel_panic *panic); +extern const struct intel_display_panic_interface i915_display_panic_interface; #endif /* __I915_GEM_PANIC_H__ */ diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 0520cd124686..43f747c3c31f 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -78,6 +78,7 @@ #include "gem/i915_gem_ioctls.h" #include "gem/i915_gem_mman.h" #include "gem/i915_gem_object_frontbuffer.h" +#include "gem/i915_gem_panic.h" #include "gem/i915_gem_pm.h" #include "gt/intel_gt.h" #include "gt/intel_gt_pm.h" @@ -110,7 +111,6 @@ #include "i915_irq.h" #include "i915_memcpy.h" #include "i915_overlay.h" -#include "i915_panic.h" #include "i915_perf.h" #include "i915_query.h" #include "i915_reg.h" diff --git a/drivers/gpu/drm/i915/i915_panic.c b/drivers/gpu/drm/i915/i915_panic.c deleted file mode 100644 index 412db72797d8..000000000000 --- a/drivers/gpu/drm/i915/i915_panic.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -/* Copyright © 2025 Intel Corporation */ - -#include -#include - -#include "display/intel_display_types.h" -#include "display/intel_fb.h" -#include "gem/i915_gem_panic.h" - -#include "i915_panic.h" - -static struct intel_panic *intel_panic_alloc(void) -{ - return i915_gem_object_alloc_panic(); -} - -static int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) -{ - struct intel_framebuffer *fb = sb->private; - struct drm_gem_object *obj = intel_fb_bo(&fb->base); - - return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling); -} - -static void intel_panic_finish(struct intel_panic *panic) -{ - return i915_gem_object_panic_finish(panic); -} - -const struct intel_display_panic_interface i915_display_panic_interface = { - .alloc = intel_panic_alloc, - .setup = intel_panic_setup, - .finish = intel_panic_finish, -}; diff --git a/drivers/gpu/drm/i915/i915_panic.h b/drivers/gpu/drm/i915/i915_panic.h deleted file mode 100644 index 743d8c861c42..000000000000 --- a/drivers/gpu/drm/i915/i915_panic.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* Copyright © 2025 Intel Corporation */ - -#ifndef __I915_PANIC_H__ -#define __I915_PANIC_H__ - -extern const struct intel_display_panic_interface i915_display_panic_interface; - -#endif /* __I915_PANIC_H__ */ From 566ef35261dd8f3686c02b12ed383f7e6466a6de Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:06 +0300 Subject: [PATCH 127/186] drm/i915/panic: remove the extra layer from panic hooks The extra layer in the panic hooks is useless. Remove it. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/69a776c2951b3d1f81d8eb3870cbc7fda4d1c6e0.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/gem/i915_gem_panic.c | 30 ++++++----------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c index 14ea45dcdd8f..bb26a0ece176 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_panic.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -98,9 +98,11 @@ static struct intel_panic *i915_gem_object_alloc_panic(void) * Use current vaddr if it exists, or setup a list of pages. * pfn is not supported yet. */ -static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling) +static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) { + struct intel_framebuffer *fb = sb->private; + struct drm_gem_object *_obj = intel_fb_bo(&fb->base); + bool panic_tiling = fb->panic_tiling; enum i915_map_type has_type; struct drm_i915_gem_object *obj = to_intel_bo(_obj); void *ptr; @@ -135,26 +137,8 @@ static void i915_gem_object_panic_finish(struct intel_panic *panic) panic->pages = NULL; } -static struct intel_panic *intel_panic_alloc(void) -{ - return i915_gem_object_alloc_panic(); -} - -static int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) -{ - struct intel_framebuffer *fb = sb->private; - struct drm_gem_object *obj = intel_fb_bo(&fb->base); - - return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling); -} - -static void intel_panic_finish(struct intel_panic *panic) -{ - return i915_gem_object_panic_finish(panic); -} - const struct intel_display_panic_interface i915_display_panic_interface = { - .alloc = intel_panic_alloc, - .setup = intel_panic_setup, - .finish = intel_panic_finish, + .alloc = i915_gem_object_alloc_panic, + .setup = i915_gem_object_panic_setup, + .finish = i915_gem_object_panic_finish, }; From c55b693678c2c1c57ffd952d3e17562443a8d5d3 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:07 +0300 Subject: [PATCH 128/186] drm/{i915,xe}/panic: pass obj to panic setup Start reducing i915 and xe core dependency on struct intel_framebuffer by passing the fb obj from display. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/840c4ccaced5f1c82277285938287776c8cdf513.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_parent.c | 5 +++-- drivers/gpu/drm/i915/display/intel_parent.h | 3 ++- drivers/gpu/drm/i915/display/intel_plane.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_panic.c | 5 ++--- drivers/gpu/drm/xe/display/xe_panic.c | 6 +++--- include/drm/intel/display_parent_interface.h | 3 ++- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index a5816561be40..0b2bc2d38442 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -251,9 +251,10 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display) return display->parent->panic->alloc(); } -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb) +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj) { - return display->parent->panic->setup(panic, sb); + return display->parent->panic->setup(panic, sb, obj); } void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic) diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 27e35f891a6b..4197d1b1af61 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -105,7 +105,8 @@ void intel_parent_overlay_cleanup(struct intel_display *display); /* panic */ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display); -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb); +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj); void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic); /* pc8 */ diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index acfe974cdc92..0fc7325fa96b 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -1627,7 +1627,7 @@ static int intel_get_scanout_buffer(struct drm_plane *plane, return -EOPNOTSUPP; } sb->private = fb; - ret = intel_parent_panic_setup(display, fb->panic, sb); + ret = intel_parent_panic_setup(display, fb->panic, sb, obj); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c index bb26a0ece176..001ccfbf7ab7 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_panic.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -5,7 +5,6 @@ #include #include -#include "display/intel_fb.h" #include "display/intel_display_types.h" #include "i915_gem_object.h" #include "i915_gem_panic.h" @@ -98,10 +97,10 @@ static struct intel_panic *i915_gem_object_alloc_panic(void) * Use current vaddr if it exists, or setup a list of pages. * pfn is not supported yet. */ -static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *_obj) { struct intel_framebuffer *fb = sb->private; - struct drm_gem_object *_obj = intel_fb_bo(&fb->base); bool panic_tiling = fb->panic_tiling; enum i915_map_type has_type; struct drm_i915_gem_object *obj = to_intel_bo(_obj); diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index bebb21d617f0..d7f456eec597 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -84,10 +84,10 @@ static struct intel_panic *xe_panic_alloc(void) return panic; } -static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *obj) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); + struct xe_bo *bo = gem_to_xe_bo(obj); if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo)) return -ENODEV; diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index 39991afeb173..b0362e231d84 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -167,7 +167,8 @@ struct intel_display_overlay_interface { struct intel_display_panic_interface { struct intel_panic *(*alloc)(void); - int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb); + int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *obj); void (*finish)(struct intel_panic *panic); }; From d9608402ff0eb599fca3b1df8ea1e21443b789d8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:08 +0300 Subject: [PATCH 129/186] drm/xe/panic: store fb bo in struct intel_panic Drop the dependency on intel_fb_bo() and intel_fb.h by storing the fb bo passed in the setup hook to struct intel_panic. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/735c3f18212207db63d71364d6a8569480c81b42.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_panic.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index d7f456eec597..4b86760ec00a 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -6,7 +6,6 @@ #include #include "intel_display_types.h" -#include "intel_fb.h" #include "xe_bo.h" #include "xe_panic.h" #include "xe_res_cursor.h" @@ -16,6 +15,8 @@ struct intel_panic { struct iosys_map vmap; int page; + + struct xe_bo *bo; }; static void xe_panic_kunmap(struct intel_panic *panic) @@ -38,7 +39,7 @@ static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int { struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; struct intel_panic *panic = fb->panic; - struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); + struct xe_bo *bo = panic->bo; unsigned int new_page; unsigned int offset; @@ -93,6 +94,8 @@ static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer * return -ENODEV; panic->page = -1; + panic->bo = bo; + sb->set_pixel = xe_panic_page_set_pixel; return 0; } From 53f12a266c3244b4895f7a3619ca1dd2ad54cffc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 2 Jun 2026 13:09:09 +0300 Subject: [PATCH 130/186] drm/{i915, xe}/panic: drop dependency on struct intel_framebuffer Store tiling function pointer in struct intel_panic instead of struct intel_framebuffer, and store struct intel_panic pointer instead of struct intel_framebuffer pointer in struct drm_scanout_buffer private member. To make this happen, pass the tiling function pointer to panic setup hook, and initialize sb->private in the hook for clarity. This allows us to drop the dependency on struct intel_framebuffer from i915 and xe panic code. Note: It would be less verbose to have a typedef for the tiling function pointer. However, there isn't a nice location for it that wouldn't also increase header interdependencies. Cc: Jocelyn Falempe Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/d97abae79db3437c617cd4cb6193ba017b3a8d78.1780394867.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/intel_parent.c | 5 ++-- drivers/gpu/drm/i915/display/intel_parent.h | 3 ++- drivers/gpu/drm/i915/display/intel_plane.c | 8 +++--- drivers/gpu/drm/i915/gem/i915_gem_panic.c | 26 +++++++++++-------- drivers/gpu/drm/xe/display/xe_panic.c | 15 ++++++----- include/drm/intel/display_parent_interface.h | 3 ++- 7 files changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index ebd00922bf3c..b0ce1b71ca27 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -151,7 +151,6 @@ struct intel_framebuffer { unsigned int min_alignment; unsigned int vtd_guard; - unsigned int (*panic_tiling)(unsigned int x, unsigned int y, unsigned int width); struct intel_panic *panic; }; diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index 0b2bc2d38442..a5e41ea66921 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -252,9 +252,10 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display) } int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, - struct drm_scanout_buffer *sb, struct drm_gem_object *obj) + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - return display->parent->panic->setup(panic, sb, obj); + return display->parent->panic->setup(panic, sb, obj, tiling); } void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic) diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 4197d1b1af61..595d4148b8eb 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -106,7 +106,8 @@ void intel_parent_overlay_cleanup(struct intel_display *display); /* panic */ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display); int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, - struct drm_scanout_buffer *sb, struct drm_gem_object *obj); + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)); void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic); /* pc8 */ diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 0fc7325fa96b..667343bed5eb 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -1617,17 +1617,17 @@ static int intel_get_scanout_buffer(struct drm_plane *plane, if (fb == intel_fbdev_framebuffer(display->fbdev.fbdev)) { intel_fbdev_get_map(display, &sb->map[0]); } else { + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width) = NULL; int ret; /* Can't disable tiling if DPT is in use */ if (intel_fb_uses_dpt(&fb->base)) { if (fb->base.format->cpp[0] != 4) return -EOPNOTSUPP; - fb->panic_tiling = intel_get_tiling_func(fb->base.modifier); - if (!fb->panic_tiling) + tiling = intel_get_tiling_func(fb->base.modifier); + if (!tiling) return -EOPNOTSUPP; } - sb->private = fb; - ret = intel_parent_panic_setup(display, fb->panic, sb, obj); + ret = intel_parent_panic_setup(display, fb->panic, sb, obj, tiling); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c index 001ccfbf7ab7..91389d36f101 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_panic.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -5,7 +5,6 @@ #include #include -#include "display/intel_display_types.h" #include "i915_gem_object.h" #include "i915_gem_panic.h" @@ -13,6 +12,8 @@ struct intel_panic { struct page **pages; int page; void *vaddr; + + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); }; static void i915_panic_kunmap(struct intel_panic *panic) @@ -45,8 +46,8 @@ static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - unsigned int offset = fb->panic_tiling(sb->width, x, y); + struct intel_panic *panic = sb->private; + unsigned int offset = panic->tiling(sb->width, x, y); iosys_map_wr(&sb->map[0], offset, u32, color); } @@ -59,13 +60,12 @@ static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, u static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { + struct intel_panic *panic = sb->private; unsigned int new_page; unsigned int offset; - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); else offset = y * sb->pitch[0] + x * sb->format->cpp[0]; @@ -98,14 +98,15 @@ static struct intel_panic *i915_gem_object_alloc_panic(void) * pfn is not supported yet. */ static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj) + struct drm_gem_object *_obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - struct intel_framebuffer *fb = sb->private; - bool panic_tiling = fb->panic_tiling; enum i915_map_type has_type; struct drm_i915_gem_object *obj = to_intel_bo(_obj); void *ptr; + sb->private = panic; + ptr = page_unpack_bits(obj->mm.mapping, &has_type); if (ptr) { if (i915_gem_object_has_iomem(obj)) @@ -113,8 +114,10 @@ static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_sca else iosys_map_set_vaddr(&sb->map[0], ptr); - if (panic_tiling) + if (tiling) { + panic->tiling = tiling; sb->set_pixel = i915_gem_object_panic_map_set_pixel; + } return 0; } if (i915_gem_object_has_struct_page(obj)) { @@ -122,6 +125,7 @@ static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_sca if (!panic->pages) return -ENOMEM; panic->page = -1; + panic->tiling = tiling; sb->set_pixel = i915_gem_object_panic_page_set_pixel; return 0; } diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index 4b86760ec00a..12c6fb99015d 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -5,7 +5,6 @@ #include #include -#include "intel_display_types.h" #include "xe_bo.h" #include "xe_panic.h" #include "xe_res_cursor.h" @@ -17,6 +16,7 @@ struct intel_panic { int page; struct xe_bo *bo; + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); }; static void xe_panic_kunmap(struct intel_panic *panic) @@ -37,14 +37,13 @@ static void xe_panic_kunmap(struct intel_panic *panic) static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; + struct intel_panic *panic = sb->private; struct xe_bo *bo = panic->bo; unsigned int new_page; unsigned int offset; - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); else offset = y * sb->pitch[0] + x * sb->format->cpp[0]; @@ -86,7 +85,8 @@ static struct intel_panic *xe_panic_alloc(void) } static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *obj) + struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { struct xe_bo *bo = gem_to_xe_bo(obj); @@ -95,8 +95,11 @@ static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer * panic->page = -1; panic->bo = bo; + panic->tiling = tiling; + sb->private = panic; sb->set_pixel = xe_panic_page_set_pixel; + return 0; } diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index b0362e231d84..de395df9ca30 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -168,7 +168,8 @@ struct intel_display_overlay_interface { struct intel_display_panic_interface { struct intel_panic *(*alloc)(void); int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *obj); + struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)); void (*finish)(struct intel_panic *panic); }; From d392b88dce93ae1a88d932c8f6680be598d22cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 14:40:35 +0300 Subject: [PATCH 131/186] drm/i915/panel: Split VRR vs. fixed refresh rate fixed mode selection into separate stages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the VRR vs. fixed refresh rate fixed mode selection into two completely separate stages. First try the VRR method, which will only accept fixed modes that are in the VRR range and whose refresh rate is equal or higher to the user's requested mode's refresh rate. If the VRR method doesn't find anything we fall back to the fixed refresh rate method of simply looking for the fixed mode with the closest refresh rate to the user's request. The main benefit is that we will only perform the VRR vtotal adjustment on fixed modes that have equal or higher refresh rate to the user's requested mode, thus we will never end up in a situation where we'd have to shrink the fixed mode's vtotal. This avoids any risk of ending up with a vtotal that is too short. v2: Drop redundant intel_panel_fixed_mode() call (Ankit) Cc: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623114035.7185-1-ville.syrjala@linux.intel.com Tested-by: Vidya Srinivas Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_panel.c | 126 ++++++++++++++------- 1 file changed, 85 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 81fb349ece5f..2b8401b6d4d6 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -67,21 +67,43 @@ static bool is_best_fixed_mode(struct intel_connector *connector, if (!best_mode) return true; - /* - * With VRR always pick a mode with equal/higher than requested - * vrefresh, which we can then reduce to match the requested - * vrefresh by extending the vblank length. - */ - if (intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh) && - fixed_mode_vrefresh < vrefresh) - return false; - /* pick the fixed_mode that is closest in terms of vrefresh */ return abs(fixed_mode_vrefresh - vrefresh) < abs(drm_mode_vrefresh(best_mode) - vrefresh); } +static const struct drm_display_mode * +intel_panel_fixed_mode_vrr(struct intel_connector *connector, + const struct drm_display_mode *mode) +{ + const struct drm_display_mode *fixed_mode, *best_mode = NULL; + int vrefresh = drm_mode_vrefresh(mode); + + if (!intel_vrr_is_in_range(connector, vrefresh)) + return NULL; + + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { + int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + if (!intel_vrr_is_in_range(connector, fixed_mode_vrefresh)) + continue; + + /* + * With VRR always pick a mode with equal/higher than requested + * vrefresh, which we can then reduce to match the requested + * vrefresh by extending the vblank length. + */ + if (fixed_mode_vrefresh < vrefresh) + continue; + + if (is_best_fixed_mode(connector, vrefresh, + fixed_mode_vrefresh, best_mode)) + best_mode = fixed_mode; + } + + return best_mode; +} + const struct drm_display_mode * intel_panel_fixed_mode(struct intel_connector *connector, const struct drm_display_mode *mode) @@ -197,47 +219,22 @@ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector) return connector->panel.vbt.drrs_type; } -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode) +static int intel_panel_compute_config_vrr(struct intel_connector *connector, + struct drm_display_mode *adjusted_mode) { - const struct drm_display_mode *fixed_mode = - intel_panel_fixed_mode(connector, adjusted_mode); + const struct drm_display_mode *fixed_mode; int vrefresh, fixed_mode_vrefresh; - bool is_vrr; + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode); if (!fixed_mode) - return 0; + return -EINVAL; vrefresh = drm_mode_vrefresh(adjusted_mode); fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); - /* - * Assume that we shouldn't muck about with the - * timings if they don't land in the VRR range. - */ - is_vrr = intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh); - - if (!is_vrr) { - /* - * We don't want to lie too much to the user about the refresh - * rate they're going to get. But we have to allow a bit of latitude - * for Xorg since it likes to automagically cook up modes with slightly - * off refresh rates. - */ - if (abs(vrefresh - fixed_mode_vrefresh) > 1) { - drm_dbg_kms(connector->base.dev, - "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", - connector->base.base.id, connector->base.name, - vrefresh, fixed_mode_vrefresh); - - return -EINVAL; - } - } - drm_mode_copy(adjusted_mode, fixed_mode); - if (is_vrr && fixed_mode_vrefresh != vrefresh) { + if (fixed_mode_vrefresh != vrefresh) { int vsync_start_offset = adjusted_mode->vtotal - adjusted_mode->vsync_start; int vsync_end_offset = adjusted_mode->vtotal - adjusted_mode->vsync_end; @@ -254,6 +251,53 @@ int intel_panel_compute_config(struct intel_connector *connector, return 0; } +static int intel_panel_compute_config_fixed_rr(struct intel_connector *connector, + struct drm_display_mode *adjusted_mode) +{ + const struct drm_display_mode *fixed_mode; + int vrefresh, fixed_mode_vrefresh; + + fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode); + if (!fixed_mode) + return 0; + + vrefresh = drm_mode_vrefresh(adjusted_mode); + fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + /* + * We don't want to lie too much to the user about the refresh + * rate they're going to get. But we have to allow a bit of latitude + * for Xorg since it likes to automagically cook up modes with slightly + * off refresh rates. + */ + if (abs(vrefresh - fixed_mode_vrefresh) > 1) { + drm_dbg_kms(connector->base.dev, + "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", + connector->base.base.id, connector->base.name, + vrefresh, fixed_mode_vrefresh); + + return -EINVAL; + } + + drm_mode_copy(adjusted_mode, fixed_mode); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + return 0; +} + +int intel_panel_compute_config(struct intel_connector *connector, + struct drm_display_mode *adjusted_mode) +{ + int ret; + + ret = intel_panel_compute_config_vrr(connector, adjusted_mode); + if (ret) + ret = intel_panel_compute_config_fixed_rr(connector, adjusted_mode); + + return ret; +} + static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector) { struct intel_display *display = to_intel_display(connector); From 230149760cdbc89d450fc7c3aa270811a60ea6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 00:35:59 +0300 Subject: [PATCH 132/186] drm/modes: Add DRM_MODE_MATCH_TIMINGS_VRR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new mode matching flag DRM_MODE_MATCH_TIMINGS_VRR. This is identical to DRM_MODE_MATCH_TIMINGS, except it requires the vsync pulse to remain anchored to the end of vtotal, as opposed to the start of the frame. VRR capable hardware can therefore treat matching modes as just variants of the same mode with a different vblank lengths. Reviewed-by: Suraj Kandpal Acked-by: Maarten Lankhorst Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260622213602.7244-3-ville.syrjala@linux.intel.com Tested-by: Vidya Srinivas Acked-by: Jani Nikula --- drivers/gpu/drm/drm_modes.c | 23 +++++++++++++++++++++++ include/drm/drm_modes.h | 1 + 2 files changed, 24 insertions(+) diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 3f8e025fd6d9..e1eed13a8e94 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1469,6 +1469,25 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_duplicate); +static bool drm_mode_match_timings_vrr(const struct drm_display_mode *mode1, + const struct drm_display_mode *mode2) +{ + int mode1_vsync_start_offset = mode1->vtotal - mode1->vsync_start; + int mode1_vsync_end_offset = mode1->vtotal - mode1->vsync_end; + int mode2_vsync_start_offset = mode2->vtotal - mode2->vsync_start; + int mode2_vsync_end_offset = mode2->vtotal - mode2->vsync_end; + + return mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1_vsync_start_offset == mode2_vsync_start_offset && + mode1_vsync_end_offset == mode2_vsync_end_offset && + mode1->vscan == mode2->vscan; +} + static bool drm_mode_match_timings(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) { @@ -1538,6 +1557,10 @@ bool drm_mode_match(const struct drm_display_mode *mode1, if (!mode1 || !mode2) return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS_VRR && + !drm_mode_match_timings_vrr(mode1, mode2)) + return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS && !drm_mode_match_timings(mode1, mode2)) return false; diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index b9bb92e4b029..6e3eccc3c349 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -193,6 +193,7 @@ enum drm_mode_status { #define DRM_MODE_MATCH_FLAGS (1 << 2) #define DRM_MODE_MATCH_3D_FLAGS (1 << 3) #define DRM_MODE_MATCH_ASPECT_RATIO (1 << 4) +#define DRM_MODE_MATCH_TIMINGS_VRR (1 << 5) /** * struct drm_display_mode - DRM kernel-internal display mode structure From 3729f93c197e2cb923158ea8b6010b49c36b389f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 00:36:00 +0300 Subject: [PATCH 133/186] drm/i915: Pass the full atomic state to .compute_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upcoming changes will need access to the full atomic state in .compute_config(). Pass it in from the top. Couple of the implementations already dug this out via the crtc_state/conn_state->state pointer, but we don't want to use that anywhere because it's a bit of a footgun by only being valid during the early stages of the commit. Reviewed-by: Suraj Kandpal Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260622213602.7244-4-ville.syrjala@linux.intel.com Tested-by: Vidya Srinivas Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/g4x_dp.c | 5 +++-- drivers/gpu/drm/i915/display/g4x_hdmi.c | 4 ++-- drivers/gpu/drm/i915/display/icl_dsi.c | 3 ++- drivers/gpu/drm/i915/display/intel_crt.c | 9 ++++++--- drivers/gpu/drm/i915/display/intel_ddi.c | 8 +++++--- drivers/gpu/drm/i915/display/intel_display.c | 4 ++-- drivers/gpu/drm/i915/display/intel_display_types.h | 6 ++++-- drivers/gpu/drm/i915/display/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/display/intel_dp.h | 3 ++- drivers/gpu/drm/i915/display/intel_dp_mst.c | 8 ++++---- drivers/gpu/drm/i915/display/intel_dvo.c | 3 ++- drivers/gpu/drm/i915/display/intel_lvds.c | 3 ++- drivers/gpu/drm/i915/display/intel_sdvo.c | 3 ++- drivers/gpu/drm/i915/display/intel_tv.c | 5 ++--- drivers/gpu/drm/i915/display/vlv_dsi.c | 3 ++- 15 files changed, 42 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index d211e6c49e0a..b867443ff227 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -1222,7 +1222,8 @@ static bool ilk_digital_port_connected(struct intel_encoder *encoder) return intel_de_read(display, DEISR) & bit; } -static int g4x_dp_compute_config(struct intel_encoder *encoder, +static int g4x_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -1232,7 +1233,7 @@ static int g4x_dp_compute_config(struct intel_encoder *encoder, if (HAS_PCH_SPLIT(display) && encoder->port != PORT_A) crtc_state->has_pch_encoder = true; - ret = intel_dp_compute_config(encoder, crtc_state, conn_state); + ret = intel_dp_compute_config(state, encoder, crtc_state, conn_state); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index acb36cab999c..4c33aa1d1d32 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -126,12 +126,12 @@ static bool g4x_compute_has_hdmi_sink(struct intel_atomic_state *state, return false; } -static int g4x_hdmi_compute_config(struct intel_encoder *encoder, +static int g4x_hdmi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); if (HAS_PCH_SPLIT(display)) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index a549f1fac810..59184f2f805c 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1657,7 +1657,8 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder, return 0; } -static int gen11_dsi_compute_config(struct intel_encoder *encoder, +static int gen11_dsi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 243e332bef57..5b8968197fbc 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -397,7 +397,8 @@ intel_crt_mode_valid(struct drm_connector *connector, return MODE_OK; } -static int intel_crt_compute_config(struct intel_encoder *encoder, +static int intel_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -413,7 +414,8 @@ static int intel_crt_compute_config(struct intel_encoder *encoder, return 0; } -static int pch_crt_compute_config(struct intel_encoder *encoder, +static int pch_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -432,7 +434,8 @@ static int pch_crt_compute_config(struct intel_encoder *encoder, return 0; } -static int hsw_crt_compute_config(struct intel_encoder *encoder, +static int hsw_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 25314ec65ae7..2b7eb010511b 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4485,7 +4485,8 @@ intel_ddi_compute_output_type(struct intel_encoder *encoder, } } -static int intel_ddi_compute_config(struct intel_encoder *encoder, +static int intel_ddi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -4503,7 +4504,7 @@ static int intel_ddi_compute_config(struct intel_encoder *encoder, ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state); } else { - ret = intel_dp_compute_config(encoder, pipe_config, conn_state); + ret = intel_dp_compute_config(state, encoder, pipe_config, conn_state); } if (ret) @@ -4608,7 +4609,8 @@ intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state, return transcoders; } -static int intel_ddi_compute_config_late(struct intel_encoder *encoder, +static int intel_ddi_compute_config_late(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 805066b02aaa..5bc8e6ea10a5 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4781,7 +4781,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, if (connector_state->crtc != &crtc->base) continue; - ret = encoder->compute_config(encoder, crtc_state, + ret = encoder->compute_config(state, encoder, crtc_state, connector_state); if (ret == -EDEADLK) return ret; @@ -4841,7 +4841,7 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state, !encoder->compute_config_late) continue; - ret = encoder->compute_config_late(encoder, crtc_state, + ret = encoder->compute_config_late(state, encoder, crtc_state, conn_state); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b0ce1b71ca27..1e9da1e6fd3b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -177,10 +177,12 @@ struct intel_encoder { enum intel_output_type (*compute_output_type)(struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); - int (*compute_config)(struct intel_encoder *, + int (*compute_config)(struct intel_atomic_state *, + struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); - int (*compute_config_late)(struct intel_encoder *, + int (*compute_config_late)(struct intel_atomic_state *, + struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); void (*pre_pll_enable)(struct intel_atomic_state *, diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 3569e61e7fee..b9324b590ee9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3627,12 +3627,12 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state, } int -intel_dp_compute_config(struct intel_encoder *encoder, +intel_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_connector *connector = intel_dp->attached_connector; diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 92ce04852326..b233739b89ce 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -71,7 +71,8 @@ void intel_dp_sink_disable_decompression(struct intel_atomic_state *state, void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder); void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder); void intel_dp_encoder_flush_work(struct drm_encoder *encoder); -int intel_dp_compute_config(struct intel_encoder *encoder, +int intel_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state); bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index bcdc50491347..a2f9440ab84a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -697,12 +697,12 @@ static int mst_stream_compute_link_for_joined_pipes(struct intel_encoder *encode return 0; } -static int mst_stream_compute_config(struct intel_encoder *encoder, +static int mst_stream_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct intel_dp *intel_dp = to_primary_dp(encoder); struct intel_connector *connector = @@ -921,11 +921,11 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state, return 0; } -static int mst_stream_compute_config_late(struct intel_encoder *encoder, +static int mst_stream_compute_config_late(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct intel_dp *intel_dp = to_primary_dp(encoder); /* lowest numbered transcoder will be designated master */ diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c index dd1a995c2979..181722c41b96 100644 --- a/drivers/gpu/drm/i915/display/intel_dvo.c +++ b/drivers/gpu/drm/i915/display/intel_dvo.c @@ -242,7 +242,8 @@ intel_dvo_mode_valid(struct drm_connector *_connector, return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode); } -static int intel_dvo_compute_config(struct intel_encoder *encoder, +static int intel_dvo_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c index c8098104d853..30e4809b36ac 100644 --- a/drivers/gpu/drm/i915/display/intel_lvds.c +++ b/drivers/gpu/drm/i915/display/intel_lvds.c @@ -413,7 +413,8 @@ intel_lvds_mode_valid(struct drm_connector *_connector, return MODE_OK; } -static int intel_lvds_compute_config(struct intel_encoder *encoder, +static int intel_lvds_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index d83d350959d8..6b73c9a5ec7f 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -1354,7 +1354,8 @@ static bool intel_sdvo_has_audio(struct intel_encoder *encoder, return intel_conn_state->force_audio == HDMI_AUDIO_ON; } -static int intel_sdvo_compute_config(struct intel_encoder *encoder, +static int intel_sdvo_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index 0a926c6f25f4..840e1dcdc2d0 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -1187,13 +1187,12 @@ static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode, } static int -intel_tv_compute_config(struct intel_encoder *encoder, +intel_tv_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = - to_intel_atomic_state(pipe_config->uapi.state); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct intel_tv_connector_state *tv_conn_state = to_intel_tv_connector_state(conn_state); diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index 877eab75f19a..b89318f5bdc2 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -266,7 +266,8 @@ static void band_gap_reset(struct intel_display *display) vlv_flisdsi_put(display); } -static int intel_dsi_compute_config(struct intel_encoder *encoder, +static int intel_dsi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { From 78e6cdf518f5d83b9619d42d03a7e82122dc7049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 14:41:29 +0300 Subject: [PATCH 134/186] drm/i915/panel: Adjust intel_panel_compute_config() calling convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the full atomic state to intel_panel_compute_config(). We'll need this for some upcoming VRR fastset tricks. And to accompany full state we'll also need the crtc (or its state) as well. v2: Rebase Reviewed-by: Suraj Kandpal Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623114130.7333-1-ville.syrjala@linux.intel.com Tested-by: Vidya Srinivas Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 2 +- drivers/gpu/drm/i915/display/intel_dp.c | 2 +- drivers/gpu/drm/i915/display/intel_dvo.c | 2 +- drivers/gpu/drm/i915/display/intel_lvds.c | 2 +- drivers/gpu/drm/i915/display/intel_panel.c | 21 +++++++++++++-------- drivers/gpu/drm/i915/display/intel_panel.h | 6 ++++-- drivers/gpu/drm/i915/display/intel_sdvo.c | 4 ++-- drivers/gpu/drm/i915/display/vlv_dsi.c | 2 +- 8 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index 59184f2f805c..ea0cdb7822f3 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1672,7 +1672,7 @@ static int gen11_dsi_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_panel_compute_config(intel_connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, intel_connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index b9324b590ee9..da8a94821c11 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3639,7 +3639,7 @@ intel_dp_compute_config(struct intel_atomic_state *state, int ret = 0, link_bpp_x16; if (intel_dp_is_edp(intel_dp)) { - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, connector); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c index 181722c41b96..f157699a7c4c 100644 --- a/drivers/gpu/drm/i915/display/intel_dvo.c +++ b/drivers/gpu/drm/i915/display/intel_dvo.c @@ -257,7 +257,7 @@ static int intel_dvo_compute_config(struct intel_atomic_state *state, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c index 30e4809b36ac..872753478cf2 100644 --- a/drivers/gpu/drm/i915/display/intel_lvds.c +++ b/drivers/gpu/drm/i915/display/intel_lvds.c @@ -460,7 +460,7 @@ static int intel_lvds_compute_config(struct intel_atomic_state *state, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, crtc_state, connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 2b8401b6d4d6..faa24537ef63 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -219,9 +219,11 @@ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector) return connector->panel.vbt.drrs_type; } -static int intel_panel_compute_config_vrr(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode) +static int intel_panel_compute_config_vrr(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) { + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; const struct drm_display_mode *fixed_mode; int vrefresh, fixed_mode_vrefresh; @@ -251,9 +253,11 @@ static int intel_panel_compute_config_vrr(struct intel_connector *connector, return 0; } -static int intel_panel_compute_config_fixed_rr(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode) +static int intel_panel_compute_config_fixed_rr(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) { + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; const struct drm_display_mode *fixed_mode; int vrefresh, fixed_mode_vrefresh; @@ -286,14 +290,15 @@ static int intel_panel_compute_config_fixed_rr(struct intel_connector *connector return 0; } -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode) +int intel_panel_compute_config(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) { int ret; - ret = intel_panel_compute_config_vrr(connector, adjusted_mode); + ret = intel_panel_compute_config_vrr(state, crtc_state, connector); if (ret) - ret = intel_panel_compute_config_fixed_rr(connector, adjusted_mode); + ret = intel_panel_compute_config_fixed_rr(state, crtc_state, connector); return ret; } diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h index 23bd227826c9..30c6078ecb1b 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.h +++ b/drivers/gpu/drm/i915/display/intel_panel.h @@ -14,6 +14,7 @@ struct drm_connector; struct drm_connector_state; struct drm_display_mode; struct drm_edid; +struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; struct intel_display; @@ -45,8 +46,9 @@ enum drm_mode_status intel_panel_mode_valid(struct intel_connector *connector, const struct drm_display_mode *mode, int *target_clock); -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode); +int intel_panel_compute_config(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector); void intel_panel_add_edid_fixed_modes(struct intel_connector *connector, bool use_alt_fixed_modes); void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector); diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index 6b73c9a5ec7f..3075ef04df56 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -1399,8 +1399,8 @@ static int intel_sdvo_compute_config(struct intel_atomic_state *state, const struct drm_display_mode *fixed_mode; int ret; - ret = intel_panel_compute_config(&intel_sdvo_connector->base, - adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, + &intel_sdvo_connector->base); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index b89318f5bdc2..8829f365592e 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -281,7 +281,7 @@ static int intel_dsi_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_panel_compute_config(intel_connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, intel_connector); if (ret) return ret; From 4a2bdf0c87c7ae8f825181032263820a1d3beee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 00:36:02 +0300 Subject: [PATCH 135/186] drm/i915/panel: Attempt VRR based refresh rate change for !allow_modeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adjust the panel fixed mode selection algorithm to only consider fixed modes that are "VRR compatible" with the old fixed mode when userspace doesn't want to allow full modesets. This will allow a VRR based refresh rate changes (ie. just a change in the vblank length) via the fastset path. When full modesets are allowed, we still use the original algorithm as that may pick a fixed mode with a more optimal dotclock, potentially leading to reduced power consumption. This approach works as long as userspace does the initial allow_modeset=true commit using the highest refresh rate it will want to use. Subsequent commits with allow_modeset=false can then switch between lower refresh rates without blinks. One remaining hurdle we may need to solve is the guardband length. Assuming the highest refresh rate vblank is too short for intel_vrr_compute_optimized_guardband() the intitial guardband will match the highest refresh rate vblank. A subsequent switch to a lower refresh rate will then recompute the guardband and select a value that is higher (since the vblank will be longer). The mismatch in guardband lengths will prevent the fastset. We may either have to preserve the original (sub-optimal) guardband, or we'll have to revisit the idea of changing the guardband without a full modeset. Note that I'm not 100% happy with this solution because intel_panel_fixed_mode() is no longer fully idempotent, but I wasn't able to come up with anything truly better either :/ The simple solution would be just to always pick the fixed mode with the highest dotclock, but that could lead to increased power consumption even when high refresh rates are never used. Perhaps the proper solution would be to just deprecate this idea of taking in random modes for internal panels and then cooking up a compatible fixed modes. Life would be easier if userspace was required to provide the desired fixed mode directly. But in order to do that we'd need to introduce new uapi properties to control the pfit aspect of this, and we'd probably need a new client cap to select between the old and new userspace behaviour. Something to consider in the future... v2: Rebase due to earlier changes to VRR fixed mode selection Reviewed-by: Ankit Nautiyal #v1 Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260622213602.7244-6-ville.syrjala@linux.intel.com Reviewed-by: Ankit Nautiyal Tested-by: Vidya Srinivas Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_panel.c | 42 ++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index faa24537ef63..81e638d0c7b3 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -72,9 +72,20 @@ static bool is_best_fixed_mode(struct intel_connector *connector, abs(drm_mode_vrefresh(best_mode) - vrefresh); } +static bool is_vrr_compatible(const struct drm_display_mode *mode1, + const struct drm_display_mode *mode2) +{ + return drm_mode_match(mode1, mode2, + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_TIMINGS_VRR | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS); +} + static const struct drm_display_mode * intel_panel_fixed_mode_vrr(struct intel_connector *connector, - const struct drm_display_mode *mode) + const struct drm_display_mode *mode, + const struct drm_display_mode *vrr_ref_mode) { const struct drm_display_mode *fixed_mode, *best_mode = NULL; int vrefresh = drm_mode_vrefresh(mode); @@ -82,6 +93,10 @@ intel_panel_fixed_mode_vrr(struct intel_connector *connector, if (!intel_vrr_is_in_range(connector, vrefresh)) return NULL; + if (vrr_ref_mode && + !intel_vrr_is_in_range(connector, drm_mode_vrefresh(vrr_ref_mode))) + return NULL; + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); @@ -96,6 +111,10 @@ intel_panel_fixed_mode_vrr(struct intel_connector *connector, if (fixed_mode_vrefresh < vrefresh) continue; + if (vrr_ref_mode && + !is_vrr_compatible(fixed_mode, vrr_ref_mode)) + continue; + if (is_best_fixed_mode(connector, vrefresh, fixed_mode_vrefresh, best_mode)) best_mode = fixed_mode; @@ -224,10 +243,27 @@ static int intel_panel_compute_config_vrr(struct intel_atomic_state *state, struct intel_connector *connector) { struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - const struct drm_display_mode *fixed_mode; + const struct drm_display_mode *fixed_mode = NULL; int vrefresh, fixed_mode_vrefresh; - fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode); + /* + * Attempt a VRR based refresh rate change if possible + * when userspace has forbidden a full modeset. + */ + if (!state->base.allow_modeset) { + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + + if (old_crtc_state->hw.enable && + old_crtc_state->uapi.encoder_mask == crtc_state->uapi.encoder_mask) + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, + &old_crtc_state->hw.adjusted_mode); + } + + if (!fixed_mode) + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, NULL); + if (!fixed_mode) return -EINVAL; From e6bc88aa38714c234bec2609e62f6084142c9a02 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:21 +0300 Subject: [PATCH 136/186] drm/i915/dp: Rename intel_dp_link_config to intel_dp_link_config_entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename intel_dp_link_config to intel_dp_link_config_entry to prepare for tracking a link configuration in both an internal packed and a public unpacked format. A follow-up change will add struct intel_dp_link_config representing the public unpacked format. Reviewed-by: Mika Kahola Reviewed-by: Michał Grzelak Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-2-imre.deak@intel.com --- .../gpu/drm/i915/display/intel_display_types.h | 2 +- drivers/gpu/drm/i915/display/intel_dp.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 1e9da1e6fd3b..95b047240021 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1842,7 +1842,7 @@ struct intel_dp { #define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS) #define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) - struct intel_dp_link_config { + struct intel_dp_link_config_entry { u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; } configs[INTEL_DP_MAX_LINK_CONFIGS]; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index da8a94821c11..1cbe2ece9304 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -699,18 +699,18 @@ int intel_dp_rate_index(const int *rates, int len, int rate) } static int intel_dp_link_config_rate(struct intel_dp *intel_dp, - const struct intel_dp_link_config *lc) + const struct intel_dp_link_config_entry *lc) { return intel_dp_common_rate(intel_dp, lc->link_rate_idx); } -static int intel_dp_link_config_lane_count(const struct intel_dp_link_config *lc) +static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lc) { return 1 << lc->lane_count_exp; } static int intel_dp_link_config_bw(struct intel_dp *intel_dp, - const struct intel_dp_link_config *lc) + const struct intel_dp_link_config_entry *lc) { return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc), intel_dp_link_config_lane_count(lc)); @@ -719,8 +719,8 @@ static int intel_dp_link_config_bw(struct intel_dp *intel_dp, static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) { struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ - const struct intel_dp_link_config *lc_a = a; - const struct intel_dp_link_config *lc_b = b; + const struct intel_dp_link_config_entry *lc_a = a; + const struct intel_dp_link_config_entry *lc_b = b; int bw_a = intel_dp_link_config_bw(intel_dp, lc_a); int bw_b = intel_dp_link_config_bw(intel_dp, lc_b); @@ -734,7 +734,7 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) static void intel_dp_link_config_init(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - struct intel_dp_link_config *lc; + struct intel_dp_link_config_entry *lc; int num_common_lane_configs; int i; int j; @@ -769,7 +769,7 @@ static void intel_dp_link_config_init(struct intel_dp *intel_dp) void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) { struct intel_display *display = to_intel_display(intel_dp); - const struct intel_dp_link_config *lc; + const struct intel_dp_link_config_entry *lc; if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs)) idx = 0; @@ -788,7 +788,7 @@ int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lan int i; for (i = 0; i < intel_dp->link.num_configs; i++) { - const struct intel_dp_link_config *lc = &intel_dp->link.configs[i]; + const struct intel_dp_link_config_entry *lc = &intel_dp->link.configs[i]; if (lc->lane_count_exp == lane_count_exp && lc->link_rate_idx == link_rate_idx) From f5fed6af1c7e2e7008a3952f1cf8bffa4de5c3f0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:22 +0300 Subject: [PATCH 137/186] drm/i915/dp: Add struct intel_dp_link_config Add a struct representing the public unpacked format of a link configuration. This will be used by the DP link capability API added as a follow-up, and by DP code in general that needs to track a link configuration. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-3-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display_types.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 95b047240021..0a9bf084dbbb 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1802,6 +1802,14 @@ struct intel_psr { struct ref_tracker *vblank_wakeref; }; +struct intel_dp_link_config { + int rate; + int lane_count; +}; + +#define INTEL_DP_LINK_CONFIG_NULL \ + ((struct intel_dp_link_config){}) + struct intel_dp { intel_reg_t output_reg; u32 DP; From ebf5ad5cbc8013a4daf28029583cc0abeb1ad75e Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:23 +0300 Subject: [PATCH 138/186] drm/i915/dp_link_caps: Introduce DP link capability module Start isolating the DP link capability logic from the generic DP code by adding a separate intel_dp_link_caps module and a corresponding state object. Allocate the state so it can remain opaque within its module. Follow-up changes will move link capability helpers and state from intel_dp.c and intel_dp_link_training.c to the new module and state. v2: Remove unnecessary function documentation. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-4-imre.deak@intel.com --- drivers/gpu/drm/i915/Makefile | 1 + .../drm/i915/display/intel_display_types.h | 2 ++ drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++ .../gpu/drm/i915/display/intel_dp_link_caps.c | 30 +++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 12 ++++++++ drivers/gpu/drm/xe/Makefile | 1 + 6 files changed, 55 insertions(+) create mode 100644 drivers/gpu/drm/i915/display/intel_dp_link_caps.c create mode 100644 drivers/gpu/drm/i915/display/intel_dp_link_caps.h diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 1fd7a1a5f315..c4de717505d7 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -356,6 +356,7 @@ i915-y += \ display/intel_dp_aux.o \ display/intel_dp_aux_backlight.o \ display/intel_dp_hdcp.o \ + display/intel_dp_link_caps.o \ display/intel_dp_link_training.o \ display/intel_dp_mst.o \ display/intel_dp_test.o \ diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 0a9bf084dbbb..a232ed210389 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -58,6 +58,7 @@ struct cec_notifier; struct drm_printer; struct intel_connector; struct intel_ddi_buf_trans; +struct intel_dp_link_caps; struct intel_dp_link_training; struct intel_fbc; struct intel_global_objs_state; @@ -1869,6 +1870,7 @@ struct intel_dp { int force_lane_count; int force_rate; struct intel_dp_link_training *training; + struct intel_dp_link_caps *caps; } link; bool reset_link_params; int mso_link_count; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 1cbe2ece9304..47bebffae842 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -71,6 +71,7 @@ #include "intel_dp.h" #include "intel_dp_aux.h" #include "intel_dp_hdcp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -7458,10 +7459,18 @@ int intel_dp_link_init(struct intel_dp *intel_dp) if (!intel_dp->link.training) return -ENOMEM; + intel_dp->link.caps = intel_dp_link_caps_init(intel_dp); + if (!intel_dp->link.caps) { + intel_dp_link_training_cleanup(intel_dp->link.training); + + return -ENOMEM; + } + return 0; } void intel_dp_link_cleanup(struct intel_dp *intel_dp) { + intel_dp_link_caps_cleanup(intel_dp->link.caps); intel_dp_link_training_cleanup(intel_dp->link.training); } diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c new file mode 100644 index 000000000000..63989d97effd --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include + +#include "intel_dp_link_caps.h" + +struct intel_dp_link_caps { + struct intel_dp *dp; +}; + +struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) +{ + struct intel_dp_link_caps *link_caps; + + link_caps = kzalloc_obj(*link_caps); + if (!link_caps) + return NULL; + + link_caps->dp = intel_dp; + + return link_caps; +} + +void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps) +{ + kfree(link_caps); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h new file mode 100644 index 000000000000..050b279463d6 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __INTEL_DP_LINK_CAPS_H__ +#define __INTEL_DP_LINK_CAPS_H__ + +struct intel_dp; + +struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); +void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); + +#endif /* __INTEL_DP_LINK_CAPS_H__ */ diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 09661f079d03..bd153e82d66d 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -278,6 +278,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_dp_aux.o \ i915-display/intel_dp_aux_backlight.o \ i915-display/intel_dp_hdcp.o \ + i915-display/intel_dp_link_caps.o \ i915-display/intel_dp_link_training.o \ i915-display/intel_dp_mst.o \ i915-display/intel_dp_test.o \ From 2333194316b20af26428dd52dd48df0d0974f6ff Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:24 +0300 Subject: [PATCH 139/186] drm/i915/dp_link_caps: Move common rate helpers to link caps Move the helpers handling common link rates to intel_dp_link_caps.c. Their functionality is part of the link capability logic and will be updated to use the link capability state in follow-up changes. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-5-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 27 +---------------- drivers/gpu/drm/i915/display/intel_dp.h | 3 +- .../gpu/drm/i915/display/intel_dp_link_caps.c | 30 +++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 5 ++++ .../drm/i915/display/intel_dp_link_training.c | 1 + .../gpu/drm/i915/display/intel_dp_tunnel.c | 1 + 6 files changed, 39 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 47bebffae842..161a00493989 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -314,7 +314,7 @@ static void intel_dp_set_max_sink_lane_count(struct intel_dp *intel_dp) } /* Get length of rates array potentially limited by max_rate. */ -static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) +int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) { int i; @@ -327,31 +327,6 @@ static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) return 0; } -/* Get length of common rates array potentially limited by max_rate. */ -static int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, - int max_rate) -{ - return intel_dp_rate_limit_len(intel_dp->common_rates, - intel_dp->num_common_rates, max_rate); -} - -int intel_dp_common_rate(struct intel_dp *intel_dp, int index) -{ - struct intel_display *display = to_intel_display(intel_dp); - - if (drm_WARN_ON(display->drm, - index < 0 || index >= intel_dp->num_common_rates)) - return 162000; - - return intel_dp->common_rates[index]; -} - -/* Theoretical max between source and sink */ -int intel_dp_max_common_rate(struct intel_dp *intel_dp) -{ - return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); -} - int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) { int vbt_max_lanes = intel_bios_dp_max_lane_count(dig_port->base.devdata); diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index b233739b89ce..32395900c47a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -101,14 +101,13 @@ void intel_edp_backlight_off(const struct drm_connector_state *conn_state); void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp); void intel_dp_mst_suspend(struct intel_display *display); void intel_dp_mst_resume(struct intel_display *display); +int intel_dp_rate_limit_len(const int *rates, int len, int max_rate); int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port); int intel_dp_max_link_rate(struct intel_dp *intel_dp); int intel_dp_max_lane_count(struct intel_dp *intel_dp); int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); -int intel_dp_max_common_rate(struct intel_dp *intel_dp); int intel_dp_max_common_lane_count(struct intel_dp *intel_dp); -int intel_dp_common_rate(struct intel_dp *intel_dp, int index); int intel_dp_rate_index(const int *rates, int len, int rate); int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 63989d97effd..37ffd714c6a4 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -5,12 +5,42 @@ #include +#include + +#include "intel_display_core.h" +#include "intel_display_types.h" +#include "intel_dp.h" #include "intel_dp_link_caps.h" struct intel_dp_link_caps { struct intel_dp *dp; }; +/* Get length of common rates array potentially limited by max_rate. */ +int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, + int max_rate) +{ + return intel_dp_rate_limit_len(intel_dp->common_rates, + intel_dp->num_common_rates, max_rate); +} + +int intel_dp_common_rate(struct intel_dp *intel_dp, int index) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (drm_WARN_ON(display->drm, + index < 0 || index >= intel_dp->num_common_rates)) + return 162000; + + return intel_dp->common_rates[index]; +} + +/* Theoretical max between source and sink */ +int intel_dp_max_common_rate(struct intel_dp *intel_dp) +{ + return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); +} + struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 050b279463d6..3248777d1287 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -6,6 +6,11 @@ struct intel_dp; +int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, + int max_rate); +int intel_dp_common_rate(struct intel_dp *intel_dp, int index); +int intel_dp_max_common_rate(struct intel_dp *intel_dp); + struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 97cb407d084c..b915cfdeabd0 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -33,6 +33,7 @@ #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_encoder.h" diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index d6bd1f7e01e1..c82adfcce01d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -11,6 +11,7 @@ #include "intel_display_limits.h" #include "intel_display_types.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_tunnel.h" From 690e3194fa0c44ad92661acf44d8d3941bcd26b2 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:25 +0300 Subject: [PATCH 140/186] drm/i915/dp_link_caps: Move forced link param helpers to link caps Move the helpers handling forced link parameters to intel_dp_link_caps.c. Their functionality is part of the link capability logic and will be updated to use the link capability state in follow-up changes. Return the forced link rate and lane count through a struct intel_dp_link_config, which is the canonical way the rest of the link capability API will also accept and return link configurations. Reviewed-by: Mika Kahola Reviewed-by: Luca Coelho Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-6-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 39 ++++++++++--------- .../gpu/drm/i915/display/intel_dp_link_caps.c | 22 +++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 5 +++ 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 161a00493989..beed145604fb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -364,17 +364,16 @@ int intel_dp_max_common_lane_count(struct intel_dp *intel_dp) return intel_dp->max_common_lane_count; } -static int forced_lane_count(struct intel_dp *intel_dp) -{ - return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp)); -} - int intel_dp_max_lane_count(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int lane_count; + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + if (intel_dp->link.force_lane_count) - lane_count = forced_lane_count(intel_dp); + lane_count = forced_params.lane_count; else lane_count = intel_dp->link.max_lane_count; @@ -391,8 +390,12 @@ int intel_dp_max_lane_count(struct intel_dp *intel_dp) static int intel_dp_min_lane_count(struct intel_dp *intel_dp) { + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); + if (intel_dp->link.force_lane_count) - return forced_lane_count(intel_dp); + return forced_params.lane_count; return 1; } @@ -1655,23 +1658,17 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); } -static int forced_link_rate(struct intel_dp *intel_dp) -{ - int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate); - - if (len == 0) - return intel_dp_common_rate(intel_dp, 0); - - return intel_dp_common_rate(intel_dp, len - 1); -} - int intel_dp_max_link_rate(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int len; + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + if (intel_dp->link.force_rate) - return forced_link_rate(intel_dp); + return forced_params.rate; len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate); @@ -1681,8 +1678,12 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp) static int intel_dp_min_link_rate(struct intel_dp *intel_dp) { + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); + if (intel_dp->link.force_rate) - return forced_link_rate(intel_dp); + return forced_params.rate; return intel_dp_common_rate(intel_dp, 0); } diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 37ffd714c6a4..1d3a3ff007a0 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -41,6 +41,28 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp) return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); } +static int forced_lane_count(struct intel_dp *intel_dp) +{ + return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp)); +} + +static int forced_link_rate(struct intel_dp *intel_dp) +{ + int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate); + + if (len == 0) + return intel_dp_common_rate(intel_dp, 0); + + return intel_dp_common_rate(intel_dp, len - 1); +} + +void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *forced_params) +{ + forced_params->rate = forced_link_rate(link_caps->dp); + forced_params->lane_count = forced_lane_count(link_caps->dp); +} + struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 3248777d1287..61dbce86ee3d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -5,12 +5,17 @@ #define __INTEL_DP_LINK_CAPS_H__ struct intel_dp; +struct intel_dp_link_caps; +struct intel_dp_link_config; int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, int max_rate); int intel_dp_common_rate(struct intel_dp *intel_dp, int index); int intel_dp_max_common_rate(struct intel_dp *intel_dp); +void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *forced_params); + struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); From 7f5f0e8abdbc1102ded46b704671446cb390c93c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:26 +0300 Subject: [PATCH 141/186] drm/i915/dp: Simplify querying of forced link parameters Simplify querying the forced link rate and lane count by performing the zero checks inside the helpers, allowing callers to use the returned values directly. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-7-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 8 ++++---- drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 9 ++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index beed145604fb..a43d09ca7461 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -372,7 +372,7 @@ int intel_dp_max_lane_count(struct intel_dp *intel_dp) intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - if (intel_dp->link.force_lane_count) + if (forced_params.lane_count) lane_count = forced_params.lane_count; else lane_count = intel_dp->link.max_lane_count; @@ -394,7 +394,7 @@ static int intel_dp_min_lane_count(struct intel_dp *intel_dp) intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - if (intel_dp->link.force_lane_count) + if (forced_params.lane_count) return forced_params.lane_count; return 1; @@ -1667,7 +1667,7 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp) intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - if (intel_dp->link.force_rate) + if (forced_params.rate) return forced_params.rate; len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate); @@ -1682,7 +1682,7 @@ intel_dp_min_link_rate(struct intel_dp *intel_dp) intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - if (intel_dp->link.force_rate) + if (forced_params.rate) return forced_params.rate; return intel_dp_common_rate(intel_dp, 0); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 1d3a3ff007a0..e39e6c99ec25 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -43,13 +43,20 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp) static int forced_lane_count(struct intel_dp *intel_dp) { + if (!intel_dp->link.force_lane_count) + return 0; + return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp)); } static int forced_link_rate(struct intel_dp *intel_dp) { - int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate); + int len; + if (!intel_dp->link.force_rate) + return 0; + + len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate); if (len == 0) return intel_dp_common_rate(intel_dp, 0); From fdd4b01b1c914785b01639cb62aa818148262156 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:27 +0300 Subject: [PATCH 142/186] drm/i915/dp_link_caps: Move forced and max link debugfs entries to link caps Move the debugfs entries for the forced and max DP link parameters to intel_dp_link_caps. Their functionality is part of the link capability logic and will be updated to use the link capability state in follow-up changes. Reviewed-by: Luca Coelho Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-8-imre.deak@intel.com --- .../drm/i915/display/intel_display_debugfs.c | 2 + .../gpu/drm/i915/display/intel_dp_link_caps.c | 280 ++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 3 + .../drm/i915/display/intel_dp_link_training.c | 263 ---------------- 4 files changed, 285 insertions(+), 263 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 08004c1ba03f..3f02868ef105 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -32,6 +32,7 @@ #include "intel_display_types.h" #include "intel_dmc.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -1342,6 +1343,7 @@ void intel_connector_debugfs_add(struct intel_connector *connector) intel_psr_connector_debugfs_add(connector); intel_alpm_lobf_debugfs_add(connector); intel_dp_link_training_debugfs_add(connector); + intel_dp_link_caps_debugfs_add(connector); intel_link_bw_connector_debugfs_add(connector); if (DISPLAY_VER(display) >= 11 && diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index e39e6c99ec25..ea90e84500a8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -3,6 +3,7 @@ * Copyright © 2026 Intel Corporation */ +#include #include #include @@ -70,6 +71,285 @@ void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, forced_params->lane_count = forced_lane_count(link_caps->dp); } +static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + int current_rate = -1; + int force_rate; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + if (intel_dp->link.active) + current_rate = intel_dp->link_rate; + + force_rate = intel_dp->link.force_rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_printf(m, "%sauto%s", + force_rate == 0 ? "[" : "", + force_rate == 0 ? "]" : ""); + + for (i = 0; i < intel_dp->num_source_rates; i++) + seq_printf(m, " %s%d%s%s", + intel_dp->source_rates[i] == force_rate ? "[" : "", + intel_dp->source_rates[i], + intel_dp->source_rates[i] == current_rate ? "*" : "", + intel_dp->source_rates[i] == force_rate ? "]" : ""); + + seq_putc(m, '\n'); + + return 0; +} + +static int parse_link_rate(struct intel_dp *intel_dp, const char __user *ubuf, size_t len) +{ + char *kbuf; + const char *p; + int rate; + int ret = 0; + + kbuf = memdup_user_nul(ubuf, len); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + p = strim(kbuf); + + if (!strcmp(p, "auto")) { + rate = 0; + } else { + ret = kstrtoint(p, 0, &rate); + if (ret < 0) + goto out_free; + + if (intel_dp_rate_index(intel_dp->source_rates, + intel_dp->num_source_rates, + rate) < 0) + ret = -EINVAL; + } + +out_free: + kfree(kbuf); + + return ret < 0 ? ret : rate; +} + +static ssize_t i915_dp_force_link_rate_write(struct file *file, + const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + int rate; + int err; + + rate = parse_link_rate(intel_dp, ubuf, len); + if (rate < 0) + return rate; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_reset_link_params(intel_dp); + intel_dp->link.force_rate = rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + *offp += len; + + return len; +} +DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate); + +static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + int current_lane_count = -1; + int force_lane_count; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + if (intel_dp->link.active) + current_lane_count = intel_dp->lane_count; + force_lane_count = intel_dp->link.force_lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_printf(m, "%sauto%s", + force_lane_count == 0 ? "[" : "", + force_lane_count == 0 ? "]" : ""); + + for (i = 1; i <= 4; i <<= 1) + seq_printf(m, " %s%d%s%s", + i == force_lane_count ? "[" : "", + i, + i == current_lane_count ? "*" : "", + i == force_lane_count ? "]" : ""); + + seq_putc(m, '\n'); + + return 0; +} + +static int parse_lane_count(const char __user *ubuf, size_t len) +{ + char *kbuf; + const char *p; + int lane_count; + int ret = 0; + + kbuf = memdup_user_nul(ubuf, len); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + p = strim(kbuf); + + if (!strcmp(p, "auto")) { + lane_count = 0; + } else { + ret = kstrtoint(p, 0, &lane_count); + if (ret < 0) + goto out_free; + + switch (lane_count) { + case 1: + case 2: + case 4: + break; + default: + ret = -EINVAL; + } + } + +out_free: + kfree(kbuf); + + return ret < 0 ? ret : lane_count; +} + +static ssize_t i915_dp_force_lane_count_write(struct file *file, + const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + int lane_count; + int err; + + lane_count = parse_lane_count(ubuf, len); + if (lane_count < 0) + return lane_count; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_reset_link_params(intel_dp); + intel_dp->link.force_lane_count = lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + *offp += len; + + return len; +} +DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count); + +static int i915_dp_max_link_rate_show(void *data, u64 *val) +{ + struct intel_connector *connector = to_intel_connector(data); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + int err; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + *val = intel_dp->link.max_rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n"); + +static int i915_dp_max_lane_count_show(void *data, u64 *val) +{ + struct intel_connector *connector = to_intel_connector(data); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + int err; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + *val = intel_dp->link.max_lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); + + +/** + * intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector + * @connector: connector to add the debugfs files for + * + * Add the link-capability debugfs files for a DP @connector. + */ +void intel_dp_link_caps_debugfs_add(struct intel_connector *connector) +{ + struct dentry *root = connector->base.debugfs_entry; + + if (connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort && + connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) + return; + + debugfs_create_file("i915_dp_force_link_rate", 0644, root, + connector, &i915_dp_force_link_rate_fops); + + debugfs_create_file("i915_dp_force_lane_count", 0644, root, + connector, &i915_dp_force_lane_count_fops); + + debugfs_create_file("i915_dp_max_link_rate", 0444, root, + connector, &i915_dp_max_link_rate_fops); + + debugfs_create_file("i915_dp_max_lane_count", 0444, root, + connector, &i915_dp_max_lane_count_fops); +} + struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 61dbce86ee3d..c6a84891db46 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -4,6 +4,7 @@ #ifndef __INTEL_DP_LINK_CAPS_H__ #define __INTEL_DP_LINK_CAPS_H__ +struct intel_connector; struct intel_dp; struct intel_dp_link_caps; struct intel_dp_link_config; @@ -16,6 +17,8 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp); void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params); +void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); + struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index b915cfdeabd0..cbef3d45baf9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -2660,257 +2660,6 @@ void intel_dp_check_link_state(struct intel_dp *intel_dp) intel_encoder_link_check_queue_work(encoder, 0); } -static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) -{ - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int current_rate = -1; - int force_rate; - int err; - int i; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - intel_dp_flush_connector_commits(connector); - - if (intel_dp->link.active) - current_rate = intel_dp->link_rate; - - force_rate = intel_dp->link.force_rate; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); - - seq_printf(m, "%sauto%s", - force_rate == 0 ? "[" : "", - force_rate == 0 ? "]" : ""); - - for (i = 0; i < intel_dp->num_source_rates; i++) - seq_printf(m, " %s%d%s%s", - intel_dp->source_rates[i] == force_rate ? "[" : "", - intel_dp->source_rates[i], - intel_dp->source_rates[i] == current_rate ? "*" : "", - intel_dp->source_rates[i] == force_rate ? "]" : ""); - - seq_putc(m, '\n'); - - return 0; -} - -static int parse_link_rate(struct intel_dp *intel_dp, const char __user *ubuf, size_t len) -{ - char *kbuf; - const char *p; - int rate; - int ret = 0; - - kbuf = memdup_user_nul(ubuf, len); - if (IS_ERR(kbuf)) - return PTR_ERR(kbuf); - - p = strim(kbuf); - - if (!strcmp(p, "auto")) { - rate = 0; - } else { - ret = kstrtoint(p, 0, &rate); - if (ret < 0) - goto out_free; - - if (intel_dp_rate_index(intel_dp->source_rates, - intel_dp->num_source_rates, - rate) < 0) - ret = -EINVAL; - } - -out_free: - kfree(kbuf); - - return ret < 0 ? ret : rate; -} - -static ssize_t i915_dp_force_link_rate_write(struct file *file, - const char __user *ubuf, - size_t len, loff_t *offp) -{ - struct seq_file *m = file->private_data; - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int rate; - int err; - - rate = parse_link_rate(intel_dp, ubuf, len); - if (rate < 0) - return rate; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - intel_dp_flush_connector_commits(connector); - - intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_rate = rate; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); - - *offp += len; - - return len; -} -DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate); - -static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) -{ - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int current_lane_count = -1; - int force_lane_count; - int err; - int i; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - intel_dp_flush_connector_commits(connector); - - if (intel_dp->link.active) - current_lane_count = intel_dp->lane_count; - force_lane_count = intel_dp->link.force_lane_count; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); - - seq_printf(m, "%sauto%s", - force_lane_count == 0 ? "[" : "", - force_lane_count == 0 ? "]" : ""); - - for (i = 1; i <= 4; i <<= 1) - seq_printf(m, " %s%d%s%s", - i == force_lane_count ? "[" : "", - i, - i == current_lane_count ? "*" : "", - i == force_lane_count ? "]" : ""); - - seq_putc(m, '\n'); - - return 0; -} - -static int parse_lane_count(const char __user *ubuf, size_t len) -{ - char *kbuf; - const char *p; - int lane_count; - int ret = 0; - - kbuf = memdup_user_nul(ubuf, len); - if (IS_ERR(kbuf)) - return PTR_ERR(kbuf); - - p = strim(kbuf); - - if (!strcmp(p, "auto")) { - lane_count = 0; - } else { - ret = kstrtoint(p, 0, &lane_count); - if (ret < 0) - goto out_free; - - switch (lane_count) { - case 1: - case 2: - case 4: - break; - default: - ret = -EINVAL; - } - } - -out_free: - kfree(kbuf); - - return ret < 0 ? ret : lane_count; -} - -static ssize_t i915_dp_force_lane_count_write(struct file *file, - const char __user *ubuf, - size_t len, loff_t *offp) -{ - struct seq_file *m = file->private_data; - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int lane_count; - int err; - - lane_count = parse_lane_count(ubuf, len); - if (lane_count < 0) - return lane_count; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - intel_dp_flush_connector_commits(connector); - - intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_lane_count = lane_count; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); - - *offp += len; - - return len; -} -DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count); - -static int i915_dp_max_link_rate_show(void *data, u64 *val) -{ - struct intel_connector *connector = to_intel_connector(data); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int err; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - intel_dp_flush_connector_commits(connector); - - *val = intel_dp->link.max_rate; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n"); - -static int i915_dp_max_lane_count_show(void *data, u64 *val) -{ - struct intel_connector *connector = to_intel_connector(data); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int err; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - intel_dp_flush_connector_commits(connector); - - *val = intel_dp->link.max_lane_count; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); - static int i915_dp_force_link_training_failure_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); @@ -3033,18 +2782,6 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) return; - debugfs_create_file("i915_dp_force_link_rate", 0644, root, - connector, &i915_dp_force_link_rate_fops); - - debugfs_create_file("i915_dp_force_lane_count", 0644, root, - connector, &i915_dp_force_lane_count_fops); - - debugfs_create_file("i915_dp_max_link_rate", 0444, root, - connector, &i915_dp_max_link_rate_fops); - - debugfs_create_file("i915_dp_max_lane_count", 0444, root, - connector, &i915_dp_max_lane_count_fops); - debugfs_create_file("i915_dp_force_link_training_failure", 0644, root, connector, &i915_dp_force_link_training_failure_fops); From a3369e5c45988f97ac4511bbaf4c0bc91345301f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:28 +0300 Subject: [PATCH 143/186] drm/i915/dp_link_training: Use helpers to get forced link params Use intel_dp_link_caps_get_forced_params() in the link training fallback code instead of directly accessing the state. This allows the link caps module to track changes to forced parameters internally. Reviewed-by: Luca Coelho Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-9-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index cbef3d45baf9..61ada34ab9c8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1850,18 +1850,22 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, int *new_link_rate, int *new_lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int link_rate; int lane_count; int i; + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + i = intel_dp_link_config_index(intel_dp, crtc_state->port_clock, crtc_state->lane_count); for (i--; i >= 0; i--) { intel_dp_link_config_get(intel_dp, i, &link_rate, &lane_count); - if ((intel_dp->link.force_rate && - intel_dp->link.force_rate != link_rate) || - (intel_dp->link.force_lane_count && - intel_dp->link.force_lane_count != lane_count)) + if ((forced_params.rate && + forced_params.rate != link_rate) || + (forced_params.lane_count && + forced_params.lane_count != lane_count)) continue; break; @@ -1878,10 +1882,13 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int rate_index; int new_rate; - if (intel_dp->link.force_rate) + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + if (forced_params.rate) return -1; rate_index = intel_dp_rate_index(intel_dp->common_rates, @@ -1902,7 +1909,10 @@ static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) static int reduce_lane_count(struct intel_dp *intel_dp, int current_lane_count) { - if (intel_dp->link.force_lane_count) + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); + if (forced_params.lane_count) return -1; if (current_lane_count == 1) From 9382aacf74f0374af3994ac5115a9bb941470ec0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:29 +0300 Subject: [PATCH 144/186] drm/i915/dp_link_caps: Move forced link params to link_caps Move tracking of the forced link parameters from struct intel_dp to struct intel_dp_link_caps. Previous changes made all users access these parameters through the link caps helpers, so the state can now be kept internal to the link caps module. Reviewed-by: Luca Coelho Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-10-imre.deak@intel.com --- .../drm/i915/display/intel_display_types.h | 2 -- .../gpu/drm/i915/display/intel_dp_link_caps.c | 30 ++++++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index a232ed210389..e1f56fc6c7a5 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1867,8 +1867,6 @@ struct intel_dp { */ int mst_probed_lane_count; int mst_probed_rate; - int force_lane_count; - int force_rate; struct intel_dp_link_training *training; struct intel_dp_link_caps *caps; } link; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index ea90e84500a8..8ecdc01af70e 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -15,6 +15,12 @@ struct intel_dp_link_caps { struct intel_dp *dp; + + /* + * Forced parameters requested via debugfs. Remains set across sink + * disconnects. + */ + struct intel_dp_link_config forced_params; }; /* Get length of common rates array potentially limited by max_rate. */ @@ -44,20 +50,24 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp) static int forced_lane_count(struct intel_dp *intel_dp) { - if (!intel_dp->link.force_lane_count) + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + + if (!link_caps->forced_params.lane_count) return 0; - return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp)); + return clamp(link_caps->forced_params.lane_count, + 1, intel_dp_max_common_lane_count(intel_dp)); } static int forced_link_rate(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int len; - if (!intel_dp->link.force_rate) + if (!link_caps->forced_params.rate) return 0; - len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate); + len = intel_dp_common_len_rate_limit(intel_dp, link_caps->forced_params.rate); if (len == 0) return intel_dp_common_rate(intel_dp, 0); @@ -76,6 +86,7 @@ static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int current_rate = -1; int force_rate; int err; @@ -90,7 +101,7 @@ static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) if (intel_dp->link.active) current_rate = intel_dp->link_rate; - force_rate = intel_dp->link.force_rate; + force_rate = link_caps->forced_params.rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -150,6 +161,7 @@ static ssize_t i915_dp_force_link_rate_write(struct file *file, struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int rate; int err; @@ -164,7 +176,7 @@ static ssize_t i915_dp_force_link_rate_write(struct file *file, intel_dp_flush_connector_commits(connector); intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_rate = rate; + link_caps->forced_params.rate = rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -179,6 +191,7 @@ static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int current_lane_count = -1; int force_lane_count; int err; @@ -192,7 +205,7 @@ static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) if (intel_dp->link.active) current_lane_count = intel_dp->lane_count; - force_lane_count = intel_dp->link.force_lane_count; + force_lane_count = link_caps->forced_params.lane_count; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -256,6 +269,7 @@ static ssize_t i915_dp_force_lane_count_write(struct file *file, struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int lane_count; int err; @@ -270,7 +284,7 @@ static ssize_t i915_dp_force_lane_count_write(struct file *file, intel_dp_flush_connector_commits(connector); intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_lane_count = lane_count; + link_caps->forced_params.lane_count = lane_count; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); From b9d985949e32010c90727edae74da4334ff82e89 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:30 +0300 Subject: [PATCH 145/186] drm/i915/dp_link_caps: Move link config helpers to link caps Move the helpers handling link configurations to intel_dp_link_caps.c. Their functionality is part of the link capability logic and will be updated to use the link capability state in follow-up changes. Reviewed-by: Luca Coelho Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-11-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 101 ----------------- drivers/gpu/drm/i915/display/intel_dp.h | 2 - .../gpu/drm/i915/display/intel_dp_link_caps.c | 102 ++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 5 + 4 files changed, 107 insertions(+), 103 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index a43d09ca7461..740c4aab25a0 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -677,106 +676,6 @@ int intel_dp_rate_index(const int *rates, int len, int rate) return -1; } -static int intel_dp_link_config_rate(struct intel_dp *intel_dp, - const struct intel_dp_link_config_entry *lc) -{ - return intel_dp_common_rate(intel_dp, lc->link_rate_idx); -} - -static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lc) -{ - return 1 << lc->lane_count_exp; -} - -static int intel_dp_link_config_bw(struct intel_dp *intel_dp, - const struct intel_dp_link_config_entry *lc) -{ - return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc), - intel_dp_link_config_lane_count(lc)); -} - -static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) -{ - struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ - const struct intel_dp_link_config_entry *lc_a = a; - const struct intel_dp_link_config_entry *lc_b = b; - int bw_a = intel_dp_link_config_bw(intel_dp, lc_a); - int bw_b = intel_dp_link_config_bw(intel_dp, lc_b); - - if (bw_a != bw_b) - return bw_a - bw_b; - - return intel_dp_link_config_rate(intel_dp, lc_a) - - intel_dp_link_config_rate(intel_dp, lc_b); -} - -static void intel_dp_link_config_init(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_dp_link_config_entry *lc; - int num_common_lane_configs; - int i; - int j; - - if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp)))) - return; - - num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; - - if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs > - ARRAY_SIZE(intel_dp->link.configs))) - return; - - intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs; - - lc = &intel_dp->link.configs[0]; - for (i = 0; i < intel_dp->num_common_rates; i++) { - for (j = 0; j < num_common_lane_configs; j++) { - lc->lane_count_exp = j; - lc->link_rate_idx = i; - - lc++; - } - } - - sort_r(intel_dp->link.configs, intel_dp->link.num_configs, - sizeof(intel_dp->link.configs[0]), - link_config_cmp_by_bw, NULL, - intel_dp); -} - -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) -{ - struct intel_display *display = to_intel_display(intel_dp); - const struct intel_dp_link_config_entry *lc; - - if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs)) - idx = 0; - - lc = &intel_dp->link.configs[idx]; - - *link_rate = intel_dp_link_config_rate(intel_dp, lc); - *lane_count = intel_dp_link_config_lane_count(lc); -} - -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count) -{ - int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates, - link_rate); - int lane_count_exp = ilog2(lane_count); - int i; - - for (i = 0; i < intel_dp->link.num_configs; i++) { - const struct intel_dp_link_config_entry *lc = &intel_dp->link.configs[i]; - - if (lc->lane_count_exp == lane_count_exp && - lc->link_rate_idx == link_rate_idx) - return i; - } - - return -1; -} - /* Return %true if the common rates changed. */ static bool intel_dp_set_common_rates(struct intel_dp *intel_dp) { diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 32395900c47a..fcf213775bdf 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -109,8 +109,6 @@ int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); int intel_dp_max_common_lane_count(struct intel_dp *intel_dp); int intel_dp_rate_index(const int *rates, int len, int rate); -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); void intel_dp_update_sink_caps(struct intel_dp *intel_dp); void intel_dp_reset_link_params(struct intel_dp *intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 8ecdc01af70e..6a37ba8c35e2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -4,7 +4,9 @@ */ #include +#include #include +#include #include @@ -81,6 +83,106 @@ void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, forced_params->lane_count = forced_lane_count(link_caps->dp); } +static int intel_dp_link_config_rate(struct intel_dp *intel_dp, + const struct intel_dp_link_config_entry *lc) +{ + return intel_dp_common_rate(intel_dp, lc->link_rate_idx); +} + +static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lc) +{ + return 1 << lc->lane_count_exp; +} + +static int intel_dp_link_config_bw(struct intel_dp *intel_dp, + const struct intel_dp_link_config_entry *lc) +{ + return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc), + intel_dp_link_config_lane_count(lc)); +} + +static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) +{ + struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ + const struct intel_dp_link_config_entry *lc_a = a; + const struct intel_dp_link_config_entry *lc_b = b; + int bw_a = intel_dp_link_config_bw(intel_dp, lc_a); + int bw_b = intel_dp_link_config_bw(intel_dp, lc_b); + + if (bw_a != bw_b) + return bw_a - bw_b; + + return intel_dp_link_config_rate(intel_dp, lc_a) - + intel_dp_link_config_rate(intel_dp, lc_b); +} + +void intel_dp_link_config_init(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + struct intel_dp_link_config_entry *lc; + int num_common_lane_configs; + int i; + int j; + + if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp)))) + return; + + num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; + + if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs > + ARRAY_SIZE(intel_dp->link.configs))) + return; + + intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs; + + lc = &intel_dp->link.configs[0]; + for (i = 0; i < intel_dp->num_common_rates; i++) { + for (j = 0; j < num_common_lane_configs; j++) { + lc->lane_count_exp = j; + lc->link_rate_idx = i; + + lc++; + } + } + + sort_r(intel_dp->link.configs, intel_dp->link.num_configs, + sizeof(intel_dp->link.configs[0]), + link_config_cmp_by_bw, NULL, + intel_dp); +} + +void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) +{ + struct intel_display *display = to_intel_display(intel_dp); + const struct intel_dp_link_config_entry *lc; + + if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs)) + idx = 0; + + lc = &intel_dp->link.configs[idx]; + + *link_rate = intel_dp_link_config_rate(intel_dp, lc); + *lane_count = intel_dp_link_config_lane_count(lc); +} + +int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count) +{ + int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates, + link_rate); + int lane_count_exp = ilog2(lane_count); + int i; + + for (i = 0; i < intel_dp->link.num_configs; i++) { + const struct intel_dp_link_config_entry *lc = &intel_dp->link.configs[i]; + + if (lc->lane_count_exp == lane_count_exp && + lc->link_rate_idx == link_rate_idx) + return i; + } + + return -1; +} + static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index c6a84891db46..dab956e804b9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -17,6 +17,11 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp); void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params); +int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); +void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); + +void intel_dp_link_config_init(struct intel_dp *intel_dp); + void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); From 67cd7367fd2d07ab6f56fceabbd15e2bc4bc24ed Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:31 +0300 Subject: [PATCH 146/186] drm/i915/dp_link_caps: Move link config tracking to link_caps Move tracking of the link configurations from struct intel_dp to struct intel_dp_link_caps. Previous changes moved the helpers operating on configurations to the link caps module, so the state can now be kept internal to that module. Reviewed-by: Mika Kahola Reviewed-by: Luca Coelho Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-12-imre.deak@intel.com --- .../drm/i915/display/intel_display_types.h | 12 ------- .../gpu/drm/i915/display/intel_dp_link_caps.c | 36 ++++++++++++++----- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index e1f56fc6c7a5..dcf21aa34eb7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1843,18 +1843,6 @@ struct intel_dp { struct { /* TODO: move the rest of link specific fields to here */ bool active; - /* common rate,lane_count configs in bw order */ - int num_configs; -#define INTEL_DP_MAX_LANE_COUNT 4 -#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1) -#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) -#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS) -#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ - INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) - struct intel_dp_link_config_entry { - u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; - u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; - } configs[INTEL_DP_MAX_LINK_CONFIGS]; /* Max lane count for the current link */ int max_lane_count; /* Max rate for the current link */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 6a37ba8c35e2..05ec933c7440 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -3,10 +3,12 @@ * Copyright © 2026 Intel Corporation */ +#include #include #include #include #include +#include #include @@ -18,6 +20,19 @@ struct intel_dp_link_caps { struct intel_dp *dp; + /* common rate,lane_count configs in bw order */ + int num_configs; +#define INTEL_DP_MAX_LANE_COUNT 4 +#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1) +#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) +#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS) +#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ + INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) + struct intel_dp_link_config_entry { + u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; + u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; + } configs[INTEL_DP_MAX_LINK_CONFIGS]; + /* * Forced parameters requested via debugfs. Remains set across sink * disconnects. @@ -118,6 +133,7 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) void intel_dp_link_config_init(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_config_entry *lc; int num_common_lane_configs; @@ -130,12 +146,12 @@ void intel_dp_link_config_init(struct intel_dp *intel_dp) num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs > - ARRAY_SIZE(intel_dp->link.configs))) + ARRAY_SIZE(link_caps->configs))) return; - intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs; + link_caps->num_configs = intel_dp->num_common_rates * num_common_lane_configs; - lc = &intel_dp->link.configs[0]; + lc = &link_caps->configs[0]; for (i = 0; i < intel_dp->num_common_rates; i++) { for (j = 0; j < num_common_lane_configs; j++) { lc->lane_count_exp = j; @@ -145,21 +161,22 @@ void intel_dp_link_config_init(struct intel_dp *intel_dp) } } - sort_r(intel_dp->link.configs, intel_dp->link.num_configs, - sizeof(intel_dp->link.configs[0]), + sort_r(link_caps->configs, link_caps->num_configs, + sizeof(link_caps->configs[0]), link_config_cmp_by_bw, NULL, intel_dp); } void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); const struct intel_dp_link_config_entry *lc; - if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs)) + if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs)) idx = 0; - lc = &intel_dp->link.configs[idx]; + lc = &link_caps->configs[idx]; *link_rate = intel_dp_link_config_rate(intel_dp, lc); *lane_count = intel_dp_link_config_lane_count(lc); @@ -167,13 +184,14 @@ void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates, link_rate); int lane_count_exp = ilog2(lane_count); int i; - for (i = 0; i < intel_dp->link.num_configs; i++) { - const struct intel_dp_link_config_entry *lc = &intel_dp->link.configs[i]; + for (i = 0; i < link_caps->num_configs; i++) { + const struct intel_dp_link_config_entry *lc = &link_caps->configs[i]; if (lc->lane_count_exp == lane_count_exp && lc->link_rate_idx == link_rate_idx) From b5ed2013b57bf7eb204066ac924577424b333303 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:32 +0300 Subject: [PATCH 147/186] drm/i915/dp_link_caps: Rename helper updating the link configurations Rename the helper updating link configurations to intel_dp_link_caps_update() to better reflect its functionality. Reviewed-by: Nemesa Garg Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-13-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 2 +- drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 2 +- drivers/gpu/drm/i915/display/intel_dp_link_caps.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 740c4aab25a0..b9d503b94983 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -720,7 +720,7 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) if (intel_dp_set_max_common_lane_count(intel_dp)) params_changed = true; - intel_dp_link_config_init(intel_dp); + intel_dp_link_caps_update(intel_dp); return params_changed; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 05ec933c7440..207495293173 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -131,7 +131,7 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) intel_dp_link_config_rate(intel_dp, lc_b); } -void intel_dp_link_config_init(struct intel_dp *intel_dp) +void intel_dp_link_caps_update(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index dab956e804b9..aed2122a05d2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -20,7 +20,7 @@ void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); -void intel_dp_link_config_init(struct intel_dp *intel_dp); +void intel_dp_link_caps_update(struct intel_dp *intel_dp); void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); From 36f330ebdd2b40c4939c9d45f739c72487790d3c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:33 +0300 Subject: [PATCH 148/186] drm/i915/dp: Factor out helper to get link rate capabilities Factor out a helper to get the supported link rates. This allows to gather all the link capabilities and pass these to the link capability module from a single place. A follow-up change will extend this to gather and pass the maximum lane count capability in the same way. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-14-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 37 +++++++++++++++---------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index b9d503b94983..bce9a89a93a1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -676,33 +676,40 @@ int intel_dp_rate_index(const int *rates, int len, int rate) return -1; } -/* Return %true if the common rates changed. */ -static bool intel_dp_set_common_rates(struct intel_dp *intel_dp) +static void intel_dp_get_common_rates(struct intel_dp *intel_dp, + int common_rates[DP_MAX_SUPPORTED_RATES], + int *num_common_rates) { struct intel_display *display = to_intel_display(intel_dp); - int num_old_common_rates = intel_dp->num_common_rates; - int old_common_rates[DP_MAX_SUPPORTED_RATES]; drm_WARN_ON(display->drm, !intel_dp->num_source_rates || !intel_dp->num_sink_rates); + *num_common_rates = intersect_rates(intel_dp->source_rates, + intel_dp->num_source_rates, + intel_dp->sink_rates, + intel_dp->num_sink_rates, + common_rates); + + /* Paranoia, there should always be something in common. */ + if (drm_WARN_ON(display->drm, *num_common_rates == 0)) { + common_rates[0] = 162000; + *num_common_rates = 1; + } +} + +static bool intel_dp_set_common_rates(struct intel_dp *intel_dp) +{ + int num_old_common_rates = intel_dp->num_common_rates; + int old_common_rates[DP_MAX_SUPPORTED_RATES]; + /* TODO: Add a struct containing both rates and number of rates. */ static_assert(__same_type(old_common_rates[0], intel_dp->common_rates[0]) && sizeof(old_common_rates) == sizeof(intel_dp->common_rates)); memcpy(old_common_rates, intel_dp->common_rates, num_old_common_rates * sizeof(old_common_rates[0])); - intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates, - intel_dp->num_source_rates, - intel_dp->sink_rates, - intel_dp->num_sink_rates, - intel_dp->common_rates); - - /* Paranoia, there should always be something in common. */ - if (drm_WARN_ON(display->drm, intel_dp->num_common_rates == 0)) { - intel_dp->common_rates[0] = 162000; - intel_dp->num_common_rates = 1; - } + intel_dp_get_common_rates(intel_dp, intel_dp->common_rates, &intel_dp->num_common_rates); return num_old_common_rates != intel_dp->num_common_rates || memcmp(old_common_rates, intel_dp->common_rates, From 7aa14e73519d89bd0933df418a488d9d7452c3a1 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:34 +0300 Subject: [PATCH 149/186] drm/i915/dp_link_caps: Pass supported link rates to link caps update Pass the supported link rates explicitly to intel_dp_link_caps_update(). This prepares for tracking these capabilities internally within the link caps module. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-15-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 28 ++++--------------- .../gpu/drm/i915/display/intel_dp_link_caps.c | 28 +++++++++++++++---- .../gpu/drm/i915/display/intel_dp_link_caps.h | 5 +++- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index bce9a89a93a1..e2d83f0fcf91 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -698,36 +698,20 @@ static void intel_dp_get_common_rates(struct intel_dp *intel_dp, } } -static bool intel_dp_set_common_rates(struct intel_dp *intel_dp) -{ - int num_old_common_rates = intel_dp->num_common_rates; - int old_common_rates[DP_MAX_SUPPORTED_RATES]; - - /* TODO: Add a struct containing both rates and number of rates. */ - static_assert(__same_type(old_common_rates[0], intel_dp->common_rates[0]) && - sizeof(old_common_rates) == sizeof(intel_dp->common_rates)); - memcpy(old_common_rates, intel_dp->common_rates, - num_old_common_rates * sizeof(old_common_rates[0])); - - intel_dp_get_common_rates(intel_dp, intel_dp->common_rates, &intel_dp->num_common_rates); - - return num_old_common_rates != intel_dp->num_common_rates || - memcmp(old_common_rates, intel_dp->common_rates, - num_old_common_rates * sizeof(old_common_rates[0])); -} - /* Return %true if any common link param changed. */ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) { + int num_common_rates; + int common_rates[DP_MAX_SUPPORTED_RATES]; bool params_changed = false; - if (intel_dp_set_common_rates(intel_dp)) - params_changed = true; - if (intel_dp_set_max_common_lane_count(intel_dp)) params_changed = true; - intel_dp_link_caps_update(intel_dp); + intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); + if (intel_dp_link_caps_update(intel_dp, + common_rates, num_common_rates)) + params_changed = true; return params_changed; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 207495293173..679d59cc256c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -131,25 +132,39 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) intel_dp_link_config_rate(intel_dp, lc_b); } -void intel_dp_link_caps_update(struct intel_dp *intel_dp) +/* Return %true if the supported link parameters have changed. */ +bool intel_dp_link_caps_update(struct intel_dp *intel_dp, + const int *rates, int num_rates) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_config_entry *lc; + bool link_params_changed = false; int num_common_lane_configs; int i; int j; if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp)))) - return; + return false; + + if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(intel_dp->common_rates))) + return false; num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; - if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs > + if (drm_WARN_ON(display->drm, num_rates * num_common_lane_configs > ARRAY_SIZE(link_caps->configs))) - return; + return false; - link_caps->num_configs = intel_dp->num_common_rates * num_common_lane_configs; + /* TODO: Add a struct containing both rates and number of rates. */ + static_assert(__same_type(rates[0], intel_dp->common_rates[0])); + if (num_rates != intel_dp->num_common_rates || + memcmp(rates, intel_dp->common_rates, num_rates * sizeof(rates[0]))) + link_params_changed = true; + + memcpy(intel_dp->common_rates, rates, num_rates * sizeof(rates[0])); + intel_dp->num_common_rates = num_rates; + link_caps->num_configs = num_rates * num_common_lane_configs; lc = &link_caps->configs[0]; for (i = 0; i < intel_dp->num_common_rates; i++) { @@ -165,6 +180,9 @@ void intel_dp_link_caps_update(struct intel_dp *intel_dp) sizeof(link_caps->configs[0]), link_config_cmp_by_bw, NULL, intel_dp); + + /* TODO: Also detect a change in the max lane count. */ + return link_params_changed; } void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index aed2122a05d2..09e580bc5c9b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -4,6 +4,8 @@ #ifndef __INTEL_DP_LINK_CAPS_H__ #define __INTEL_DP_LINK_CAPS_H__ +#include + struct intel_connector; struct intel_dp; struct intel_dp_link_caps; @@ -20,7 +22,8 @@ void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); -void intel_dp_link_caps_update(struct intel_dp *intel_dp); +bool intel_dp_link_caps_update(struct intel_dp *intel_dp, + const int *rates, int num_rates); void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); From 96d07a10cfdcbb5327e18121079d4d7bb8553490 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:35 +0300 Subject: [PATCH 150/186] drm/i915/dp_link_caps: Add helper to print all supported link rates Add intel_dp_link_caps_print_rates() to print all the supported link rates tracked by the link_caps module. This prepares for tracking these capabilities internally within the link caps module. Suggested-by: Jani Nikula Reviewed-by: Nemesa Garg Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-16-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 4 +--- drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 14 ++++++++++++++ drivers/gpu/drm/i915/display/intel_dp_link_caps.h | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index e2d83f0fcf91..11e03ee9d639 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1543,9 +1543,7 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) seq_buf_print_array(&s, intel_dp->sink_rates, intel_dp->num_sink_rates); drm_dbg_kms(display->drm, "sink rates: %s\n", seq_buf_str(&s)); - seq_buf_clear(&s); - seq_buf_print_array(&s, intel_dp->common_rates, intel_dp->num_common_rates); - drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); + intel_dp_link_caps_print_common_rates(intel_dp->link.caps); } int diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 679d59cc256c..13f9bfd5d7ba 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,19 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp) return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); } +void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) +{ + struct intel_dp *intel_dp = link_caps->dp; + struct intel_display *display = to_intel_display(intel_dp); + DECLARE_SEQ_BUF(s, 128); + int i; + + for (i = 0; i < intel_dp->num_common_rates; i++) + seq_buf_printf(&s, "%s%d", i ? ", " : "", intel_dp->common_rates[i]); + + drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); +} + static int forced_lane_count(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 09e580bc5c9b..7333df6b82f9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -16,6 +16,8 @@ int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, int intel_dp_common_rate(struct intel_dp *intel_dp, int index); int intel_dp_max_common_rate(struct intel_dp *intel_dp); +void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps); + void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params); From 66cd2b38b5138945d3a5805695c2abec33ad2e50 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:36 +0300 Subject: [PATCH 151/186] drm/i915/dp_link_caps: Add helper to get the number of supported link rates Add intel_dp_link_caps_num_common_rates() to return the number of supported link rates tracked by the link_caps module. This prepares for tracking these capabilities internally within the link caps module. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-17-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 5 +++++ drivers/gpu/drm/i915/display/intel_dp_link_caps.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 11e03ee9d639..7ef94366fd04 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1766,7 +1766,7 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, int link_bpp_x16 = intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp); - for (i = 0; i < intel_dp->num_common_rates; i++) { + for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { link_rate = intel_dp_common_rate(intel_dp, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) @@ -1995,7 +1995,7 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, int link_rate, lane_count; int i; - for (i = 0; i < intel_dp->num_common_rates; i++) { + for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { link_rate = intel_dp_common_rate(intel_dp, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) continue; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 13f9bfd5d7ba..09b60a0cd6fb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -67,6 +67,11 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp) return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); } +int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps) +{ + return link_caps->dp->num_common_rates; +} + void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) { struct intel_dp *intel_dp = link_caps->dp; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 7333df6b82f9..3413f6f76045 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -15,6 +15,7 @@ int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, int max_rate); int intel_dp_common_rate(struct intel_dp *intel_dp, int index); int intel_dp_max_common_rate(struct intel_dp *intel_dp); +int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps); From 487beac7b8efb45beb6f09552bcb869184ed7140 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:37 +0300 Subject: [PATCH 152/186] drm/i915/dp_link_caps: Add helper to get common rate index Add intel_dp_link_caps_common_rate_idx() to look up supported link rates tracked by the link_caps module by rate. This prepares for tracking these capabilities internally within the link caps module. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-18-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 9 +++++++++ drivers/gpu/drm/i915/display/intel_dp_link_caps.h | 1 + drivers/gpu/drm/i915/display/intel_dp_link_training.c | 5 ++--- drivers/gpu/drm/i915/display/intel_dp_test.c | 7 ++++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 09b60a0cd6fb..84d9636f4adb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -61,6 +61,15 @@ int intel_dp_common_rate(struct intel_dp *intel_dp, int index) return intel_dp->common_rates[index]; } +int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate) +{ + struct intel_dp *intel_dp = link_caps->dp; + + return intel_dp_rate_index(intel_dp->common_rates, + intel_dp->num_common_rates, + rate); +} + /* Theoretical max between source and sink */ int intel_dp_max_common_rate(struct intel_dp *intel_dp) { diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 3413f6f76045..7d7d3d11ba3f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -14,6 +14,7 @@ struct intel_dp_link_config; int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, int max_rate); int intel_dp_common_rate(struct intel_dp *intel_dp, int index); +int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate); int intel_dp_max_common_rate(struct intel_dp *intel_dp); int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 61ada34ab9c8..ec9bd9b4c800 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1891,9 +1891,8 @@ static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) if (forced_params.rate) return -1; - rate_index = intel_dp_rate_index(intel_dp->common_rates, - intel_dp->num_common_rates, - current_rate); + rate_index = intel_dp_link_caps_common_rate_idx(link_caps, + current_rate); if (rate_index <= 0) return -1; diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.c b/drivers/gpu/drm/i915/display/intel_dp_test.c index ba44769c9cfb..da7632536dac 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_test.c +++ b/drivers/gpu/drm/i915/display/intel_dp_test.c @@ -14,6 +14,7 @@ #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -32,6 +33,7 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, struct intel_crtc_state *pipe_config, struct link_config_limits *limits) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); /* For DP Compliance we override the computed bpp for the pipe */ @@ -54,9 +56,8 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, */ if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate, intel_dp->compliance.test_lane_count)) { - index = intel_dp_rate_index(intel_dp->common_rates, - intel_dp->num_common_rates, - intel_dp->compliance.test_link_rate); + index = intel_dp_link_caps_common_rate_idx(link_caps, + intel_dp->compliance.test_link_rate); if (index >= 0) { limits->min_rate = intel_dp->compliance.test_link_rate; limits->max_rate = intel_dp->compliance.test_link_rate; From 70f6d73a28469592debcb2829e0dcecb861b3123 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:38 +0300 Subject: [PATCH 153/186] drm/i915/dp_link_caps: Move tracking of common rates to link_caps struct Now that all users access the supported link rates via helpers, move tracking of these rates from struct intel_dp to the link_caps state. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-19-imre.deak@intel.com --- .../drm/i915/display/intel_display_types.h | 3 -- .../gpu/drm/i915/display/intel_dp_link_caps.c | 51 +++++++++++-------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index dcf21aa34eb7..8beddb21a9b1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1836,9 +1836,6 @@ struct intel_dp { bool use_rate_select; /* Max sink lane count as reported by DP_MAX_LANE_COUNT */ int max_sink_lane_count; - /* intersection of source and sink rates */ - int num_common_rates; - int common_rates[DP_MAX_SUPPORTED_RATES]; int max_common_lane_count; struct { /* TODO: move the rest of link specific fields to here */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 84d9636f4adb..e28f7308283c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -22,6 +22,10 @@ struct intel_dp_link_caps { struct intel_dp *dp; + /* Rate, lane count caps common to source and sink. */ + int num_rates; + int rates[DP_MAX_SUPPORTED_RATES]; + /* common rate,lane_count configs in bw order */ int num_configs; #define INTEL_DP_MAX_LANE_COUNT 4 @@ -31,6 +35,7 @@ struct intel_dp_link_caps { #define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) struct intel_dp_link_config_entry { + /* index into rates[] */ u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; } configs[INTEL_DP_MAX_LINK_CONFIGS]; @@ -46,50 +51,52 @@ struct intel_dp_link_caps { int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, int max_rate) { - return intel_dp_rate_limit_len(intel_dp->common_rates, - intel_dp->num_common_rates, max_rate); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + + return intel_dp_rate_limit_len(link_caps->rates, + link_caps->num_rates, max_rate); } int intel_dp_common_rate(struct intel_dp *intel_dp, int index) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); if (drm_WARN_ON(display->drm, - index < 0 || index >= intel_dp->num_common_rates)) + index < 0 || index >= link_caps->num_rates)) return 162000; - return intel_dp->common_rates[index]; + return link_caps->rates[index]; } int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate) { - struct intel_dp *intel_dp = link_caps->dp; - - return intel_dp_rate_index(intel_dp->common_rates, - intel_dp->num_common_rates, + return intel_dp_rate_index(link_caps->rates, + link_caps->num_rates, rate); } /* Theoretical max between source and sink */ int intel_dp_max_common_rate(struct intel_dp *intel_dp) { - return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + + return intel_dp_common_rate(intel_dp, link_caps->num_rates - 1); } int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps) { - return link_caps->dp->num_common_rates; + return link_caps->num_rates; } void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) { - struct intel_dp *intel_dp = link_caps->dp; - struct intel_display *display = to_intel_display(intel_dp); + struct intel_display *display = to_intel_display(link_caps->dp); DECLARE_SEQ_BUF(s, 128); int i; - for (i = 0; i < intel_dp->num_common_rates; i++) - seq_buf_printf(&s, "%s%d", i ? ", " : "", intel_dp->common_rates[i]); + for (i = 0; i < link_caps->num_rates; i++) + seq_buf_printf(&s, "%s%d", i ? ", " : "", link_caps->rates[i]); drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); } @@ -175,7 +182,7 @@ bool intel_dp_link_caps_update(struct intel_dp *intel_dp, if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp)))) return false; - if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(intel_dp->common_rates))) + if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(link_caps->rates))) return false; num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; @@ -185,17 +192,17 @@ bool intel_dp_link_caps_update(struct intel_dp *intel_dp, return false; /* TODO: Add a struct containing both rates and number of rates. */ - static_assert(__same_type(rates[0], intel_dp->common_rates[0])); - if (num_rates != intel_dp->num_common_rates || - memcmp(rates, intel_dp->common_rates, num_rates * sizeof(rates[0]))) + static_assert(__same_type(rates[0], link_caps->rates[0])); + if (num_rates != link_caps->num_rates || + memcmp(rates, link_caps->rates, num_rates * sizeof(rates[0]))) link_params_changed = true; - memcpy(intel_dp->common_rates, rates, num_rates * sizeof(rates[0])); - intel_dp->num_common_rates = num_rates; + memcpy(link_caps->rates, rates, num_rates * sizeof(rates[0])); + link_caps->num_rates = num_rates; link_caps->num_configs = num_rates * num_common_lane_configs; lc = &link_caps->configs[0]; - for (i = 0; i < intel_dp->num_common_rates; i++) { + for (i = 0; i < link_caps->num_rates; i++) { for (j = 0; j < num_common_lane_configs; j++) { lc->lane_count_exp = j; lc->link_rate_idx = i; @@ -231,7 +238,7 @@ void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates, + int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates, link_rate); int lane_count_exp = ilog2(lane_count); int i; From de87e3bfc519b4eae0476f60af966befb988f9c7 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:39 +0300 Subject: [PATCH 154/186] drm/i915/dp_link_caps: Track max common lane count in link_caps Pass the maximum common lane count to intel_dp_link_caps_update() and track it together with the supported link rates. This prepares for converting all users of intel_dp_max_common_lane_count() to query the value from the link caps module instead. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-20-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 14 +++++--------- drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 13 +++++++++---- drivers/gpu/drm/i915/display/intel_dp_link_caps.h | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 7ef94366fd04..43b496f91f08 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -339,23 +339,19 @@ int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) /* * Theoretical max between source and sink. - * Return %true if the max common lane count changed. */ -static bool intel_dp_set_max_common_lane_count(struct intel_dp *intel_dp) +static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); int source_max = intel_dp_max_source_lane_count(dig_port); int sink_max = intel_dp->max_sink_lane_count; int lane_max = intel_tc_port_max_lane_count(dig_port); int lttpr_max = drm_dp_lttpr_max_lane_count(intel_dp->lttpr_common_caps); - int old_max_common_lane_count = intel_dp->max_common_lane_count; if (lttpr_max) sink_max = min(sink_max, lttpr_max); - intel_dp->max_common_lane_count = min3(source_max, sink_max, lane_max); - - return intel_dp->max_common_lane_count != old_max_common_lane_count; + return min3(source_max, sink_max, lane_max); } int intel_dp_max_common_lane_count(struct intel_dp *intel_dp) @@ -705,12 +701,12 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) int common_rates[DP_MAX_SUPPORTED_RATES]; bool params_changed = false; - if (intel_dp_set_max_common_lane_count(intel_dp)) - params_changed = true; + intel_dp->max_common_lane_count = intel_dp_get_max_common_lane_count(intel_dp); intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); if (intel_dp_link_caps_update(intel_dp, - common_rates, num_common_rates)) + common_rates, num_common_rates, + intel_dp_get_max_common_lane_count(intel_dp))) params_changed = true; return params_changed; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index e28f7308283c..bb727bcf4de1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -25,6 +25,7 @@ struct intel_dp_link_caps { /* Rate, lane count caps common to source and sink. */ int num_rates; int rates[DP_MAX_SUPPORTED_RATES]; + int max_lane_count; /* common rate,lane_count configs in bw order */ int num_configs; @@ -169,7 +170,7 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) /* Return %true if the supported link parameters have changed. */ bool intel_dp_link_caps_update(struct intel_dp *intel_dp, - const int *rates, int num_rates) + const int *rates, int num_rates, int max_lane_count) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); @@ -179,13 +180,13 @@ bool intel_dp_link_caps_update(struct intel_dp *intel_dp, int i; int j; - if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp)))) + if (drm_WARN_ON(display->drm, !is_power_of_2(max_lane_count))) return false; if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(link_caps->rates))) return false; - num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; + num_common_lane_configs = ilog2(max_lane_count) + 1; if (drm_WARN_ON(display->drm, num_rates * num_common_lane_configs > ARRAY_SIZE(link_caps->configs))) @@ -197,8 +198,13 @@ bool intel_dp_link_caps_update(struct intel_dp *intel_dp, memcmp(rates, link_caps->rates, num_rates * sizeof(rates[0]))) link_params_changed = true; + if (max_lane_count != link_caps->max_lane_count) + link_params_changed = true; + memcpy(link_caps->rates, rates, num_rates * sizeof(rates[0])); link_caps->num_rates = num_rates; + link_caps->max_lane_count = max_lane_count; + link_caps->num_configs = num_rates * num_common_lane_configs; lc = &link_caps->configs[0]; @@ -216,7 +222,6 @@ bool intel_dp_link_caps_update(struct intel_dp *intel_dp, link_config_cmp_by_bw, NULL, intel_dp); - /* TODO: Also detect a change in the max lane count. */ return link_params_changed; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 7d7d3d11ba3f..e2f53eb167a8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -27,7 +27,7 @@ int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lan void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); bool intel_dp_link_caps_update(struct intel_dp *intel_dp, - const int *rates, int num_rates); + const int *rates, int num_rates, int max_lane_count); void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); From bbf51a67d1fe895f3d27402d2560259a619b6dba Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:40 +0300 Subject: [PATCH 155/186] drm/i915/dp_link_caps: Use max common lane count from link_caps Convert all users of intel_dp_max_common_lane_count() to query the maximum common lane count via the link capability API, in common with the link rate queries. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-21-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/intel_dp.c | 9 +-------- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 7 ++++++- drivers/gpu/drm/i915/display/intel_dp_link_caps.h | 1 + drivers/gpu/drm/i915/display/intel_dp_tunnel.c | 3 ++- 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 8beddb21a9b1..e5492996b9e4 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1836,7 +1836,6 @@ struct intel_dp { bool use_rate_select; /* Max sink lane count as reported by DP_MAX_LANE_COUNT */ int max_sink_lane_count; - int max_common_lane_count; struct { /* TODO: move the rest of link specific fields to here */ bool active; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 43b496f91f08..c06d575ebd32 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -354,11 +354,6 @@ static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp) return min3(source_max, sink_max, lane_max); } -int intel_dp_max_common_lane_count(struct intel_dp *intel_dp) -{ - return intel_dp->max_common_lane_count; -} - int intel_dp_max_lane_count(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; @@ -701,8 +696,6 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) int common_rates[DP_MAX_SUPPORTED_RATES]; bool params_changed = false; - intel_dp->max_common_lane_count = intel_dp_get_max_common_lane_count(intel_dp); - intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); if (intel_dp_link_caps_update(intel_dp, common_rates, num_common_rates, @@ -3613,7 +3606,7 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, void intel_dp_reset_link_params(struct intel_dp *intel_dp) { - intel_dp->link.max_lane_count = intel_dp_max_common_lane_count(intel_dp); + intel_dp->link.max_lane_count = intel_dp_link_caps_max_common_lane_count(intel_dp->link.caps); intel_dp->link.max_rate = intel_dp_max_common_rate(intel_dp); intel_dp->link.mst_probed_lane_count = 0; intel_dp->link.mst_probed_rate = 0; diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index fcf213775bdf..02b691df6755 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -107,7 +107,6 @@ int intel_dp_max_link_rate(struct intel_dp *intel_dp); int intel_dp_max_lane_count(struct intel_dp *intel_dp); int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); -int intel_dp_max_common_lane_count(struct intel_dp *intel_dp); int intel_dp_rate_index(const int *rates, int len, int rate); void intel_dp_update_sink_caps(struct intel_dp *intel_dp); void intel_dp_reset_link_params(struct intel_dp *intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index bb727bcf4de1..b227c9a55f63 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -102,6 +102,11 @@ void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); } +int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) +{ + return link_caps->max_lane_count; +} + static int forced_lane_count(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; @@ -110,7 +115,7 @@ static int forced_lane_count(struct intel_dp *intel_dp) return 0; return clamp(link_caps->forced_params.lane_count, - 1, intel_dp_max_common_lane_count(intel_dp)); + 1, intel_dp_link_caps_max_common_lane_count(link_caps)); } static int forced_link_rate(struct intel_dp *intel_dp) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index e2f53eb167a8..9218cb5de2c7 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -17,6 +17,7 @@ int intel_dp_common_rate(struct intel_dp *intel_dp, int index); int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate); int intel_dp_max_common_rate(struct intel_dp *intel_dp); int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); +int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps); void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps); diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index c82adfcce01d..9d9d8d04742b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -57,8 +57,9 @@ static int kbytes_to_mbits(int kbytes) static int get_current_link_bw(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int rate = intel_dp_max_common_rate(intel_dp); - int lane_count = intel_dp_max_common_lane_count(intel_dp); + int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps); return intel_dp_max_link_data_rate(intel_dp, rate, lane_count); } From 45408c4c32ddcb3ebce358726d0c2a02d228a6c7 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:41 +0300 Subject: [PATCH 156/186] drm/i915/dp_link_caps: Add helpers to get max link limits Add intel_dp_link_caps_get_max_limits() to query the current maximum link limits (max bound over all allowed configurations) through the link caps API instead of direct accesses. This allows tracking the state internally within the link caps module. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-22-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 8 +++- .../gpu/drm/i915/display/intel_dp_link_caps.c | 38 ++++++++++++++++++- .../gpu/drm/i915/display/intel_dp_link_caps.h | 3 ++ .../drm/i915/display/intel_dp_link_training.c | 8 +++- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c06d575ebd32..c132225ed61e 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -357,15 +357,17 @@ static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp) int intel_dp_max_lane_count(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; struct intel_dp_link_config forced_params; int lane_count; + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); intel_dp_link_caps_get_forced_params(link_caps, &forced_params); if (forced_params.lane_count) lane_count = forced_params.lane_count; else - lane_count = intel_dp->link.max_lane_count; + lane_count = max_link_limits.lane_count; switch (lane_count) { case 1: @@ -1539,6 +1541,7 @@ int intel_dp_max_link_rate(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; struct intel_dp_link_config forced_params; int len; @@ -1547,7 +1550,8 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp) if (forced_params.rate) return forced_params.rate; - len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate); + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); + len = intel_dp_common_len_rate_limit(intel_dp, max_link_limits.rate); return intel_dp_common_rate(intel_dp, len - 1); } diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index b227c9a55f63..fa7dabc94ddf 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -151,6 +151,36 @@ static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_ent return 1 << lc->lane_count_exp; } +/** + * intel_dp_link_caps_get_max_limits - get the current maximum link limits + * @link_caps: link capabilities state + * @max_link_limits: returned maximum link limits + * + * Return the current maximum rate and lane count limits in + * @max_link_limits. + * + * These limits constrain the set of allowed configurations. + * + * The limits are set to the maximum common supported values after + * intel_dp_link_caps_reset() is called, and can later be modified by + * intel_dp_link_caps_set_max_limits(). The max rate and lane count + * parameters are independent limits, so the pair does not necessarily + * define a valid configuration. + * + * This function may be called without serializing against updates to + * @link_caps. However, without such serialization the returned value may be + * an out-of-sync (link rate, lane count) tuple, i.e. the parameters may + * belong to different update snapshots in time. + */ +void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_link_limits) +{ + struct intel_dp *intel_dp = link_caps->dp; + + max_link_limits->rate = intel_dp->link.max_rate; + max_link_limits->lane_count = intel_dp->link.max_lane_count; +} + static int intel_dp_link_config_bw(struct intel_dp *intel_dp, const struct intel_dp_link_config_entry *lc) { @@ -482,6 +512,7 @@ static int i915_dp_max_link_rate_show(void *data, u64 *val) struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_config max_link_limits; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -490,7 +521,8 @@ static int i915_dp_max_link_rate_show(void *data, u64 *val) intel_dp_flush_connector_commits(connector); - *val = intel_dp->link.max_rate; + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); + *val = max_link_limits.rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -503,6 +535,7 @@ static int i915_dp_max_lane_count_show(void *data, u64 *val) struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_config max_link_limits; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); @@ -511,7 +544,8 @@ static int i915_dp_max_lane_count_show(void *data, u64 *val) intel_dp_flush_connector_commits(connector); - *val = intel_dp->link.max_lane_count; + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); + *val = max_link_limits.lane_count; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 9218cb5de2c7..376dbd9bd5ab 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -27,6 +27,9 @@ void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); +void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_link_limits); + bool intel_dp_link_caps_update(struct intel_dp *intel_dp, const int *rates, int num_rates, int max_lane_count); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index ec9bd9b4c800..7145f2d0ad6d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -2389,6 +2389,8 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, u8 lane_count) { + struct intel_dp_link_config max_link_limits; + /* * FIXME: we need to synchronize the current link parameters with * hardware readout. Currently fast link training doesn't work on @@ -2411,12 +2413,14 @@ bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, * configuration. Although that happens to be true for now, it will * stop being guaranteed once fallback depends only on disabled configs. */ + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); + if (link_rate == 0 || - link_rate > intel_dp->link.max_rate) + link_rate > max_link_limits.rate) return false; if (lane_count == 0 || - lane_count > intel_dp_max_lane_count(intel_dp)) + lane_count > max_link_limits.lane_count) return false; return true; From 17659f83926c0bd8938df72392ff45a568a38b47 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:42 +0300 Subject: [PATCH 157/186] drm/i915/dp_link_caps: Add helpers to set max link limits Add intel_dp_link_caps_set_max_limits() to set the current maximum link limits (max bound over all allowed configurations) through the link caps API instead of direct accesses. This allows tracking the state internally within the link caps module. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-23-imre.deak@intel.com --- .../gpu/drm/i915/display/intel_dp_link_caps.c | 35 +++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 2 ++ .../drm/i915/display/intel_dp_link_training.c | 9 +++-- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index fa7dabc94ddf..e568f00720d3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -151,6 +151,15 @@ static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_ent return 1 << lc->lane_count_exp; } +static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + struct intel_dp *intel_dp = link_caps->dp; + + intel_dp->link.max_rate = max_link_limits->rate; + intel_dp->link.max_lane_count = max_link_limits->lane_count; +} + /** * intel_dp_link_caps_get_max_limits - get the current maximum link limits * @link_caps: link capabilities state @@ -181,6 +190,32 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, max_link_limits->lane_count = intel_dp->link.max_lane_count; } +/** + * intel_dp_link_caps_set_max_limits - set the current maximum link limits + * @link_caps: link capabilities state + * @max_link_limits: new maximum link limits + * + * Set the current maximum rate and lane count limits to @max_link_limits, + * constraining the set of allowed configurations. + * + * Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize + * this call against concurrent queries and updates to @link_caps, in line + * with the rest of the API. + * + * Return: + * - %true if the @link_caps cached max limits value got updated with + * @max_link_limits. + * - %false if @max_link_limits is invalid. + */ +bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + set_max_link_limits_no_update(link_caps, max_link_limits); + + /* TODO: validate max_link_limits */ + return true; +} + static int intel_dp_link_config_bw(struct intel_dp *intel_dp, const struct intel_dp_link_config_entry *lc) { diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 376dbd9bd5ab..c6c60b788887 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -29,6 +29,8 @@ void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits); +bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits); bool intel_dp_link_caps_update(struct intel_dp *intel_dp, const int *rates, int num_rates, int max_lane_count); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 7145f2d0ad6d..9d9911ebad43 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1958,6 +1958,8 @@ static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crt static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; int new_link_rate; int new_lane_count; @@ -1983,8 +1985,11 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, crtc_state->lane_count, crtc_state->port_clock, new_lane_count, new_link_rate); - intel_dp->link.max_rate = new_link_rate; - intel_dp->link.max_lane_count = new_lane_count; + max_link_limits.rate = new_link_rate; + max_link_limits.lane_count = new_lane_count; + + /* TODO: handle an update failure */ + intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits); return 0; } From 13b1b0b98b82ddaefd899a29318bc745acaf433c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:43 +0300 Subject: [PATCH 158/186] drm/i915/dp_link_caps: Add helper to reset max link limits Add a helper to reset the link_caps::max_limits max link limits to the maximum common supported rate and lane count. This is needed by a follow-up change in the link training fallback code, which temporarily resets max_limits before searching for a fallback configuration. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-24-imre.deak@intel.com --- .../gpu/drm/i915/display/intel_dp_link_caps.c | 22 +++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 1 + 2 files changed, 23 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index e568f00720d3..ae10200bdd93 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -160,6 +160,16 @@ static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, intel_dp->link.max_lane_count = max_link_limits->lane_count; } +static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps) +{ + struct intel_dp_link_config max_link_limits = { + .rate = intel_dp_max_common_rate(link_caps->dp), + .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps), + }; + + set_max_link_limits_no_update(link_caps, &max_link_limits); +} + /** * intel_dp_link_caps_get_max_limits - get the current maximum link limits * @link_caps: link capabilities state @@ -216,6 +226,18 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, return true; } +/** + * intel_dp_link_caps_reset_max_limits - reset the current maximum link limits + * @link_caps: link capabilities state + * + * Reset the current maximum link limits to the maximum supported common link + * rate and lane count. + */ +void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) +{ + reset_max_link_limits_no_update(link_caps); +} + static int intel_dp_link_config_bw(struct intel_dp *intel_dp, const struct intel_dp_link_config_entry *lc) { diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index c6c60b788887..7baeb4359d2d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -31,6 +31,7 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits); bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits); +void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps); bool intel_dp_link_caps_update(struct intel_dp *intel_dp, const int *rates, int num_rates, int max_lane_count); From cd0d255747211963b358f0bf414b951ed12166ee Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:44 +0300 Subject: [PATCH 159/186] drm/i915/dp_link_caps: Add helper to reset link_caps state Add a helper to reset the link_caps state, removing all restrictions except user-forced parameters, re-allowing all supported configurations. Currently this only resets the maximum link limits, but follow-up changes will also re-enable configurations previously disabled on a per-configuration basis by fallback or other logic. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-25-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 3 +-- .../gpu/drm/i915/display/intel_dp_link_caps.c | 18 ++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c132225ed61e..c0976e07e703 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3610,8 +3610,7 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, void intel_dp_reset_link_params(struct intel_dp *intel_dp) { - intel_dp->link.max_lane_count = intel_dp_link_caps_max_common_lane_count(intel_dp->link.caps); - intel_dp->link.max_rate = intel_dp_max_common_rate(intel_dp); + intel_dp_link_caps_reset(intel_dp->link.caps); intel_dp->link.mst_probed_lane_count = 0; intel_dp->link.mst_probed_rate = 0; intel_dp_link_training_reset(intel_dp->link.training); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index ae10200bdd93..9b7da5a64ee2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -351,6 +351,24 @@ int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lan return -1; } +/** + * intel_dp_link_caps_reset - reset link capability restrictions + * @link_caps: link capabilities state + * + * Reset all current restrictions except for the user requested forced + * parameters, thus updating the set of allowed configurations and the + * derived maximum link information accordingly. + * + * This function is regularly called after a sink is connected, either + * for the first time to the connector or after a previous sink was + * disconnected from it, and intel_dp_link_caps_update() was called. + */ +void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps) +{ + /* TODO: Update the maximum link information. */ + reset_max_link_limits_no_update(link_caps); +} + static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 7baeb4359d2d..fa45a4672305 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -35,6 +35,7 @@ void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps); bool intel_dp_link_caps_update(struct intel_dp *intel_dp, const int *rates, int num_rates, int max_lane_count); +void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps); void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); From ba4680dece956cd576ba827432f97428c9e926b7 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:45 +0300 Subject: [PATCH 160/186] drm/i915/dp_link_caps: Move max link limits to link_caps Now that all users access the max link limits via helpers, move tracking of these limits from struct intel_dp to the link_caps state. Reviewed-by: Nemesa Garg Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-26-imre.deak@intel.com --- .../drm/i915/display/intel_display_types.h | 4 ---- .../gpu/drm/i915/display/intel_dp_link_caps.c | 21 ++++++++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index e5492996b9e4..c048da7d6fea 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1839,10 +1839,6 @@ struct intel_dp { struct { /* TODO: move the rest of link specific fields to here */ bool active; - /* Max lane count for the current link */ - int max_lane_count; - /* Max rate for the current link */ - int max_rate; /* * Link parameters for which the MST topology was probed. * Tracking these ensures that the MST path resources are diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 9b7da5a64ee2..43427e7cf422 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -46,6 +46,17 @@ struct intel_dp_link_caps { * disconnects. */ struct intel_dp_link_config forced_params; + + /* + * User set maximum limits. These limits constrain the currently + * allowed set of configurations and are not adjusted when sink + * capabilities change. + * + * max_limits.rate/lane_count may come from different allowed + * configurations, i.e. the (max_limits.rate, max_limits.lane_count) + * tuple itself may not be an allowed configuration. + */ + struct intel_dp_link_config max_limits; }; /* Get length of common rates array potentially limited by max_rate. */ @@ -154,10 +165,7 @@ static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_ent static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits) { - struct intel_dp *intel_dp = link_caps->dp; - - intel_dp->link.max_rate = max_link_limits->rate; - intel_dp->link.max_lane_count = max_link_limits->lane_count; + link_caps->max_limits = *max_link_limits; } static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps) @@ -194,10 +202,7 @@ static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits) { - struct intel_dp *intel_dp = link_caps->dp; - - max_link_limits->rate = intel_dp->link.max_rate; - max_link_limits->lane_count = intel_dp->link.max_lane_count; + *max_link_limits = link_caps->max_limits; } /** From 24e7de994bbe3fb4ac638865677ab23b349a1cbc Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:46 +0300 Subject: [PATCH 161/186] drm/i915/dp_link_caps: Pass link_caps to static functions Pass the link_caps pointer to static functions in intel_dp_link_caps.c, as it holds the state with the relevant information. Reviewed-by: Nemesa Garg Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-27-imre.deak@intel.com --- .../gpu/drm/i915/display/intel_dp_link_caps.c | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 43427e7cf422..fc1061149ef2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -118,10 +118,8 @@ int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_cap return link_caps->max_lane_count; } -static int forced_lane_count(struct intel_dp *intel_dp) +static int forced_lane_count(struct intel_dp_link_caps *link_caps) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - if (!link_caps->forced_params.lane_count) return 0; @@ -129,9 +127,9 @@ static int forced_lane_count(struct intel_dp *intel_dp) 1, intel_dp_link_caps_max_common_lane_count(link_caps)); } -static int forced_link_rate(struct intel_dp *intel_dp) +static int forced_link_rate(struct intel_dp_link_caps *link_caps) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp *intel_dp = link_caps->dp; int len; if (!link_caps->forced_params.rate) @@ -147,14 +145,14 @@ static int forced_link_rate(struct intel_dp *intel_dp) void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params) { - forced_params->rate = forced_link_rate(link_caps->dp); - forced_params->lane_count = forced_lane_count(link_caps->dp); + forced_params->rate = forced_link_rate(link_caps); + forced_params->lane_count = forced_lane_count(link_caps); } -static int intel_dp_link_config_rate(struct intel_dp *intel_dp, +static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config_entry *lc) { - return intel_dp_common_rate(intel_dp, lc->link_rate_idx); + return intel_dp_common_rate(link_caps->dp, lc->link_rate_idx); } static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lc) @@ -243,26 +241,28 @@ void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) reset_max_link_limits_no_update(link_caps); } -static int intel_dp_link_config_bw(struct intel_dp *intel_dp, +static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config_entry *lc) { - return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc), + return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lc), intel_dp_link_config_lane_count(lc)); } static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) { struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + const struct intel_dp_link_config_entry *lc_a = a; const struct intel_dp_link_config_entry *lc_b = b; - int bw_a = intel_dp_link_config_bw(intel_dp, lc_a); - int bw_b = intel_dp_link_config_bw(intel_dp, lc_b); + int bw_a = intel_dp_link_config_bw(link_caps, lc_a); + int bw_b = intel_dp_link_config_bw(link_caps, lc_b); if (bw_a != bw_b) return bw_a - bw_b; - return intel_dp_link_config_rate(intel_dp, lc_a) - - intel_dp_link_config_rate(intel_dp, lc_b); + return intel_dp_link_config_rate(link_caps, lc_a) - + intel_dp_link_config_rate(link_caps, lc_b); } /* Return %true if the supported link parameters have changed. */ @@ -333,7 +333,7 @@ void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate lc = &link_caps->configs[idx]; - *link_rate = intel_dp_link_config_rate(intel_dp, lc); + *link_rate = intel_dp_link_config_rate(link_caps, lc); *lane_count = intel_dp_link_config_lane_count(lc); } @@ -414,8 +414,9 @@ static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) return 0; } -static int parse_link_rate(struct intel_dp *intel_dp, const char __user *ubuf, size_t len) +static int parse_link_rate(struct intel_dp_link_caps *link_caps, const char __user *ubuf, size_t len) { + struct intel_dp *intel_dp = link_caps->dp; char *kbuf; const char *p; int rate; @@ -458,7 +459,7 @@ static ssize_t i915_dp_force_link_rate_write(struct file *file, int rate; int err; - rate = parse_link_rate(intel_dp, ubuf, len); + rate = parse_link_rate(link_caps, ubuf, len); if (rate < 0) return rate; From 3aa9d374255a441825dffd2119dc19715aab03c6 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:47 +0300 Subject: [PATCH 162/186] drm/i915/dp_link_caps: Pass link_caps to config update/lookup helpers Pass the link_caps pointer to the update/lookup helpers in intel_dp_link_caps.c, as it holds the state with the relevant information. Reviewed-by: Nemesa Garg Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-28-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 2 +- drivers/gpu/drm/i915/display/intel_dp_link_caps.c | 14 +++++++------- drivers/gpu/drm/i915/display/intel_dp_link_caps.h | 8 +++++--- .../gpu/drm/i915/display/intel_dp_link_training.c | 5 +++-- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c0976e07e703..e1639e8406d5 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -699,7 +699,7 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) bool params_changed = false; intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); - if (intel_dp_link_caps_update(intel_dp, + if (intel_dp_link_caps_update(intel_dp->link.caps, common_rates, num_common_rates, intel_dp_get_max_common_lane_count(intel_dp))) params_changed = true; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index fc1061149ef2..c5701f02fbf6 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -266,10 +266,10 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) } /* Return %true if the supported link parameters have changed. */ -bool intel_dp_link_caps_update(struct intel_dp *intel_dp, +bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, const int *rates, int num_rates, int max_lane_count) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp *intel_dp = link_caps->dp; struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_config_entry *lc; bool link_params_changed = false; @@ -322,10 +322,10 @@ bool intel_dp_link_caps_update(struct intel_dp *intel_dp, return link_params_changed; } -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) +void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, + int idx, int *link_rate, int *lane_count) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_display *display = to_intel_display(intel_dp); + struct intel_display *display = to_intel_display(link_caps->dp); const struct intel_dp_link_config_entry *lc; if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs)) @@ -337,9 +337,9 @@ void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate *lane_count = intel_dp_link_config_lane_count(lc); } -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count) +int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, + int link_rate, int lane_count) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates, link_rate); int lane_count_exp = ilog2(lane_count); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index fa45a4672305..9256f02fed11 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -24,8 +24,10 @@ void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params); -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); +int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, + int link_rate, int lane_count); +void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, + int idx, int *link_rate, int *lane_count); void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits); @@ -33,7 +35,7 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits); void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps); -bool intel_dp_link_caps_update(struct intel_dp *intel_dp, +bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, const int *rates, int num_rates, int max_lane_count); void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 9d9911ebad43..0d4a0bf1dac5 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1858,9 +1858,10 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - i = intel_dp_link_config_index(intel_dp, crtc_state->port_clock, crtc_state->lane_count); + i = intel_dp_link_config_index(intel_dp->link.caps, + crtc_state->port_clock, crtc_state->lane_count); for (i--; i >= 0; i--) { - intel_dp_link_config_get(intel_dp, i, &link_rate, &lane_count); + intel_dp_link_config_get(intel_dp->link.caps, i, &link_rate, &lane_count); if ((forced_params.rate && forced_params.rate != link_rate) || From 9e6bb4b2416ef7594560bfddcb3a494842e3b17a Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 16 Jun 2026 23:08:48 +0300 Subject: [PATCH 163/186] drm/i915/dp_link_caps: Pass link_caps to common rate helpers Pass the link_caps pointer to the common rate helpers in intel_dp_link_caps.c, as it holds the state with the relevant information. Reviewed-by: Nemesa Garg Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260616200849.3534628-29-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 13 ++++++---- .../gpu/drm/i915/display/intel_dp_link_caps.c | 26 +++++++------------ .../gpu/drm/i915/display/intel_dp_link_caps.h | 6 ++--- .../drm/i915/display/intel_dp_link_training.c | 5 ++-- .../gpu/drm/i915/display/intel_dp_tunnel.c | 2 +- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index e1639e8406d5..50819e633f24 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1551,14 +1551,15 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp) return forced_params.rate; intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); - len = intel_dp_common_len_rate_limit(intel_dp, max_link_limits.rate); + len = intel_dp_common_len_rate_limit(link_caps, max_link_limits.rate); - return intel_dp_common_rate(intel_dp, len - 1); + return intel_dp_common_rate(link_caps, len - 1); } static int intel_dp_min_link_rate(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_dp_link_config forced_params; intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); @@ -1566,7 +1567,7 @@ intel_dp_min_link_rate(struct intel_dp *intel_dp) if (forced_params.rate) return forced_params.rate; - return intel_dp_common_rate(intel_dp, 0); + return intel_dp_common_rate(link_caps, 0); } int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) @@ -1750,6 +1751,7 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state, const struct link_config_limits *limits) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state); int link_rate, link_avail; @@ -1760,7 +1762,7 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp); for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { - link_rate = intel_dp_common_rate(intel_dp, i); + link_rate = intel_dp_common_rate(link_caps, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) continue; @@ -1984,12 +1986,13 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, const struct link_config_limits *limits, int dsc_bpp_x16) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; int link_rate, lane_count; int i; for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { - link_rate = intel_dp_common_rate(intel_dp, i); + link_rate = intel_dp_common_rate(link_caps, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) continue; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index c5701f02fbf6..0917e7f51a26 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -60,19 +60,16 @@ struct intel_dp_link_caps { }; /* Get length of common rates array potentially limited by max_rate. */ -int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, +int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, int max_rate) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - return intel_dp_rate_limit_len(link_caps->rates, link_caps->num_rates, max_rate); } -int intel_dp_common_rate(struct intel_dp *intel_dp, int index) +int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_display *display = to_intel_display(intel_dp); + struct intel_display *display = to_intel_display(link_caps->dp); if (drm_WARN_ON(display->drm, index < 0 || index >= link_caps->num_rates)) @@ -89,11 +86,9 @@ int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int } /* Theoretical max between source and sink */ -int intel_dp_max_common_rate(struct intel_dp *intel_dp) +int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - - return intel_dp_common_rate(intel_dp, link_caps->num_rates - 1); + return intel_dp_common_rate(link_caps, link_caps->num_rates - 1); } int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps) @@ -129,17 +124,16 @@ static int forced_lane_count(struct intel_dp_link_caps *link_caps) static int forced_link_rate(struct intel_dp_link_caps *link_caps) { - struct intel_dp *intel_dp = link_caps->dp; int len; if (!link_caps->forced_params.rate) return 0; - len = intel_dp_common_len_rate_limit(intel_dp, link_caps->forced_params.rate); + len = intel_dp_common_len_rate_limit(link_caps, link_caps->forced_params.rate); if (len == 0) - return intel_dp_common_rate(intel_dp, 0); + return intel_dp_common_rate(link_caps, 0); - return intel_dp_common_rate(intel_dp, len - 1); + return intel_dp_common_rate(link_caps, len - 1); } void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, @@ -152,7 +146,7 @@ void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config_entry *lc) { - return intel_dp_common_rate(link_caps->dp, lc->link_rate_idx); + return intel_dp_common_rate(link_caps, lc->link_rate_idx); } static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lc) @@ -169,7 +163,7 @@ static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps) { struct intel_dp_link_config max_link_limits = { - .rate = intel_dp_max_common_rate(link_caps->dp), + .rate = intel_dp_max_common_rate(link_caps), .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps), }; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index 9256f02fed11..af9028e7cb98 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -11,11 +11,11 @@ struct intel_dp; struct intel_dp_link_caps; struct intel_dp_link_config; -int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, +int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, int max_rate); -int intel_dp_common_rate(struct intel_dp *intel_dp, int index); +int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index); int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate); -int intel_dp_max_common_rate(struct intel_dp *intel_dp); +int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps); int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps); diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 0d4a0bf1dac5..b521dd11b62a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1898,7 +1898,7 @@ static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) if (rate_index <= 0) return -1; - new_rate = intel_dp_common_rate(intel_dp, rate_index - 1); + new_rate = intel_dp_common_rate(link_caps, rate_index - 1); /* TODO: Make switching from UHBR to non-UHBR rates work. */ if (drm_dp_is_uhbr_rate(current_rate) != drm_dp_is_uhbr_rate(new_rate)) @@ -1925,6 +1925,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, int *new_link_rate, int *new_lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int link_rate; int lane_count; @@ -1932,7 +1933,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, link_rate = reduce_link_rate(intel_dp, crtc_state->port_clock); if (link_rate < 0) { lane_count = reduce_lane_count(intel_dp, crtc_state->lane_count); - link_rate = intel_dp_max_common_rate(intel_dp); + link_rate = intel_dp_max_common_rate(link_caps); } if (lane_count < 0) diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index 9d9d8d04742b..76e9753766b9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -58,7 +58,7 @@ static int kbytes_to_mbits(int kbytes) static int get_current_link_bw(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int rate = intel_dp_max_common_rate(intel_dp); + int rate = intel_dp_max_common_rate(link_caps); int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps); return intel_dp_max_link_data_rate(intel_dp, rate, lane_count); From ba3b58d2c952aeea1621b2c7f0d60888715cf34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 15:51:06 +0300 Subject: [PATCH 164/186] drm/i915/gmbus: Rename GPIO pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the alphabetical GPIOA,GPIOB,... with numeric GPIO_0,GPIO_1,... This makes the naming scheme agree with BSpec. No idea why the alphabetical naming was originally chosen as BSpec never used that convention for the GPIO pins. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623125111.6632-2-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_gmbus.c | 124 ++++++++++----------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index 049157c41fe2..9990e6391b03 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -54,21 +54,21 @@ struct intel_gmbus { }; enum gmbus_gpio { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, - GPIOH, - __GPIOI_UNUSED, - GPIOJ, - GPIOK, - GPIOL, - GPIOM, - GPION, - GPIOO, + GPIO_0, + GPIO_1, + GPIO_2, + GPIO_3, + GPIO_4, + GPIO_5, + GPIO_6, + GPIO_7, + GPIO_8, + GPIO_9, + GPIO_10, + GPIO_11, + GPIO_12, + GPIO_13, + GPIO_14, }; struct gmbus_pin { @@ -78,77 +78,77 @@ struct gmbus_pin { /* Map gmbus pin pairs to names and registers. */ static const struct gmbus_pin gmbus_pins[] = { - [GMBUS_PIN_SSC] = { "ssc", GPIOB }, - [GMBUS_PIN_VGADDC] = { "vga", GPIOA }, - [GMBUS_PIN_PANEL] = { "panel", GPIOC }, - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, + [GMBUS_PIN_SSC] = { "ssc", GPIO_1 }, + [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, + [GMBUS_PIN_PANEL] = { "panel", GPIO_2 }, + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; static const struct gmbus_pin gmbus_pins_bdw[] = { - [GMBUS_PIN_VGADDC] = { "vga", GPIOA }, - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, + [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; static const struct gmbus_pin gmbus_pins_skl[] = { - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; static const struct gmbus_pin gmbus_pins_bxt[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIOC }, - [GMBUS_PIN_3_BXT] = { "misc", GPIOD }, + [GMBUS_PIN_1_BXT] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2_BXT] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3_BXT] = { "misc", GPIO_3 }, }; static const struct gmbus_pin gmbus_pins_cnp[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIOC }, - [GMBUS_PIN_3_BXT] = { "misc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, + [GMBUS_PIN_1_BXT] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2_BXT] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3_BXT] = { "misc", GPIO_3 }, + [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_icp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM }, - [GMBUS_PIN_13_TC5_TGP] = { "tc5", GPION }, - [GMBUS_PIN_14_TC6_TGP] = { "tc6", GPIOO }, + [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, + [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIO_12 }, + [GMBUS_PIN_13_TC5_TGP] = { "tc5", GPIO_13 }, + [GMBUS_PIN_14_TC6_TGP] = { "tc6", GPIO_14 }, }; static const struct gmbus_pin gmbus_pins_dg1[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, + [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_dg2[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, + [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, + [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIO_9 }, }; static const struct gmbus_pin gmbus_pins_mtp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, - [GMBUS_PIN_5_MTP] = { "dpe", GPIOF }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM }, + [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, + [GMBUS_PIN_5_MTP] = { "dpe", GPIO_5 }, + [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIO_12 }, }; static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, From ace377427754e5a81471ab171051b5acefc61789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 15:51:07 +0300 Subject: [PATCH 165/186] drm/i915/gmbus: s/gmbus_pins_bdw/gmbus_pins_lpt/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GMBUS pin pair <-> GPIO mapping is purely a property of the PCH (on the platforms where GMBUS lives in the PCH). So rename gmbus_pins_bdw[] to gmbus_pins_lpt[] and extend it to cover all platforms with LPT/WPT PCHs. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623125111.6632-3-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_gmbus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index 9990e6391b03..2869ec23e1ec 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -86,7 +86,7 @@ static const struct gmbus_pin gmbus_pins[] = { [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; -static const struct gmbus_pin gmbus_pins_bdw[] = { +static const struct gmbus_pin gmbus_pins_lpt[] = { [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, @@ -178,9 +178,9 @@ static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, } else if (DISPLAY_VER(display) == 9) { pins = gmbus_pins_skl; size = ARRAY_SIZE(gmbus_pins_skl); - } else if (display->platform.broadwell) { - pins = gmbus_pins_bdw; - size = ARRAY_SIZE(gmbus_pins_bdw); + } else if (HAS_PCH_LPT(display)) { + pins = gmbus_pins_lpt; + size = ARRAY_SIZE(gmbus_pins_lpt); } else { pins = gmbus_pins; size = ARRAY_SIZE(gmbus_pins); From 1c626c95b4bf4200f1d17817f7cc98a272c7a604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 15:51:08 +0300 Subject: [PATCH 166/186] drm/i915/gmbus: Add gmbus_pins_lpt_lp[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LPT/WPT-LP don't have as many GPIO pins as the -H variants. Add proper mapping for the -LP PCHs so that we can't end up poking at non-existent GPIOs. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623125111.6632-4-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_gmbus.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index 2869ec23e1ec..f69b8841c5dd 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -86,13 +86,18 @@ static const struct gmbus_pin gmbus_pins[] = { [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; -static const struct gmbus_pin gmbus_pins_lpt[] = { +static const struct gmbus_pin gmbus_pins_lpt_h[] = { [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; +static const struct gmbus_pin gmbus_pins_lpt_lp[] = { + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, +}; + static const struct gmbus_pin gmbus_pins_skl[] = { [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, @@ -178,9 +183,12 @@ static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, } else if (DISPLAY_VER(display) == 9) { pins = gmbus_pins_skl; size = ARRAY_SIZE(gmbus_pins_skl); + } else if (HAS_PCH_LPT_LP(display)) { + pins = gmbus_pins_lpt_lp; + size = ARRAY_SIZE(gmbus_pins_lpt_lp); } else if (HAS_PCH_LPT(display)) { - pins = gmbus_pins_lpt; - size = ARRAY_SIZE(gmbus_pins_lpt); + pins = gmbus_pins_lpt_h; + size = ARRAY_SIZE(gmbus_pins_lpt_h); } else { pins = gmbus_pins; size = ARRAY_SIZE(gmbus_pins); From 85b4ba03d54933056451c9c873cbe16173aeba27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 15:51:09 +0300 Subject: [PATCH 167/186] drm/i915/gmbus: s/gmbus_pins_skl/gmbus_pins_spt/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GMBUS pin pair <-> GPIO mapping is purely a property of the PCH (on the platforms where GMBUS lives in the PCH). So rename gmbus_pins_skl[] to gmbus_pins_spt[] and apply it based on the presence of the correct PCH type. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623125111.6632-5-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_gmbus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index f69b8841c5dd..dec0f66f756f 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -98,7 +98,7 @@ static const struct gmbus_pin gmbus_pins_lpt_lp[] = { [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, }; -static const struct gmbus_pin gmbus_pins_skl[] = { +static const struct gmbus_pin gmbus_pins_spt[] = { [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, @@ -180,9 +180,9 @@ static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, } else if (display->platform.geminilake || display->platform.broxton) { pins = gmbus_pins_bxt; size = ARRAY_SIZE(gmbus_pins_bxt); - } else if (DISPLAY_VER(display) == 9) { - pins = gmbus_pins_skl; - size = ARRAY_SIZE(gmbus_pins_skl); + } else if (HAS_PCH_SPT(display)) { + pins = gmbus_pins_spt; + size = ARRAY_SIZE(gmbus_pins_spt); } else if (HAS_PCH_LPT_LP(display)) { pins = gmbus_pins_lpt_lp; size = ARRAY_SIZE(gmbus_pins_lpt_lp); From 4c18cc665ea311837f7ce134a7681bc03ee50e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 17:11:17 +0300 Subject: [PATCH 168/186] drm/i915/gmbus: Drop the platform suffixes from GMBUS pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modern GMBUS pin numbers are just a sequential set of numbers, so the platform suffixes don't really buy us anything. Let's just drop them. We'll keep the _TCx suffixes for pins 9+ since that's the way they always get used. v2: Deal with gvt Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623141117.24115-1-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_bios.c | 62 ++++++++++---------- drivers/gpu/drm/i915/display/intel_gmbus.c | 68 +++++++++++----------- drivers/gpu/drm/i915/display/intel_gmbus.h | 22 +++---- drivers/gpu/drm/i915/display/intel_hdmi.c | 42 ++++++------- drivers/gpu/drm/i915/gvt/edid.c | 14 ++--- 5 files changed, 104 insertions(+), 104 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index b6fe87c29aa7..15ebadc72b88 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -2197,52 +2197,52 @@ static u8 translate_iboost(struct intel_display *display, u8 val) static const u8 cnp_ddc_pin_map[] = { [0] = 0, /* N/A */ - [GMBUS_PIN_1_BXT] = DDC_BUS_DDI_B, - [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_C, - [GMBUS_PIN_4_CNP] = DDC_BUS_DDI_D, /* sic */ - [GMBUS_PIN_3_BXT] = DDC_BUS_DDI_F, /* sic */ + [GMBUS_PIN_1] = DDC_BUS_DDI_B, + [GMBUS_PIN_2] = DDC_BUS_DDI_C, + [GMBUS_PIN_4] = DDC_BUS_DDI_D, /* sic */ + [GMBUS_PIN_3] = DDC_BUS_DDI_F, /* sic */ }; static const u8 icp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_3_BXT] = TGL_DDC_BUS_DDI_C, - [GMBUS_PIN_9_TC1_ICP] = ICL_DDC_BUS_PORT_1, - [GMBUS_PIN_10_TC2_ICP] = ICL_DDC_BUS_PORT_2, - [GMBUS_PIN_11_TC3_ICP] = ICL_DDC_BUS_PORT_3, - [GMBUS_PIN_12_TC4_ICP] = ICL_DDC_BUS_PORT_4, - [GMBUS_PIN_13_TC5_TGP] = TGL_DDC_BUS_PORT_5, - [GMBUS_PIN_14_TC6_TGP] = TGL_DDC_BUS_PORT_6, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_3] = TGL_DDC_BUS_DDI_C, + [GMBUS_PIN_9_TC1] = ICL_DDC_BUS_PORT_1, + [GMBUS_PIN_10_TC2] = ICL_DDC_BUS_PORT_2, + [GMBUS_PIN_11_TC3] = ICL_DDC_BUS_PORT_3, + [GMBUS_PIN_12_TC4] = ICL_DDC_BUS_PORT_4, + [GMBUS_PIN_13_TC5] = TGL_DDC_BUS_PORT_5, + [GMBUS_PIN_14_TC6] = TGL_DDC_BUS_PORT_6, }; static const u8 rkl_pch_tgp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = RKL_DDC_BUS_DDI_D, - [GMBUS_PIN_10_TC2_ICP] = RKL_DDC_BUS_DDI_E, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = RKL_DDC_BUS_DDI_D, + [GMBUS_PIN_10_TC2] = RKL_DDC_BUS_DDI_E, }; static const u8 adls_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_9_TC1_ICP] = ADLS_DDC_BUS_PORT_TC1, - [GMBUS_PIN_10_TC2_ICP] = ADLS_DDC_BUS_PORT_TC2, - [GMBUS_PIN_11_TC3_ICP] = ADLS_DDC_BUS_PORT_TC3, - [GMBUS_PIN_12_TC4_ICP] = ADLS_DDC_BUS_PORT_TC4, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_9_TC1] = ADLS_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2] = ADLS_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3] = ADLS_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4] = ADLS_DDC_BUS_PORT_TC4, }; static const u8 gen9bc_tgp_ddc_pin_map[] = { - [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = DDC_BUS_DDI_C, - [GMBUS_PIN_10_TC2_ICP] = DDC_BUS_DDI_D, + [GMBUS_PIN_2] = DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = DDC_BUS_DDI_C, + [GMBUS_PIN_10_TC2] = DDC_BUS_DDI_D, }; static const u8 adlp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = ADLP_DDC_BUS_PORT_TC1, - [GMBUS_PIN_10_TC2_ICP] = ADLP_DDC_BUS_PORT_TC2, - [GMBUS_PIN_11_TC3_ICP] = ADLP_DDC_BUS_PORT_TC3, - [GMBUS_PIN_12_TC4_ICP] = ADLP_DDC_BUS_PORT_TC4, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = ADLP_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2] = ADLP_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3] = ADLP_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4] = ADLP_DDC_BUS_PORT_TC4, }; static u8 map_ddc_pin(struct intel_display *display, u8 vbt_pin) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index dec0f66f756f..60a70dea5d85 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -105,55 +105,55 @@ static const struct gmbus_pin gmbus_pins_spt[] = { }; static const struct gmbus_pin gmbus_pins_bxt[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIO_1 }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIO_2 }, - [GMBUS_PIN_3_BXT] = { "misc", GPIO_3 }, + [GMBUS_PIN_1] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3] = { "misc", GPIO_3 }, }; static const struct gmbus_pin gmbus_pins_cnp[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIO_1 }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIO_2 }, - [GMBUS_PIN_3_BXT] = { "misc", GPIO_3 }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, + [GMBUS_PIN_1] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3] = { "misc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_icp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIO_9 }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIO_10 }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIO_11 }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIO_12 }, - [GMBUS_PIN_13_TC5_TGP] = { "tc5", GPIO_13 }, - [GMBUS_PIN_14_TC6_TGP] = { "tc6", GPIO_14 }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 }, + [GMBUS_PIN_13_TC5] = { "tc5", GPIO_13 }, + [GMBUS_PIN_14_TC6] = { "tc6", GPIO_14 }, }; static const struct gmbus_pin gmbus_pins_dg1[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_dg2[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIO_9 }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, }; static const struct gmbus_pin gmbus_pins_mtp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIO_1 }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIO_2 }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIO_3 }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIO_4 }, - [GMBUS_PIN_5_MTP] = { "dpe", GPIO_5 }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIO_9 }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIO_10 }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIO_11 }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIO_12 }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, + [GMBUS_PIN_5] = { "dpe", GPIO_5 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 }, }; static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.h b/drivers/gpu/drm/i915/display/intel_gmbus.h index 35a200a9efc0..5fdeab1aa794 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.h +++ b/drivers/gpu/drm/i915/display/intel_gmbus.h @@ -20,17 +20,17 @@ struct intel_display; #define GMBUS_PIN_DPB 5 /* SDVO, HDMIB */ #define GMBUS_PIN_DPD 6 /* HDMID */ #define GMBUS_PIN_RESERVED 7 /* 7 reserved */ -#define GMBUS_PIN_1_BXT 1 /* BXT+ (atom) and CNP+ (big core) */ -#define GMBUS_PIN_2_BXT 2 -#define GMBUS_PIN_3_BXT 3 -#define GMBUS_PIN_4_CNP 4 -#define GMBUS_PIN_5_MTP 5 -#define GMBUS_PIN_9_TC1_ICP 9 -#define GMBUS_PIN_10_TC2_ICP 10 -#define GMBUS_PIN_11_TC3_ICP 11 -#define GMBUS_PIN_12_TC4_ICP 12 -#define GMBUS_PIN_13_TC5_TGP 13 -#define GMBUS_PIN_14_TC6_TGP 14 +#define GMBUS_PIN_1 1 /* BXT+ (atom) and CNP+ (big core) */ +#define GMBUS_PIN_2 2 +#define GMBUS_PIN_3 3 +#define GMBUS_PIN_4 4 +#define GMBUS_PIN_5 5 +#define GMBUS_PIN_9_TC1 9 /* ICP+ */ +#define GMBUS_PIN_10_TC2 10 +#define GMBUS_PIN_11_TC3 11 +#define GMBUS_PIN_12_TC4 12 +#define GMBUS_PIN_13_TC5 13 +#define GMBUS_PIN_14_TC6 14 #define GMBUS_NUM_PINS 15 /* including 0 */ diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index beca0ff5a5b4..f046918fd4bc 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2825,14 +2825,14 @@ static u8 bxt_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (port) { case PORT_B: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PORT_C: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; default: MISSING_CASE(port); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2845,20 +2845,20 @@ static u8 cnp_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (port) { case PORT_B: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PORT_C: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; case PORT_D: - ddc_pin = GMBUS_PIN_4_CNP; + ddc_pin = GMBUS_PIN_4; break; case PORT_F: - ddc_pin = GMBUS_PIN_3_BXT; + ddc_pin = GMBUS_PIN_3; break; default: MISSING_CASE(port); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2870,12 +2870,12 @@ static u8 icl_encoder_to_ddc_pin(struct intel_encoder *encoder) enum port port = encoder->port; if (intel_encoder_is_combo(encoder)) - return GMBUS_PIN_1_BXT + port; + return GMBUS_PIN_1 + port; else if (intel_encoder_is_tc(encoder)) - return GMBUS_PIN_9_TC1_ICP + intel_encoder_to_tc(encoder); + return GMBUS_PIN_9_TC1 + intel_encoder_to_tc(encoder); drm_WARN(display->drm, 1, "Unknown port:%c\n", port_name(port)); - return GMBUS_PIN_2_BXT; + return GMBUS_PIN_2; } static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2885,17 +2885,17 @@ static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (phy) { case PHY_A: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PHY_B: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; case PHY_C: - ddc_pin = GMBUS_PIN_9_TC1_ICP; + ddc_pin = GMBUS_PIN_9_TC1; break; default: MISSING_CASE(phy); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2915,9 +2915,9 @@ static u8 rkl_encoder_to_ddc_pin(struct intel_encoder *encoder) * all outputs. */ if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C) - return GMBUS_PIN_9_TC1_ICP + phy - PHY_C; + return GMBUS_PIN_9_TC1 + phy - PHY_C; - return GMBUS_PIN_1_BXT + phy; + return GMBUS_PIN_1 + phy; } static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2934,9 +2934,9 @@ static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder) * all outputs. */ if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C) - return GMBUS_PIN_9_TC1_ICP + phy - PHY_C; + return GMBUS_PIN_9_TC1 + phy - PHY_C; - return GMBUS_PIN_1_BXT + phy; + return GMBUS_PIN_1 + phy; } static u8 dg1_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2955,9 +2955,9 @@ static u8 adls_encoder_to_ddc_pin(struct intel_encoder *encoder) * except first combo output. */ if (phy == PHY_A) - return GMBUS_PIN_1_BXT; + return GMBUS_PIN_1; - return GMBUS_PIN_9_TC1_ICP + phy - PHY_B; + return GMBUS_PIN_9_TC1 + phy - PHY_B; } static u8 g4x_encoder_to_ddc_pin(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index ca5b54466a65..dc9ef98ff51b 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -90,13 +90,13 @@ static inline int cnp_get_port_from_gmbus0(u32 gmbus0) int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; int port = -EINVAL; - if (port_select == GMBUS_PIN_1_BXT) + if (port_select == GMBUS_PIN_1) port = PORT_B; - else if (port_select == GMBUS_PIN_2_BXT) + else if (port_select == GMBUS_PIN_2) port = PORT_C; - else if (port_select == GMBUS_PIN_3_BXT) + else if (port_select == GMBUS_PIN_3) port = PORT_D; - else if (port_select == GMBUS_PIN_4_CNP) + else if (port_select == GMBUS_PIN_4) port = PORT_E; return port; } @@ -106,11 +106,11 @@ static inline int bxt_get_port_from_gmbus0(u32 gmbus0) int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; int port = -EINVAL; - if (port_select == GMBUS_PIN_1_BXT) + if (port_select == GMBUS_PIN_1) port = PORT_B; - else if (port_select == GMBUS_PIN_2_BXT) + else if (port_select == GMBUS_PIN_2) port = PORT_C; - else if (port_select == GMBUS_PIN_3_BXT) + else if (port_select == GMBUS_PIN_3) port = PORT_D; return port; } From 8ba604778974ca18990a14381de62bf1cbec8ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 23 Jun 2026 15:51:11 +0300 Subject: [PATCH 169/186] drm/i915/hdmi: Remove CNP port F leftovers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since CNL got nuked cnp_encoder_to_ddc_pin() will never see a port F. Remove the leftovers. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260623125111.6632-7-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak --- drivers/gpu/drm/i915/display/intel_hdmi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index f046918fd4bc..8a019d3574df 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2853,9 +2853,6 @@ static u8 cnp_encoder_to_ddc_pin(struct intel_encoder *encoder) case PORT_D: ddc_pin = GMBUS_PIN_4; break; - case PORT_F: - ddc_pin = GMBUS_PIN_3; - break; default: MISSING_CASE(port); ddc_pin = GMBUS_PIN_1; From 6929823fc85d15b9a00e8e4f22beacb800fa74d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grzelak?= Date: Wed, 24 Jun 2026 00:46:18 +0200 Subject: [PATCH 170/186] drm/i915/dp_link_caps: s/lc/lce/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lc variable took it's name as an acronym from struct intel_dp_link_config. Rename the variable into lce since the struct was renamed into intel_dp_link_config_entry. Signed-off-by: Michał Grzelak Reviewed-by: Imre Deak Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260623224619.1949975-30-michal.grzelak@intel.com --- .../gpu/drm/i915/display/intel_dp_link_caps.c | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 0917e7f51a26..1c34ba6c49c3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -144,14 +144,14 @@ void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, } static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps, - const struct intel_dp_link_config_entry *lc) + const struct intel_dp_link_config_entry *lce) { - return intel_dp_common_rate(link_caps, lc->link_rate_idx); + return intel_dp_common_rate(link_caps, lce->link_rate_idx); } -static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lc) +static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lce) { - return 1 << lc->lane_count_exp; + return 1 << lce->lane_count_exp; } static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, @@ -236,10 +236,10 @@ void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) } static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps, - const struct intel_dp_link_config_entry *lc) + const struct intel_dp_link_config_entry *lce) { - return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lc), - intel_dp_link_config_lane_count(lc)); + return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lce), + intel_dp_link_config_lane_count(lce)); } static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) @@ -247,16 +247,16 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - const struct intel_dp_link_config_entry *lc_a = a; - const struct intel_dp_link_config_entry *lc_b = b; - int bw_a = intel_dp_link_config_bw(link_caps, lc_a); - int bw_b = intel_dp_link_config_bw(link_caps, lc_b); + const struct intel_dp_link_config_entry *lce_a = a; + const struct intel_dp_link_config_entry *lce_b = b; + int bw_a = intel_dp_link_config_bw(link_caps, lce_a); + int bw_b = intel_dp_link_config_bw(link_caps, lce_b); if (bw_a != bw_b) return bw_a - bw_b; - return intel_dp_link_config_rate(link_caps, lc_a) - - intel_dp_link_config_rate(link_caps, lc_b); + return intel_dp_link_config_rate(link_caps, lce_a) - + intel_dp_link_config_rate(link_caps, lce_b); } /* Return %true if the supported link parameters have changed. */ @@ -265,7 +265,7 @@ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, { struct intel_dp *intel_dp = link_caps->dp; struct intel_display *display = to_intel_display(intel_dp); - struct intel_dp_link_config_entry *lc; + struct intel_dp_link_config_entry *lce; bool link_params_changed = false; int num_common_lane_configs; int i; @@ -298,13 +298,13 @@ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, link_caps->num_configs = num_rates * num_common_lane_configs; - lc = &link_caps->configs[0]; + lce = &link_caps->configs[0]; for (i = 0; i < link_caps->num_rates; i++) { for (j = 0; j < num_common_lane_configs; j++) { - lc->lane_count_exp = j; - lc->link_rate_idx = i; + lce->lane_count_exp = j; + lce->link_rate_idx = i; - lc++; + lce++; } } @@ -320,15 +320,15 @@ void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, int idx, int *link_rate, int *lane_count) { struct intel_display *display = to_intel_display(link_caps->dp); - const struct intel_dp_link_config_entry *lc; + const struct intel_dp_link_config_entry *lce; if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs)) idx = 0; - lc = &link_caps->configs[idx]; + lce = &link_caps->configs[idx]; - *link_rate = intel_dp_link_config_rate(link_caps, lc); - *lane_count = intel_dp_link_config_lane_count(lc); + *link_rate = intel_dp_link_config_rate(link_caps, lce); + *lane_count = intel_dp_link_config_lane_count(lce); } int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, @@ -340,10 +340,10 @@ int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, int i; for (i = 0; i < link_caps->num_configs; i++) { - const struct intel_dp_link_config_entry *lc = &link_caps->configs[i]; + const struct intel_dp_link_config_entry *lce = &link_caps->configs[i]; - if (lc->lane_count_exp == lane_count_exp && - lc->link_rate_idx == link_rate_idx) + if (lce->lane_count_exp == lane_count_exp && + lce->link_rate_idx == link_rate_idx) return i; } From a82af46fd08474c56ca424156b4047f94121ec5e Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 21:47:58 +0300 Subject: [PATCH 171/186] drm/i915: move intel_display_device_probe() call a level higher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having display probe be called from i915_driver_create() is slightly misleading, and an artefact from the past. Move the intel_display_device_probe() call a level higher. Use the shared PCI disable error path while at it. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/c97a8790a5cb1f6b10061286adad8148972c5b3b.1781549229.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_driver.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 43f747c3c31f..09810d5a0ad4 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -802,7 +802,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) const struct intel_device_info *match_info = (struct intel_device_info *)ent->driver_data; struct drm_i915_private *i915; - struct intel_display *display; i915 = devm_drm_dev_alloc(&pdev->dev, &i915_drm_driver, struct drm_i915_private, drm); @@ -817,12 +816,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) /* Set up device info and initial runtime info. */ intel_device_info_driver_create(i915, pdev->device, match_info); - display = intel_display_device_probe(pdev, &parent); - if (IS_ERR(display)) - return ERR_CAST(display); - - i915->display = display; - return i915; } @@ -855,7 +848,13 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return PTR_ERR(i915); } - display = i915->display; + display = intel_display_device_probe(pdev, &parent); + if (IS_ERR(display)) { + ret = PTR_ERR(display); + goto out_pci_disable; + } + + i915->display = display; ret = i915_driver_early_probe(i915); if (ret < 0) From 2420e540785053bcc8bc00d9df5ce2d18bb7e1ca Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 21:47:59 +0300 Subject: [PATCH 172/186] drm/i915: remove superfluous checks for pdev->msi_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_disable_msi() checks for pdev->msi_enabled internally. There's no need to peek at pdev internals in i915. Remove them. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/71f61eee227178b61af9c1211be8545828a3f3ef.1781549229.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_driver.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 09810d5a0ad4..e36566f34960 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -583,8 +583,7 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) err_opregion: intel_opregion_cleanup(display); - if (pdev->msi_enabled) - pci_disable_msi(pdev); + pci_disable_msi(pdev); err_mem_regions: intel_memory_regions_driver_release(dev_priv); err_ggtt: @@ -610,8 +609,7 @@ static void i915_driver_hw_remove(struct drm_i915_private *dev_priv) intel_opregion_cleanup(display); - if (pdev->msi_enabled) - pci_disable_msi(pdev); + pci_disable_msi(pdev); } /** From ff8c73b7bfb54782358bf0ba236e0045adccbb35 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 21:48:00 +0300 Subject: [PATCH 173/186] drm/{i915, xe}: move opregion/dram/bw init to intel_display_driver_probe_noirq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intel_opregion_setup(), intel_dram_detect(), and intel_bw_init_hw() calls should really be in display. Move them at the beginning of intel_display_driver_probe_noirq(). This is a completely non-functional change for xe. For i915, the init order changes slightly: - i915_pcode_init() will happen before intel_opregion_setup(). This should be of no consequence. - The intel_gvt_init() calls will happen before the mentioned functions. There's a lot going on in intel_gvt_init(), but it does not look like this should have dependencies on them either. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/aa37d6443516ae660c2de53aba8ca42e6e6d1a5c.1781549229.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_driver.c | 17 +++++++++++++++- drivers/gpu/drm/i915/i915_driver.c | 20 ++----------------- drivers/gpu/drm/xe/display/xe_display.c | 20 ++----------------- 3 files changed, 20 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 462f78d5b020..15a61f171d73 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -43,6 +43,7 @@ #include "intel_dp_tunnel.h" #include "intel_dpll.h" #include "intel_dpll_mgr.h" +#include "intel_dram.h" #include "intel_encoder.h" #include "intel_fb.h" #include "intel_fbc.h" @@ -203,11 +204,23 @@ int intel_display_driver_probe_noirq(struct intel_display *display) { int ret; + intel_opregion_setup(display); + + /* + * Fill the dram structure to get the system dram info. This will be + * used for memory latency calculation. + */ + ret = intel_dram_detect(display); + if (ret) + goto cleanup_opregion; + + intel_bw_init_hw(display); + if (HAS_DISPLAY(display)) { ret = drm_vblank_init(display->drm, INTEL_NUM_PIPES(display)); if (ret) - return ret; + goto cleanup_opregion; } intel_bios_init(display); @@ -306,6 +319,8 @@ int intel_display_driver_probe_noirq(struct intel_display *display) intel_display_power_driver_remove(display); cleanup_bios: intel_bios_driver_remove(display); +cleanup_opregion: + intel_opregion_cleanup(display); return ret; } diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index e36566f34960..4b588364ffb1 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -51,7 +51,6 @@ #include #include "display/i9xx_display_sr.h" -#include "display/intel_bw.h" #include "display/intel_cdclk.h" #include "display/intel_crtc.h" #include "display/intel_display_device.h" @@ -60,7 +59,6 @@ #include "display/intel_dmc.h" #include "display/intel_dp.h" #include "display/intel_dpt.h" -#include "display/intel_dram.h" #include "display/intel_fbdev.h" #include "display/intel_gmbus.h" #include "display/intel_hotplug.h" @@ -469,7 +467,6 @@ static int i915_pcode_init(struct drm_i915_private *i915) */ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) { - struct intel_display *display = dev_priv->display; struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); int ret; @@ -563,26 +560,13 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) drm_dbg(&dev_priv->drm, "can't enable MSI"); } - intel_opregion_setup(display); - ret = i915_pcode_init(dev_priv); if (ret) - goto err_opregion; - - /* - * Fill the dram structure to get the system dram info. This will be - * used for memory latency calculation. - */ - ret = intel_dram_detect(display); - if (ret) - goto err_opregion; - - intel_bw_init_hw(display); + goto err_msi; return 0; -err_opregion: - intel_opregion_cleanup(display); +err_msi: pci_disable_msi(pdev); err_mem_regions: intel_memory_regions_driver_release(dev_priv); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 42fd87a6b26e..b99247ef8a6e 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -19,7 +19,6 @@ #include "intel_acpi.h" #include "intel_audio.h" -#include "intel_bw.h" #include "intel_display.h" #include "intel_display_core.h" #include "intel_display_device.h" @@ -29,7 +28,6 @@ #include "intel_dmc.h" #include "intel_dmc_wl.h" #include "intel_dp.h" -#include "intel_dram.h" #include "intel_fbdev.h" #include "intel_hdcp.h" #include "intel_hotplug.h" @@ -133,22 +131,9 @@ int xe_display_init_early(struct xe_device *xe) return 0; } - /* Early display init.. */ - intel_opregion_setup(display); - - /* - * Fill the dram structure to get the system dram info. This will be - * used for memory latency calculation. - */ - err = intel_dram_detect(display); - if (err) - goto err_opregion; - - intel_bw_init_hw(display); - err = intel_display_driver_probe_noirq(display); if (err) - goto err_opregion; + return err; err = intel_display_driver_probe_nogem(display); if (err) @@ -158,8 +143,7 @@ int xe_display_init_early(struct xe_device *xe) err_noirq: intel_display_driver_remove_noirq(display); intel_display_power_cleanup(display); -err_opregion: - intel_opregion_cleanup(display); + return err; } From 854d2c4092f4833d77bf625a9d709fd96cbb087e Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 21:48:01 +0300 Subject: [PATCH 174/186] drm/xe/display: change order of intel_display_driver_remove_{nogem, noirq}() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The display driver init and cleanup calls are slightly asymmetric. The cleanup order should be intel_display_driver_remove_noirq() and intel_display_driver_remove_nogem(), not the other way around. This is also what i915 does. Follow suit in xe. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/afb11c4e11cc4d946f0360aaad1664d004a93f41.1781549229.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/display/xe_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index b99247ef8a6e..f0f148a3bcb3 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -103,8 +103,8 @@ static void xe_display_fini_early(void *arg) return; intel_hpd_cancel_work(display); - intel_display_driver_remove_nogem(display); intel_display_driver_remove_noirq(display); + intel_display_driver_remove_nogem(display); intel_opregion_cleanup(display); intel_display_power_cleanup(display); } From a6ad0edf838f0b84912eaf2a3b176750c81c7289 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 21:48:02 +0300 Subject: [PATCH 175/186] drm/{i915, xe}: move opregion cleanup to intel_display_driver_remove_nogem() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intel_opregion_cleanup() call should really be in display. Move it at the end of intel_display_driver_probe_noirq(). For xe, this is a completely non-functional change now that the noirq/nogem cleanup calls are in the right order. For i915, this only changes the relative order of intel_opregion_cleanup() and i915_perf_fini(), which should be of no consequence. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/8ccd49a5945e0560ba22079d686db1268e8e9f7f.1781549229.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_driver.c | 2 ++ drivers/gpu/drm/i915/i915_driver.c | 3 --- drivers/gpu/drm/xe/display/xe_display.c | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 15a61f171d73..a1c91fbf737c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -662,6 +662,8 @@ void intel_display_driver_remove_nogem(struct intel_display *display) intel_display_power_driver_remove(display); intel_bios_driver_remove(display); + + intel_opregion_cleanup(display); } void intel_display_driver_unregister(struct intel_display *display) diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 4b588364ffb1..b6d8bc476a4d 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -586,13 +586,10 @@ ALLOW_ERROR_INJECTION(i915_driver_hw_probe, ERRNO); */ static void i915_driver_hw_remove(struct drm_i915_private *dev_priv) { - struct intel_display *display = dev_priv->display; struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); i915_perf_fini(dev_priv); - intel_opregion_cleanup(display); - pci_disable_msi(pdev); } diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index f0f148a3bcb3..94a8312704c4 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -105,7 +105,6 @@ static void xe_display_fini_early(void *arg) intel_hpd_cancel_work(display); intel_display_driver_remove_noirq(display); intel_display_driver_remove_nogem(display); - intel_opregion_cleanup(display); intel_display_power_cleanup(display); } From 3bf0abfbddfd42a458f1cc6a715b348f1adc84d7 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 15 Jun 2026 21:48:03 +0300 Subject: [PATCH 176/186] drm/{i915, xe}: move intel_hpd_cancel_work() to intel_display_driver_remove_noirq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_hpd_cancel_work() gets called before intel_display_driver_remove_noirq(). Move it there. Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/6be8d033a6c8d0038dc14100d3ee6612d6204770.1781549229.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_driver.c | 2 ++ drivers/gpu/drm/i915/i915_driver.c | 1 - drivers/gpu/drm/xe/display/xe_display.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index a1c91fbf737c..bb5301b90231 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -622,6 +622,8 @@ void intel_display_driver_remove_noirq(struct intel_display *display) if (!HAS_DISPLAY(display)) return; + intel_hpd_cancel_work(display); + intel_display_driver_suspend_access(display); /* diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index b6d8bc476a4d..45f2dcc5130e 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -948,7 +948,6 @@ void i915_driver_remove(struct drm_i915_private *i915) intel_display_driver_remove(display); intel_irq_uninstall(i915); - intel_hpd_cancel_work(display); intel_display_driver_remove_noirq(display); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 94a8312704c4..db77d05c5428 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -102,7 +102,6 @@ static void xe_display_fini_early(void *arg) if (!xe->info.probe_display) return; - intel_hpd_cancel_work(display); intel_display_driver_remove_noirq(display); intel_display_driver_remove_nogem(display); intel_display_power_cleanup(display); From 7a1bbc1bf66fa7491a2264959e92b9ba1f506636 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Wed, 24 Jun 2026 04:23:10 +0800 Subject: [PATCH 177/186] drm/i915: Refactor generic_handle_irq_safe() error messages Refactor all error messages resulting from generic_handle_irq_safe() failures in I915 for clarity. v2: - Use drm_err_ratelimited() correctly (jcavitt) v3: - Use xe_err_ratelimited() instead (Jadav) - Split into patch series (jcavitt) v4: - Use suggested phrasing (Wajdeczko) v5: - s/PTR_ERR/ERR_PTR (jcavitt) Suggested-by: Raag Jadav Suggested-by: Michal Wajdeczko Signed-off-by: Jonathan Cavitt Reviewed-by: Jani Nikula Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260623202310.1023770-1-jonathan.cavitt@intel.com --- drivers/gpu/drm/i915/display/intel_lpe_audio.c | 2 +- drivers/gpu/drm/i915/gt/intel_gsc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lpe_audio.c b/drivers/gpu/drm/i915/display/intel_lpe_audio.c index 022ad18044bf..ff2cf479d8e1 100644 --- a/drivers/gpu/drm/i915/display/intel_lpe_audio.c +++ b/drivers/gpu/drm/i915/display/intel_lpe_audio.c @@ -265,7 +265,7 @@ void intel_lpe_audio_irq_handler(struct intel_display *display) ret = generic_handle_irq_safe(display->audio.lpe.irq); if (ret) drm_err_ratelimited(display->drm, - "error handling LPE audio irq: %d\n", ret); + "LPE audio: irq handling failed (%pe)\n", ERR_PTR(ret)); } /** diff --git a/drivers/gpu/drm/i915/gt/intel_gsc.c b/drivers/gpu/drm/i915/gt/intel_gsc.c index 050d909fb4f8..1c06bf76568a 100644 --- a/drivers/gpu/drm/i915/gt/intel_gsc.c +++ b/drivers/gpu/drm/i915/gt/intel_gsc.c @@ -286,7 +286,7 @@ static void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id) ret = generic_handle_irq_safe(gt->gsc.intf[intf_id].irq); if (ret) - gt_err_ratelimited(gt, "error handling GSC irq: %d\n", ret); + gt_err_ratelimited(gt, "GSC: irq handling failed (%pe)\n", ERR_PTR(ret)); } void intel_gsc_irq_handler(struct intel_gt *gt, u32 iir) From 4dfcc789a144a21aa9be94f19f928aaa9fdc834d Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Mon, 22 Jun 2026 15:47:36 +0530 Subject: [PATCH 178/186] Revert "drm/i915/psr: Allow SCL=0 on platforms with always-on VRR TG" This reverts commit 4f1cab2e4863d96ce13b8d94151f4848e38c3d5b. Allowing SCL=0 on platforms with always-on VRR timing generator is causing underruns and other issues on PTL in some cases. SCL still needs to be non-zero in certain scenarios. Revert for now until this is better understood. Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260622101736.2389991-1-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 911afb9cb24e..92af21d823a3 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1405,9 +1405,6 @@ int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state needs_panel_replay) return 0; - if (intel_vrr_always_use_vrr_tg(display)) - return 0; - return 1; } From 9284ab3b6e776c315883ac2611283d263c9460fd Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 20:03:04 +0300 Subject: [PATCH 179/186] drm/i915/hdcp: check streams[] bounds before overflow The data->streams[] overflow check is done after the buffer overflow has already happened. Move the overflow check before the write. Side note, emitting a warning splat with a backtrace might be overkill here, but prefer not changing the behaviour other than not doing the overrun. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: e03187e12cae ("drm/i915/hdcp: MST streams support in hdcp port_data") Cc: stable@vger.kernel.org # v5.12+ Cc: Anshuman Gupta Cc: Suraj Kandpal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260625170304.1104723-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_hdcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index e88fec24af49..521786a75c42 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -145,6 +145,9 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, if (!new_conn_state || !new_conn_state->crtc) continue; + if (drm_WARN_ON(display->drm, data->k >= INTEL_NUM_PIPES(display))) + return -EINVAL; + data->streams[data->k].stream_id = intel_conn_to_vcpi(state, connector); data->k++; @@ -155,7 +158,7 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, } drm_connector_list_iter_end(&conn_iter); - if (drm_WARN_ON(display->drm, data->k > INTEL_NUM_PIPES(display) || data->k == 0)) + if (drm_WARN_ON(display->drm, !data->k)) return -EINVAL; /* From 58a224375c81179b52558c53d8857b93196d2687 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 13:44:07 +0300 Subject: [PATCH 180/186] drm/i915/hdcp: require monotonically increasing seq_num_v The HDCP 2.2 specification requires the seq_num_v to be monotonically increasing, and repeated seq_num_v needs to be treated as an integrity failure. Make it so. For the first message, seq_num_v must be zero, and is already checked. We can only check for less-than-or-equal for the subsequent messages, where hdcp2_encrypted is true. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: d849178e2c9e ("drm/i915: Implement HDCP2.2 repeater authentication") Cc: stable@vger.kernel.org # v5.2+ Cc: Suraj Kandpal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260625104407.1025614-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_hdcp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 521786a75c42..0a076d2ed70a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1801,9 +1801,10 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector) return -EINVAL; } - if (seq_num_v < hdcp->seq_num_v) { - /* Roll over of the seq_num_v from repeater. Reauthenticate. */ - drm_dbg_kms(display->drm, "Seq_num_v roll over.\n"); + if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) { + /* Reauthenticate on Seq_num_v repeat or rollover */ + drm_dbg_kms(display->drm, "Seq_num_v %s\n", + seq_num_v == hdcp->seq_num_v ? "repeat" : "rollover"); return -EINVAL; } From 1765cf59f517b02f3b0591fe5120930d08bddeb6 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 16:10:40 +0300 Subject: [PATCH 181/186] drm/i915/vrr: require valid min/max vfreq for VRR Ensure the EDID provided min/max vfreq are valid. Most scenarios are already covered (by coincidence) through the checks in intel_vrr_is_capable() and intel_vrr_is_in_range(), but be more explicit about it. At worst, a zero min_vfreq could lead to a division by zero in intel_vrr_compute_vmax(). Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: 117cd09ba528 ("drm/i915/display/dp: Compute VRR state in atomic_check") Cc: stable@vger.kernel.org # v5.12+ Cc: Ankit Nautiyal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260625131040.1051272-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_vrr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 5d9b11185296..bffbdee76ee1 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -76,6 +76,10 @@ bool intel_vrr_is_capable(struct intel_connector *connector) return false; } + if (!info->monitor_range.min_vfreq || !info->monitor_range.max_vfreq || + info->monitor_range.min_vfreq > info->monitor_range.max_vfreq) + return false; + return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10; } From c9ebe5d2f25729d6cfbbb1235d640bf67f9275df Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 26 Jun 2026 17:01:55 +0300 Subject: [PATCH 182/186] drm/i915/bios: range check LFP Data Block panel_type2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the panel_type from LFP Data Block is range checked, panel_type2 is not. Add a few helpers for range checking, and use them to not only check panel_type2, but also improve clarity and correctness in the panel type selection. Discovered using AI-assisted static analysis confirmed by Intel Product Security. v2: - Fix commit message typo (Michał) - Add is_panel_type_pnp() (Ville) Reported-by: Martin Hodo Fixes: 6434cf630086 ("drm/i915/bios: calculate panel type as per child device index in VBT") Cc: stable@vger.kernel.org # v6.0+ Cc: Animesh Manna Cc: Ville Syrjälä Reviewed-by: Michał Grzelak # v1 Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260626140155.1389655-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_bios.c | 36 ++++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 15ebadc72b88..97cbae2e547e 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -623,6 +623,21 @@ get_lfp_data_tail(const struct bdb_lfp_data *data, return NULL; } +static bool is_panel_type_valid(int panel_type) +{ + return panel_type >= 0 && panel_type < 16; +} + +static bool is_panel_type_pnp(int panel_type) +{ + return panel_type == 0xff; +} + +static bool is_panel_type_valid_or_pnp(int panel_type) +{ + return is_panel_type_valid(panel_type) || is_panel_type_pnp(panel_type); +} + static int opregion_get_panel_type(struct intel_display *display, const struct intel_bios_encoder_data *devdata, const struct drm_edid *drm_edid, bool use_fallback) @@ -640,15 +655,21 @@ static int vbt_get_panel_type(struct intel_display *display, if (!lfp_options) return -1; - if (lfp_options->panel_type > 0xf && - lfp_options->panel_type != 0xff) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type)) { drm_dbg_kms(display->drm, "Invalid VBT panel type 0x%x\n", lfp_options->panel_type); return -1; } - if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) + if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type2)) { + drm_dbg_kms(display->drm, "Invalid VBT panel type 2 0x%x\n", + lfp_options->panel_type2); + return -1; + } + return lfp_options->panel_type2; + } drm_WARN_ON(display->drm, devdata && devdata->child.handle != DEVICE_HANDLE_LFP1); @@ -762,13 +783,12 @@ static int get_panel_type(struct intel_display *display, panel_types[i].name, panel_types[i].panel_type); } - if (panel_types[PANEL_TYPE_OPREGION].panel_type >= 0) + if (is_panel_type_valid(panel_types[PANEL_TYPE_OPREGION].panel_type)) i = PANEL_TYPE_OPREGION; - else if (panel_types[PANEL_TYPE_VBT].panel_type == 0xff && - panel_types[PANEL_TYPE_PNPID].panel_type >= 0) + else if (is_panel_type_pnp(panel_types[PANEL_TYPE_VBT].panel_type) && + is_panel_type_valid(panel_types[PANEL_TYPE_PNPID].panel_type)) i = PANEL_TYPE_PNPID; - else if (panel_types[PANEL_TYPE_VBT].panel_type != 0xff && - panel_types[PANEL_TYPE_VBT].panel_type >= 0) + else if (is_panel_type_valid(panel_types[PANEL_TYPE_VBT].panel_type)) i = PANEL_TYPE_VBT; else i = PANEL_TYPE_FALLBACK; From 3e64a9e21093942d71a06d44ed93435601c6d4c0 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Mon, 29 Jun 2026 23:32:35 +0530 Subject: [PATCH 183/186] drm/i915/display: Guard CMTG disable with intel_cmtg_is_allowed() intel_cmtg_disable() maps crtc_state->cpu_transcoder to a CMTG transcoder via to_cmtg_transcoder(), which only returns a valid transcoder for TRANSCODER_A/B. The disable call sites in hsw_crtc_disable() and the fastset/VRR path only check the sticky crtc->cmtg.enabled flag, so during a big-joiner reconfiguration that moves the eDP across pipes intel_cmtg_disable() can be reached with a crtc_state whose cpu_transcoder does not map to a CMTG transcoder. That results in a negative register-array index (trans_offsets[-1]) and a UBSAN array-index-out-of-bounds splat: UBSAN: array-index-out-of-bounds in .../display/intel_cmtg.c:187:24 intel_cmtg_disable+0x395/0x3d0 [xe] intel_old_crtc_state_disables+0xfb/0x1f0 [xe] intel_atomic_commit_tail+0xca6/0x2040 [xe] Gate both call sites with intel_cmtg_is_allowed() so that intel_cmtg_disable() is only invoked for configurations that actually map to a CMTG transcoder. v2: - Guard the intel_cmtg_disable() call sites with intel_cmtg_is_allowed() instead of a silent return (Suraj). Fixes: 3bb44e8d421a ("drm/i915/cmtg: Modify existing hook to disable CMTG") Signed-off-by: Animesh Manna Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260629180236.1353704-2-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 5bc8e6ea10a5..90c05ad08f86 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -1790,7 +1790,7 @@ static void hsw_crtc_disable(struct intel_atomic_state *state, intel_atomic_get_old_crtc_state(state, crtc); struct intel_crtc *pipe_crtc; - if (crtc->cmtg.enabled) { + if (crtc->cmtg.enabled && intel_cmtg_is_allowed(old_crtc_state)) { intel_cmtg_set_clk_select(old_crtc_state); intel_cmtg_disable(old_crtc_state); } @@ -6886,7 +6886,8 @@ static void intel_update_crtc(struct intel_atomic_state *state, old_crtc_state->inherited) intel_crtc_arm_fifo_underrun(crtc, new_crtc_state); - if (crtc->cmtg.enabled && (intel_crtc_vrr_enabling(state, crtc))) { + if (crtc->cmtg.enabled && intel_crtc_vrr_enabling(state, crtc) && + intel_cmtg_is_allowed(new_crtc_state)) { intel_cmtg_set_clk_select(new_crtc_state); intel_cmtg_disable(new_crtc_state); } From c2408fda34d67b99a72727a5e9a128a379e28557 Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Mon, 29 Jun 2026 23:32:36 +0530 Subject: [PATCH 184/186] drm/i915/cmtg: Warn on invalid CMTG transcoder in intel_cmtg_disable() intel_cmtg_disable() indexes the per-transcoder register array using the CMTG transcoder returned by to_cmtg_transcoder(), which is only valid for TRANSCODER_A/B. The callers are now gated by intel_cmtg_is_allowed(), so reaching this function with an invalid CMTG transcoder should never happen. Add a drm_WARN_ON() that bails out early in that case, both to document the invariant and to guard against the out-of-bounds register access (trans_offsets[-1]) should a future caller get it wrong. v2: - Add an in-function INVALID_TRANSCODER drm_WARN_ON check. (Suraj) Signed-off-by: Animesh Manna Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260629180236.1353704-3-animesh.manna@intel.com --- drivers/gpu/drm/i915/display/intel_cmtg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index 6da28c185080..c8e0f90af910 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -183,6 +183,9 @@ void intel_cmtg_disable(const struct intel_crtc_state *crtc_state) if (!crtc->cmtg.enabled) return; + if (drm_WARN_ON(display->drm, cmtg_transcoder == INVALID_TRANSCODER)) + return; + crtc->cmtg.enabled = false; intel_de_rmw(display, TRANS_VRR_CTL(display, cmtg_transcoder), VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN, 0); From 2c9a89d6855c85062350b8a345339e6ffdb25e32 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Wed, 1 Jul 2026 14:45:02 +0530 Subject: [PATCH 185/186] drm/i915/ltphy: Readout ssc_enabled for LT PHY We need to readout the ssc_enabled param for LT PHY pll state too. Create a function that does that, we only need to read SSC Enable PLL A bit since that is the only one we write Xe3p onwards. While at it improve the dump using str_yes_or_no. Bspec: 74667 Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260701091503.1302226-2-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 14 ++++++++++++-- 1 file changed, 12 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 615ee980470e..956181f80d35 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2178,8 +2178,9 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, { int i, j; - drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %d, tbt mode: %d\n", - hw_state->lane_count, hw_state->ssc_enabled, hw_state->tbt_mode); + drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %s, tbt mode: %s\n", + hw_state->lane_count, str_yes_no(hw_state->ssc_enabled), + str_yes_no(hw_state->tbt_mode)); for (i = 0; i < 3; i++) { drm_printf(p, "config[%d] = 0x%.4x,\n", @@ -2221,6 +2222,14 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) XELPDP_LANE_PCLK_PLL_ACK(0); } +static bool readout_ssc_state(struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + + return intel_de_read(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port)) & + XELPDP_SSC_ENABLE_PLLA; +} + bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, struct intel_dpll *pll, struct intel_dpll_hw_state *hw_state) @@ -2250,6 +2259,7 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1; wakeref = intel_lt_phy_transaction_begin(encoder); + pll_state->ssc_enabled = readout_ssc_state(encoder); pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_LT_PHY_LANE0, INTEL_LT_PHY_LANE1); From 8e27f752037e72ccee9c4a7c4a6202ecf3daf603 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Wed, 1 Jul 2026 14:45:03 +0530 Subject: [PATCH 186/186] drm/i915/ltphy: Fix SSC Enablement bit in PORT_CLOCK_CTL According to Bspec we only need to write SSC Enable PLL A bit and leave PLL B bit alone in PORT_CLOCK_CTL register. Bspec: 74667, 74492 Fixes: 3383ba2479f7 ("drm/i915/ltphy: Enable SSC during port clock programming") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260701091503.1302226-3-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 956181f80d35..8fc6d230493f 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, else val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); - /* DP2.0 10G and 20G rates enable MPLLA*/ - if (port_clock == 1000000 || port_clock == 2000000) - val |= XELPDP_SSC_ENABLE_PLLA; - else - val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0; intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE |