From 65852b56bfa929f99e28c96fd98b02058959da7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Thu, 12 Mar 2026 10:37:09 +0200 Subject: [PATCH 01/68] drm/i915/psr: Disable PSR on update_m_n and update_lrr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PSR/PR parameters might change based on update_m_n or update_lrr. Disable on update_m_n and update_lrr to ensure proper parameters are taken into use on next PSR enable in intel_psr_post_plane_update. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15771 Fixes: 2bc98c6f97af ("drm/i915/alpm: Compute ALPM parameters into crtc_state->alpm_state") Cc: # v6.19+ Signed-off-by: Jouni Högander Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312083710.1593781-2-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 5041a5a138d1..7e0e4c3bf985 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -3112,6 +3112,8 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, * - Display WA #1136: skl, bxt */ if (intel_crtc_needs_modeset(new_crtc_state) || + new_crtc_state->update_m_n || + new_crtc_state->update_lrr || !new_crtc_state->has_psr || !new_crtc_state->active_planes || new_crtc_state->has_sel_update != psr->sel_update_enabled || From 8c229b4aa00262c13787982e998c61c0783285e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Thu, 12 Mar 2026 10:37:10 +0200 Subject: [PATCH 02/68] drm/i915/psr: Compute PSR entry_setup_frames into intel_crtc_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PSR entry_setup_frames is currently computed directly into struct intel_dp:intel_psr:entry_setup_frames. This causes a problem if mode change gets rejected after PSR compute config: Psr_entry_setup_frames computed for this rejected state is in intel_dp:intel_psr:entry_setup_frame. Fix this by computing it into intel_crtc_state and copy the value into intel_dp:intel_psr:entry_setup_frames on PSR enable. Fixes: 2b981d57e480 ("drm/i915/display: Support PSR entry VSC packet to be transmitted one frame earlier") Cc: Mika Kahola Cc: # v6.8+ Signed-off-by: Jouni Högander Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312083710.1593781-3-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_psr.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index e189f8c39ccb..d3a9ace4c9d1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1188,6 +1188,7 @@ struct intel_crtc_state { u32 dc3co_exitline; u16 su_y_granularity; u8 active_non_psr_pipes; + u8 entry_setup_frames; const char *no_psr_reason; /* diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 7e0e4c3bf985..c13116e6f17f 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1716,7 +1716,7 @@ static bool _psr_compute_config(struct intel_dp *intel_dp, entry_setup_frames = intel_psr_entry_setup_frames(intel_dp, conn_state, adjusted_mode); if (entry_setup_frames >= 0) { - intel_dp->psr.entry_setup_frames = entry_setup_frames; + crtc_state->entry_setup_frames = entry_setup_frames; } else { crtc_state->no_psr_reason = "PSR setup timing not met"; drm_dbg_kms(display->drm, @@ -1814,7 +1814,7 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp, { struct intel_display *display = to_intel_display(intel_dp); - return (DISPLAY_VER(display) == 20 && intel_dp->psr.entry_setup_frames > 0 && + return (DISPLAY_VER(display) == 20 && crtc_state->entry_setup_frames > 0 && !crtc_state->has_sel_update); } @@ -2190,6 +2190,7 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, intel_dp->psr.pkg_c_latency_used = crtc_state->pkg_c_latency_used; intel_dp->psr.io_wake_lines = crtc_state->alpm_state.io_wake_lines; intel_dp->psr.fast_wake_lines = crtc_state->alpm_state.fast_wake_lines; + intel_dp->psr.entry_setup_frames = crtc_state->entry_setup_frames; if (!psr_interrupt_error_check(intel_dp)) return; From 33978364a2f3fc2989751bdabcaea0ec7e8d1ae8 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Fri, 23 Jan 2026 15:21:22 +0000 Subject: [PATCH 03/68] drm/i915/display: PORT_NONE is not valid Static analysis issue: In assert_port_valid, add a check to ensure port != PORT_NONE, as that is not a valid port. The check must be explicit to prevent a bad bit shift operation in the general case via short-circuiting. It's not likely this will ever come up in a real use case, but it's at least worth guarding against. It would probably also be pertinent to modify the behavior of the port_name function to correctly print PORT_NONE in this case, as currently the port would be reported as 'port @' by the debugger. But that should be done separately, and given port_name is mostly just a debug printing helper function anyways, fixing it is a low priority. v2: - Conditional check was backwards. Fix it. (Jani) Signed-off-by: Jonathan Cavitt Cc: Jani Nikula Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260123152121.7042-2-jonathan.cavitt@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index b18ce0c36a64..ee501009a251 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -7890,7 +7890,8 @@ static bool intel_ddi_crt_present(struct intel_display *display) bool assert_port_valid(struct intel_display *display, enum port port) { - return !drm_WARN(display->drm, !(DISPLAY_RUNTIME_INFO(display)->port_mask & BIT(port)), + return !drm_WARN(display->drm, + !(port >= 0 && DISPLAY_RUNTIME_INFO(display)->port_mask & BIT(port)), "Platform does not support port %c\n", port_name(port)); } From 1cabff0d18733f1f33bd6ab0505cbec430834469 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Wed, 4 Feb 2026 16:19:46 +0000 Subject: [PATCH 04/68] drm/i915/gvt: Swap read and write checks The function intel_gvt_i2c_handle_aux_ch_write currently does not support the DP_AUX_I2C_WRITE operation. Notably, we check if op & 0x1 == DP_AUX_I2C_WRITE (one), and if it does not, assert that op & 0x1 == DP_AUX_I2C_READ (zero). This is unnecessary because if op & 0x1 != 1, then op & 0x1 == 0. But beyond that, it probably makes more sense to check for the condition that is implemented, rather than check for the condition that is not. Swap the conditions. We can also get rid of the unnecessary drm_WARN_ON while we're here. Suggested-by: Jani Nikula Signed-off-by: Jonathan Cavitt Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260204161945.8127-2-jonathan.cavitt@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/gvt/edid.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 021afff1cd5d..ca5b54466a65 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -535,16 +535,7 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, i2c_edid->edid_available = true; } } - } else if ((op & 0x1) == DP_AUX_I2C_WRITE) { - /* TODO - * We only support EDID reading from I2C_over_AUX. And - * we do not expect the index mode to be used. Right now - * the WRITE operation is ignored. It is good enough to - * support the gfx driver to do EDID access. - */ - } else { - if (drm_WARN_ON(&i915->drm, (op & 0x1) != DP_AUX_I2C_READ)) - return; + } else if ((op & 0x1) == DP_AUX_I2C_READ) { if (drm_WARN_ON(&i915->drm, msg_length != 4)) return; if (i2c_edid->edid_available && i2c_edid->target_selected) { @@ -553,6 +544,13 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, aux_data_for_write = (val << 16); } else aux_data_for_write = (0xff << 16); + } else { + /* TODO + * We only support EDID reading from I2C_over_AUX. And + * we do not expect the index mode to be used. Right now + * the WRITE operation is ignored. It is good enough to + * support the gfx driver to do EDID access. + */ } /* write the return value in AUX_CH_DATA reg which includes: * ACK of I2C_WRITE From 902d174c6fd30d428337e90ed769a3aa81951983 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:14:58 +0200 Subject: [PATCH 05/68] drm/i915/dmc: simplify stepping info initialization Having intel_get_stepping_info() return the pointer that was passed in isn't necessary. Just use a pointer to the local variable instead. The initialization to ** didn't make a difference, because it was always overridden. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/c9affb82fd3e9fb464778013bb7c8fab06232bfd.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dmc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 90ba932d940a..41842ff7d90f 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -418,15 +418,12 @@ bool intel_dmc_has_payload(struct intel_display *display) return has_dmc_id_fw(display, DMC_FW_MAIN); } -static const struct stepping_info * -intel_get_stepping_info(struct intel_display *display, - struct stepping_info *si) +static void initialize_stepping_info(struct intel_display *display, struct stepping_info *si) { const char *step_name = intel_step_name(INTEL_DISPLAY_STEP(display)); si->stepping = step_name[0]; si->substepping = step_name[1]; - return si; } static void gen9_set_dc_state_debugmask(struct intel_display *display) @@ -1274,8 +1271,7 @@ static int parse_dmc_fw(struct intel_dmc *dmc, const struct firmware *fw) struct intel_css_header *css_header; struct intel_package_header *package_header; struct intel_dmc_header_base *dmc_header; - struct stepping_info display_info = { '*', '*'}; - const struct stepping_info *si = intel_get_stepping_info(display, &display_info); + struct stepping_info si = {}; enum intel_dmc_id dmc_id; u32 readcount = 0; u32 r, offset; @@ -1283,6 +1279,8 @@ static int parse_dmc_fw(struct intel_dmc *dmc, const struct firmware *fw) if (!fw) return -EINVAL; + initialize_stepping_info(display, &si); + /* Extract CSS Header information */ css_header = (struct intel_css_header *)fw->data; r = parse_dmc_fw_css(dmc, css_header, fw->size); @@ -1293,7 +1291,7 @@ static int parse_dmc_fw(struct intel_dmc *dmc, const struct firmware *fw) /* Extract Package Header information */ package_header = (struct intel_package_header *)&fw->data[readcount]; - r = parse_dmc_fw_package(dmc, package_header, si, fw->size - readcount); + r = parse_dmc_fw_package(dmc, package_header, &si, fw->size - readcount); if (!r) return -EINVAL; From 2ca0e7657f6f18838e396febaf8ca93a762e486a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:14:59 +0200 Subject: [PATCH 06/68] drm/i915/display: add step name in display runtime info Initialize the stepping name in display runtime info. This avoids having to use intel_step_name(). For display device info print at boot, debugfs and snapshot this changes the unknown step name from ** to N/A, which is more user friendly anyway. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/aab445dedb8235d9fdddfe2ee5bb624cdf453a18.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- .../drm/i915/display/intel_display_device.c | 28 +++++++++++++++++-- .../drm/i915/display/intel_display_device.h | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c index 55689111c5d7..30d734a43211 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.c +++ b/drivers/gpu/drm/i915/display/intel_display_device.c @@ -1653,6 +1653,28 @@ static void display_platforms_or(struct intel_display_platforms *dst, bitmap_or(dst->bitmap, dst->bitmap, src->bitmap, display_platforms_num_bits()); } +#define __STEP_NAME(name) [STEP_##name] = #name, + +static void initialize_step(struct intel_display *display, enum intel_step step) +{ + static const char step_names[][3] = { + STEP_NAME_LIST(__STEP_NAME) + }; + + DISPLAY_RUNTIME_INFO(display)->step = step; + + /* Step name will remain an empty string if not applicable */ + if (step >= 0 && step < ARRAY_SIZE(step_names)) + strscpy(DISPLAY_RUNTIME_INFO(display)->step_name, step_names[step]); +} + +#undef __STEP_NAME + +static const char *step_name(const struct intel_display_runtime_info *runtime) +{ + return strlen(runtime->step_name) ? runtime->step_name : "N/A"; +} + struct intel_display *intel_display_device_probe(struct pci_dev *pdev, const struct intel_display_parent_interface *parent) { @@ -1730,14 +1752,14 @@ struct intel_display *intel_display_device_probe(struct pci_dev *pdev, subdesc ? &subdesc->step_info : NULL); } - DISPLAY_RUNTIME_INFO(display)->step = step; + initialize_step(display, step); drm_info(display->drm, "Found %s%s%s (device ID %04x) %s display version %u.%02u stepping %s\n", desc->name, subdesc ? "/" : "", subdesc ? subdesc->name : "", pdev->device, display->platform.dgfx ? "discrete" : "integrated", DISPLAY_RUNTIME_INFO(display)->ip.ver, DISPLAY_RUNTIME_INFO(display)->ip.rel, - step != STEP_NONE ? intel_step_name(step) : "N/A"); + step_name(DISPLAY_RUNTIME_INFO(display))); return display; @@ -1953,7 +1975,7 @@ void intel_display_device_info_print(const struct intel_display_device_info *inf drm_printf(p, "display version: %u\n", runtime->ip.ver); - drm_printf(p, "display stepping: %s\n", intel_step_name(runtime->step)); + drm_printf(p, "display stepping: %s\n", step_name(runtime)); #define PRINT_FLAG(name) drm_printf(p, "%s: %s\n", #name, str_yes_no(info->name)) DEV_INFO_DISPLAY_FOR_EACH_FLAG(PRINT_FLAG); diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index e84c190dcc4f..1170ac346615 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -287,6 +287,7 @@ struct intel_display_runtime_info { u16 step; /* hardware */ } ip; int step; /* symbolic */ + char step_name[3]; /* empty string if not applicable */ u32 rawclk_freq; From 58371a116845706ba2b90d3a1e0c9193794ebf06 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:15:00 +0200 Subject: [PATCH 07/68] drm/i915/dmc: use step name from runtime info Now that the step name is in runtime info, switch to using it instead of intel_step_name(). The ** are only relevant for DMC, so make their use explicit. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/395906e52e76bc726b9dac69a453583cc6e3f6c1.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dmc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 41842ff7d90f..1667a829e708 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -39,7 +39,6 @@ #include "intel_dmc.h" #include "intel_dmc_regs.h" #include "intel_flipq.h" -#include "intel_step.h" /** * DOC: DMC Firmware Support @@ -420,10 +419,10 @@ bool intel_dmc_has_payload(struct intel_display *display) static void initialize_stepping_info(struct intel_display *display, struct stepping_info *si) { - const char *step_name = intel_step_name(INTEL_DISPLAY_STEP(display)); + const char *step_name = DISPLAY_RUNTIME_INFO(display)->step_name; - si->stepping = step_name[0]; - si->substepping = step_name[1]; + si->stepping = step_name[0] ?: '*'; + si->substepping = step_name[1] ?: '*'; } static void gen9_set_dc_state_debugmask(struct intel_display *display) From 706d58da4c4b363b5dc3e690d97a05fdf47d2620 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:15:01 +0200 Subject: [PATCH 08/68] drm/xe/compat: remove intel_step_name macro As there are no more compat users left for intel_step_name(), remove the macro and use the more direct include for the enumerations. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/816e3f6dda0a112392e8f8ccff820a81aff63f32.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/xe/compat-i915-headers/intel_step.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h index 2cf13a572ab0..0eabe2866f5f 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_step.h @@ -6,9 +6,8 @@ #ifndef __INTEL_STEP_H__ #define __INTEL_STEP_H__ -#include "xe_step.h" +#include "xe_step_types.h" #define intel_step xe_step -#define intel_step_name xe_step_name #endif /* __INTEL_STEP_H__ */ From 3ccc8a922906703cd0efdf1bdd6186f18f7e23ec Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 16 Mar 2026 14:15:02 +0200 Subject: [PATCH 09/68] drm/intel: add shared step.h and switch i915 to use it As the first step towards using shared definitions for step name enumerations, add shared include/drm/intel/step.h and switch i915 to use it. Reviewed-by: Luca Coelho Link: https://patch.msgid.link/e76412a316ddff44dc46633d80e9caa5df54ed6b.1773663208.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_step.h | 57 +--------------------------- include/drm/intel/step.h | 62 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 55 deletions(-) create mode 100644 include/drm/intel/step.h diff --git a/drivers/gpu/drm/i915/intel_step.h b/drivers/gpu/drm/i915/intel_step.h index 22f1d6905160..2ca36eae4b5a 100644 --- a/drivers/gpu/drm/i915/intel_step.h +++ b/drivers/gpu/drm/i915/intel_step.h @@ -8,6 +8,8 @@ #include +#include + struct drm_i915_private; struct intel_step_info { @@ -19,61 +21,6 @@ struct intel_step_info { u8 media_step; }; -#define STEP_ENUM_VAL(name) STEP_##name, - -#define STEP_NAME_LIST(func) \ - func(A0) \ - func(A1) \ - func(A2) \ - func(A3) \ - func(B0) \ - func(B1) \ - func(B2) \ - func(B3) \ - func(C0) \ - func(C1) \ - func(C2) \ - func(C3) \ - func(D0) \ - func(D1) \ - func(D2) \ - func(D3) \ - func(E0) \ - func(E1) \ - func(E2) \ - func(E3) \ - func(F0) \ - func(F1) \ - func(F2) \ - func(F3) \ - func(G0) \ - func(G1) \ - func(G2) \ - func(G3) \ - func(H0) \ - func(H1) \ - func(H2) \ - func(H3) \ - func(I0) \ - func(I1) \ - func(I2) \ - func(I3) \ - func(J0) \ - func(J1) \ - func(J2) \ - func(J3) - -/* - * Symbolic steppings that do not match the hardware. These are valid both as gt - * and display steppings as symbolic names. - */ -enum intel_step { - STEP_NONE = 0, - STEP_NAME_LIST(STEP_ENUM_VAL) - STEP_FUTURE, - STEP_FOREVER, -}; - void intel_step_init(struct drm_i915_private *i915); const char *intel_step_name(enum intel_step step); diff --git a/include/drm/intel/step.h b/include/drm/intel/step.h new file mode 100644 index 000000000000..4de7520109bc --- /dev/null +++ b/include/drm/intel/step.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __STEP_H__ +#define __STEP_H__ + +#define STEP_ENUM_VAL(name) STEP_##name, + +#define STEP_NAME_LIST(func) \ + func(A0) \ + func(A1) \ + func(A2) \ + func(A3) \ + func(B0) \ + func(B1) \ + func(B2) \ + func(B3) \ + func(C0) \ + func(C1) \ + func(C2) \ + func(C3) \ + func(D0) \ + func(D1) \ + func(D2) \ + func(D3) \ + func(E0) \ + func(E1) \ + func(E2) \ + func(E3) \ + func(F0) \ + func(F1) \ + func(F2) \ + func(F3) \ + func(G0) \ + func(G1) \ + func(G2) \ + func(G3) \ + func(H0) \ + func(H1) \ + func(H2) \ + func(H3) \ + func(I0) \ + func(I1) \ + func(I2) \ + func(I3) \ + func(J0) \ + func(J1) \ + func(J2) \ + func(J3) + +/* + * Symbolic steppings that do not match the hardware. These are valid both as gt + * and display steppings as symbolic names. + */ +enum intel_step { + STEP_NONE = 0, + STEP_NAME_LIST(STEP_ENUM_VAL) + STEP_FUTURE, + STEP_FOREVER, +}; + +#endif /* __STEP_H__ */ From 0fb03890d18205ec0909fc47049eceae8ba36457 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 16 Mar 2026 08:48:51 +0530 Subject: [PATCH 10/68] drm/i915/backlight: Check if VESA backlight is possible Check if BACKLIGHT_BRIGHTNESS_AUX_SET_CAPABLE bit is set then EDP_PWMGEN_BIT_COUNT_CAP_MIN and EDP_PWMGEN_BIT_COUNT_CAP_MAX follow the eDP 1.4b Section 10.3. Which states min should be >= 1 and max should be >= min. Some legacy panels do not follow this properly. They set the BACKLIGHT_BRIGHTNESS_AUX_SET_CAPABLE bit while not correctly populating the min and max fields leading to a 0 max value. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7514 Fixes: 40d2f5820951 ("drm/i915/backlight: Remove try_vesa_interface") Signed-off-by: Suraj Kandpal Reviewed-by: Pranay Samala Link: https://patch.msgid.link/20260316031850.81794-1-suraj.kandpal@intel.com --- .../drm/i915/display/intel_dp_aux_backlight.c | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index a7b186d0e3c4..d0c76632a946 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -609,6 +609,34 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, return 0; } +static bool +check_if_vesa_backlight_possible(struct intel_dp *intel_dp) +{ + int ret; + u8 bit_min, bit_max; + + if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) + return true; + + ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min); + if (ret < 0) + return false; + + bit_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + if (bit_min < 1) + return false; + + ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &bit_max); + if (ret < 0) + return false; + + bit_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + if (bit_max < bit_min) + return false; + + return true; +} + static bool intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector) { @@ -625,12 +653,14 @@ intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector) return true; } - if (drm_edp_backlight_supported(intel_dp->edp_dpcd)) { + if (drm_edp_backlight_supported(intel_dp->edp_dpcd) && + check_if_vesa_backlight_possible(intel_dp)) { drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] AUX Backlight Control Supported!\n", connector->base.base.id, connector->base.name); return true; } + return false; } From 4ab0f09ee73fc853d00466682635f67c531f909c Mon Sep 17 00:00:00 2001 From: Samasth Norway Ananda Date: Mon, 16 Mar 2026 16:19:19 -0700 Subject: [PATCH 11/68] drm/i915/gmbus: fix spurious timeout on 512-byte burst reads When reading exactly 512 bytes with burst read enabled, the extra_byte_added path breaks out of the inner do-while without decrementing len. The outer while(len) then re-enters and gmbus_wait() times out since all data has been delivered. Decrement len before the break so the outer loop terminates correctly. Fixes: d5dc0f43f268 ("drm/i915/gmbus: Enable burst read") Signed-off-by: Samasth Norway Ananda Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260316231920.135438-2-samasth.norway.ananda@oracle.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_gmbus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index df48f27f1cc1..dd79a866b87e 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -495,8 +495,10 @@ gmbus_xfer_read_chunk(struct intel_display *display, val = intel_de_read_fw(display, GMBUS3(display)); do { - if (extra_byte_added && len == 1) + if (extra_byte_added && len == 1) { + len--; break; + } *buf++ = val & 0xff; val >>= 8; From e5b3fe57dc5893c7ea5f478a1161b8643adf5dd9 Mon Sep 17 00:00:00 2001 From: Samasth Norway Ananda Date: Mon, 16 Mar 2026 16:19:20 -0700 Subject: [PATCH 12/68] drm/i915/gmbus: fix a typo in comment message Fix a typo inside a comment message from ("generata" -> "generate") in function do_gmbus_xfer() before calling intel_de_write_fw() Signed-off-by: Samasth Norway Ananda Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260316231920.135438-3-samasth.norway.ananda@oracle.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_gmbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index dd79a866b87e..ea5cf8f51b31 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -694,7 +694,7 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num, goto clear_err; } - /* Generate a STOP condition on the bus. Note that gmbus can't generata + /* Generate a STOP condition on the bus. Note that gmbus can't generate * a STOP on the very first cycle. To simplify the code we * unconditionally generate the STOP condition with an additional gmbus * cycle. */ From b63c6b9b7f5ed02bb3abf7a39c18ea54d1a69f0f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 4 Mar 2026 09:36:48 +0100 Subject: [PATCH 13/68] drm/i915/fbdev: fix link failure without FBDEV emulation If CONFIG_DRM_FBDEV_EMULATION is disabled but CONFIG_FRAMEBUFFER_CONSOLE is turned on, the i915 driver now fails to link: ERROR: modpost: "intel_fbdev_fb_prefer_stolen" [drivers/gpu/drm/i915/i915.ko] undefined! Fix the contition to include a check for the symbol that controls compilation of intel_fbdev_fb.c. Fixes: 94c7d2861292 ("drm/i915/fbdev: Extract intel_fbdev_fb_prefer_stolen()") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260304083701.724908-1-arnd@kernel.org Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_initial_plane.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_initial_plane.c b/drivers/gpu/drm/i915/i915_initial_plane.c index 5594548f51d8..390a9248d631 100644 --- a/drivers/gpu/drm/i915/i915_initial_plane.c +++ b/drivers/gpu/drm/i915/i915_initial_plane.c @@ -115,7 +115,8 @@ initial_plane_vma(struct drm_i915_private *i915, * important and we should probably use that space with FBC or other * features. */ - if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) && + if (IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) && + IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) && mem == i915->mm.stolen_region && !intel_fbdev_fb_prefer_stolen(&i915->drm, size)) { drm_dbg_kms(&i915->drm, "Initial FB size exceeds half of stolen, discarding\n"); From 45c77d4bf8d4d15453d709b9b828e498898e0751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Tue, 17 Mar 2026 08:24:02 +0200 Subject: [PATCH 14/68] drm/i915/psr: Disable Panel Replay on Dell XPS 14 DA14260 as a quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new quirk (QUIRK_DISABLE_PANEL_REPLAY) for disabling Panel Replay as quirk for problematic setups. Apply this newly added quirk on Dell XPS 14 DA14260 if specific panel model is installed. We are observing problems with Dell XPS 14 DA14260. This device has certain LGD panel model which seems to be problematic. We have seen other LGD panel model with same OUI is working fine. Due to this we can't apply the quirk only based on panel OUI. There are also cases where same device model has differing panel model. We don't want to disable Panel Replay on such devices. Best we can do is to apply the quirk based on both device model and panel model. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7521 Signed-off-by: Jouni Högander Reviewed-by: Mika Kahola Link: https://patch.msgid.link/20260317062402.1888624-1-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ drivers/gpu/drm/i915/display/intel_quirks.c | 17 ++++++++++++++++- drivers/gpu/drm/i915/display/intel_quirks.h | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index c13116e6f17f..b319e5bd6274 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -49,6 +49,7 @@ #include "intel_hdmi.h" #include "intel_psr.h" #include "intel_psr_regs.h" +#include "intel_quirks.h" #include "intel_snps_phy.h" #include "intel_step.h" #include "intel_vblank.h" @@ -609,6 +610,12 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp, struct intel_conn if (intel_dp->mst_detect == DRM_DP_MST) return; + if (intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY)) { + drm_dbg_kms(display->drm, + "Panel Replay support not currently available for this setup\n"); + return; + } + ret = drm_dp_dpcd_read_data(&intel_dp->aux, DP_PANEL_REPLAY_CAP_SUPPORT, &connector->dp.panel_replay_caps.dpcd, sizeof(connector->dp.panel_replay_caps.dpcd)); diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c index 1abbdd426e58..8f1bf8f418ec 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.c +++ b/drivers/gpu/drm/i915/display/intel_quirks.c @@ -86,6 +86,14 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display *display) drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n"); } +static void quirk_disable_panel_replay(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + + intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY); + drm_info(display->drm, "Applying disable Panel Replay quirk\n"); +} + struct intel_quirk { int device; int subsystem_vendor; @@ -251,7 +259,14 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { .sink_oui = SINK_OUI(0x38, 0xec, 0x11), .hook = quirk_fw_sync_len, }, - + /* Dell XPS 14 DA14260 */ + { + .device = 0xb080, + .subsystem_vendor = 0x1028, + .subsystem_device = 0x0db9, + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), + .hook = quirk_disable_panel_replay, + }, }; void intel_init_quirks(struct intel_display *display) diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h index 06da0e286c67..77e490caed0d 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.h +++ b/drivers/gpu/drm/i915/display/intel_quirks.h @@ -21,6 +21,7 @@ enum intel_quirk_id { QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK, QUIRK_FW_SYNC_LEN, QUIRK_EDP_LIMIT_RATE_HBR2, + QUIRK_DISABLE_PANEL_REPLAY, }; void intel_init_quirks(struct intel_display *display); From a464bace0482aa9a83e9aa7beefbaf44cd58e6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Mar 2026 13:07:40 +0200 Subject: [PATCH 15/68] drm/i915: Order OP vs. timeout correctly in __wait_for() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put the barrier() before the OP so that anything we read out in OP and check in COND will actually be read out after the timeout has been evaluated. Currently the only place where we use OP is __intel_wait_for_register(), but the use there is precisely susceptible to this reordering, assuming the ktime_*() stuff itself doesn't act as a sufficient barrier: __intel_wait_for_register(...) { ... ret = __wait_for(reg_value = intel_uncore_read_notrace(...), (reg_value & mask) == value, ...); ... } Cc: stable@vger.kernel.org Fixes: 1c3c1dc66a96 ("drm/i915: Add compiler barrier to wait_for") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260313110740.24620-1-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/i915_wait_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_wait_util.h b/drivers/gpu/drm/i915/i915_wait_util.h index 7376898e3bf8..e1ed7921ec70 100644 --- a/drivers/gpu/drm/i915/i915_wait_util.h +++ b/drivers/gpu/drm/i915/i915_wait_util.h @@ -25,9 +25,9 @@ might_sleep(); \ for (;;) { \ const bool expired__ = ktime_after(ktime_get_raw(), end__); \ - OP; \ /* Guarantee COND check prior to timeout */ \ barrier(); \ + OP; \ if (COND) { \ ret__ = 0; \ break; \ From 017ecd04985573eeeb0745fa2c23896fb22ee0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 16 Mar 2026 18:39:51 +0200 Subject: [PATCH 16/68] drm/i915: Unlink NV12 planes earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit unlink_nv12_plane() will clobber parts of the plane state potentially already set up by plane_atomic_check(), so we must make sure not to call the two in the wrong order. The problem happens when a plane previously selected as a Y plane is now configured as a normal plane by user space. plane_atomic_check() will first compute the proper plane state based on the userspace request, and unlink_nv12_plane() later clears some of the state. This used to work on account of unlink_nv12_plane() skipping the state clearing based on the plane visibility. But I removed that check, thinking it was an impossible situation. Now when that situation happens unlink_nv12_plane() will just WARN and proceed to clobber the state. Rather than reverting to the old way of doing things, I think it's more clear if we unlink the NV12 planes before we even compute the new plane state. Cc: stable@vger.kernel.org Reported-by: Khaled Almahallawy Closes: https://lore.kernel.org/intel-gfx/20260212004852.1920270-1-khaled.almahallawy@intel.com/ Tested-by: Khaled Almahallawy Fixes: 6a01df2f1b2a ("drm/i915: Remove pointless visible check in unlink_nv12_plane()") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260316163953.12905-2-ville.syrjala@linux.intel.com Reviewed-by: Uma Shankar --- drivers/gpu/drm/i915/display/intel_plane.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index e06a0618b4c6..076b9b356481 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -436,11 +436,16 @@ void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, drm_framebuffer_get(plane_state->hw.fb); } +static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state); + void intel_plane_set_invisible(struct intel_crtc_state *crtc_state, struct intel_plane_state *plane_state) { struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + unlink_nv12_plane(crtc_state, plane_state); + crtc_state->active_planes &= ~BIT(plane->id); crtc_state->scaled_planes &= ~BIT(plane->id); crtc_state->nv12_planes &= ~BIT(plane->id); @@ -1513,6 +1518,9 @@ static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, struct intel_display *display = to_intel_display(plane_state); struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + if (!plane_state->planar_linked_plane) + return; + plane_state->planar_linked_plane = NULL; if (!plane_state->is_y_plane) @@ -1550,8 +1558,7 @@ static int icl_check_nv12_planes(struct intel_atomic_state *state, if (plane->pipe != crtc->pipe) continue; - if (plane_state->planar_linked_plane) - unlink_nv12_plane(crtc_state, plane_state); + unlink_nv12_plane(crtc_state, plane_state); } if (!crtc_state->nv12_planes) From 7b3a14322d1a8cbc6facdc857ac81960497e1c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 16 Mar 2026 18:39:52 +0200 Subject: [PATCH 17/68] drm/i915: Relocate unlink_nv12_plane() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move unlink_nv12_plane() ahead of its first caller to avoid the forward declaration. Cc: Khaled Almahallawy Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260316163953.12905-3-ville.syrjala@linux.intel.com Reviewed-by: Uma Shankar --- drivers/gpu/drm/i915/display/intel_plane.c | 49 ++++++++++------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 076b9b356481..bc1c801a06d7 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -437,7 +437,29 @@ void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, } static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, - struct intel_plane_state *plane_state); + struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane_state); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + + if (!plane_state->planar_linked_plane) + return; + + plane_state->planar_linked_plane = NULL; + + if (!plane_state->is_y_plane) + return; + + drm_WARN_ON(display->drm, plane_state->uapi.visible); + + plane_state->is_y_plane = false; + + crtc_state->enabled_planes &= ~BIT(plane->id); + crtc_state->active_planes &= ~BIT(plane->id); + crtc_state->update_planes |= BIT(plane->id); + crtc_state->data_rate[plane->id] = 0; + crtc_state->rel_data_rate[plane->id] = 0; +} void intel_plane_set_invisible(struct intel_crtc_state *crtc_state, struct intel_plane_state *plane_state) @@ -1512,31 +1534,6 @@ static void link_nv12_planes(struct intel_crtc_state *crtc_state, icl_link_nv12_planes(uv_plane_state, y_plane_state); } -static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, - struct intel_plane_state *plane_state) -{ - struct intel_display *display = to_intel_display(plane_state); - struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); - - if (!plane_state->planar_linked_plane) - return; - - plane_state->planar_linked_plane = NULL; - - if (!plane_state->is_y_plane) - return; - - drm_WARN_ON(display->drm, plane_state->uapi.visible); - - plane_state->is_y_plane = false; - - crtc_state->enabled_planes &= ~BIT(plane->id); - crtc_state->active_planes &= ~BIT(plane->id); - crtc_state->update_planes |= BIT(plane->id); - crtc_state->data_rate[plane->id] = 0; - crtc_state->rel_data_rate[plane->id] = 0; -} - static int icl_check_nv12_planes(struct intel_atomic_state *state, struct intel_crtc *crtc) { From c5121204ad99196b6f80d12e5029e9a766cd8008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 16 Mar 2026 18:39:53 +0200 Subject: [PATCH 18/68] drm/i915: Skip redundant NV12 plane unlinking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plane_atomic_check() will already have unlinked the old NV12 planes by the time icl_check_nv12_planes() gets called. Drop the redundant second unlinking. Cc: Khaled Almahallawy Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260316163953.12905-4-ville.syrjala@linux.intel.com Reviewed-by: Uma Shankar --- drivers/gpu/drm/i915/display/intel_plane.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index bc1c801a06d7..5390ceb21ca4 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -1547,17 +1547,6 @@ static int icl_check_nv12_planes(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11) return 0; - /* - * Destroy all old plane links and make the Y plane invisible - * in the crtc_state->active_planes mask. - */ - for_each_new_intel_plane_in_state(state, plane, plane_state, i) { - if (plane->pipe != crtc->pipe) - continue; - - unlink_nv12_plane(crtc_state, plane_state); - } - if (!crtc_state->nv12_planes) return 0; From c2d3e41f6e52269736cb9ecb17416e04f52b959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:26 +0200 Subject: [PATCH 19/68] drm/i915/wm: Nuke is_planar from skl+ wm structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need is_planar in either the actual watermarks or the wm_params structure used during the wm computation. Get rid of both. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/skl_watermark.c | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index d3a9ace4c9d1..93b8b2f91484 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -851,7 +851,6 @@ struct skl_plane_wm { struct skl_wm_level wm0; struct skl_wm_level trans_wm; } sagv; - bool is_planar; }; struct skl_pipe_wm { diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index b1f9546b8cda..0f99a3264f05 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -63,7 +63,6 @@ static void skl_sagv_disable(struct intel_display *display); struct skl_wm_params { bool x_tiled, y_tiled; bool rc_surface; - bool is_planar; u32 width; u8 cpp; u32 plane_pixel_rate; @@ -1675,10 +1674,9 @@ skl_compute_wm_params(const struct intel_crtc_state *crtc_state, wp->y_tiled = modifier != I915_FORMAT_MOD_X_TILED && intel_fb_is_tiled_modifier(modifier); wp->rc_surface = intel_fb_is_ccs_modifier(modifier); - wp->is_planar = intel_format_info_is_yuv_semiplanar(format, modifier); wp->width = width; - if (color_plane == 1 && wp->is_planar) + if (color_plane == 1 && intel_format_info_is_yuv_semiplanar(format, modifier)) wp->width /= 2; wp->cpp = format->cpp[color_plane]; @@ -2073,8 +2071,6 @@ static int skl_build_plane_wm_uv(struct intel_crtc_state *crtc_state, struct skl_wm_params wm_params; int ret; - wm->is_planar = true; - /* uv plane watermarks must also be validated for NV12/Planar */ ret = skl_compute_plane_wm_params(crtc_state, plane_state, &wm_params, 1); From cdb41e341c6c174e9e778c02caa56eb08d256c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:27 +0200 Subject: [PATCH 20/68] drm/i915/wm: Reorder the arguments to skl_allocate_plane_ddb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group the ddb and data_rate together in the skl_allocate_plane_ddb() arguments. Upcoming changes will adjust the UV plane handling and keeing the ddb allocation and the data rate used to calculate it together will help with clarity. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 0f99a3264f05..1664b84d0387 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1391,9 +1391,8 @@ struct skl_plane_ddb_iter { static void skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, - struct skl_ddb_entry *ddb, const struct skl_wm_level *wm, - u64 data_rate) + struct skl_ddb_entry *ddb, u64 data_rate) { u16 size, extra = 0; @@ -1523,13 +1522,13 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11 && crtc_state->nv12_planes & BIT(plane_id)) { - skl_allocate_plane_ddb(&iter, ddb_y, &wm->wm[level], - crtc_state->rel_data_rate_y[plane_id]); - skl_allocate_plane_ddb(&iter, ddb, &wm->uv_wm[level], - crtc_state->rel_data_rate[plane_id]); + skl_allocate_plane_ddb(&iter, &wm->wm[level], + ddb_y, crtc_state->rel_data_rate_y[plane_id]); + skl_allocate_plane_ddb(&iter, &wm->uv_wm[level], + ddb, crtc_state->rel_data_rate[plane_id]); } else { - skl_allocate_plane_ddb(&iter, ddb, &wm->wm[level], - crtc_state->rel_data_rate[plane_id]); + skl_allocate_plane_ddb(&iter, &wm->wm[level], + ddb, crtc_state->rel_data_rate[plane_id]); } if (DISPLAY_VER(display) >= 30) { From cd4387fa57b4a2b276b40c61caa6e2e6ad217724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:28 +0200 Subject: [PATCH 21/68] drm/i915/wm: s/skl_check_nv12_wm_level()/skl_check_wm_level_nv12()/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename skl_check_nv12_wm_level() to skl_check_wm_level_nv12(). There will be a sort of DDB counterparts to skl_check_wm_level*(), and putting the "nv12" part to the end will allow consistent naming. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-4-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 1664b84d0387..24978f312fec 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1356,7 +1356,7 @@ skl_check_wm_level(struct skl_wm_level *wm, const struct skl_ddb_entry *ddb) } static void -skl_check_nv12_wm_level(struct skl_wm_level *wm, struct skl_wm_level *uv_wm, +skl_check_wm_level_nv12(struct skl_wm_level *wm, struct skl_wm_level *uv_wm, const struct skl_ddb_entry *ddb_y, const struct skl_ddb_entry *ddb) { if (wm->min_ddb_alloc > skl_ddb_entry_size(ddb_y) || @@ -1555,7 +1555,7 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11 && crtc_state->nv12_planes & BIT(plane_id)) - skl_check_nv12_wm_level(&wm->wm[level], + skl_check_wm_level_nv12(&wm->wm[level], &wm->uv_wm[level], ddb_y, ddb); else From bcc8da0436839562becbbc9e1f134559e86a8470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:29 +0200 Subject: [PATCH 22/68] drm/i915/wm: Extract skl_allocate_plane_ddb_nv12() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract skl_allocate_plane_ddb_nv12() as the compute counterpart to skl_check_wm_level_nv12(). Mainly to hide some of the clutter from skl_crtc_allocate_plane_ddb(). Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-5-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 40 ++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 24978f312fec..7c4c42dde991 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1390,9 +1390,9 @@ struct skl_plane_ddb_iter { }; static void -skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, - const struct skl_wm_level *wm, - struct skl_ddb_entry *ddb, u64 data_rate) +_skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, + u16 min_ddb_alloc, + struct skl_ddb_entry *ddb, u64 data_rate) { u16 size, extra = 0; @@ -1409,12 +1409,31 @@ skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, * to avoid skl_ddb_add_affected_planes() adding them to * the state when other planes change their allocations. */ - size = wm->min_ddb_alloc + extra; + size = min_ddb_alloc + extra; if (size) iter->start = skl_ddb_entry_init(ddb, iter->start, iter->start + size); } +static void +skl_allocate_plane_ddb(struct skl_plane_ddb_iter *iter, + const struct skl_wm_level *wm, + struct skl_ddb_entry *ddb, u64 data_rate) +{ + _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc, ddb, data_rate); +} + +static void +skl_allocate_plane_ddb_nv12(struct skl_plane_ddb_iter *iter, + const struct skl_wm_level *wm, + struct skl_ddb_entry *ddb_y, u64 data_rate_y, + const struct skl_wm_level *uv_wm, + struct skl_ddb_entry *ddb, u64 data_rate) +{ + _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc, ddb_y, data_rate_y); + _skl_allocate_plane_ddb(iter, uv_wm->min_ddb_alloc, ddb, data_rate); +} + static int skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, struct intel_crtc *crtc) @@ -1521,15 +1540,14 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, continue; if (DISPLAY_VER(display) < 11 && - crtc_state->nv12_planes & BIT(plane_id)) { - skl_allocate_plane_ddb(&iter, &wm->wm[level], - ddb_y, crtc_state->rel_data_rate_y[plane_id]); - skl_allocate_plane_ddb(&iter, &wm->uv_wm[level], - ddb, crtc_state->rel_data_rate[plane_id]); - } else { + crtc_state->nv12_planes & BIT(plane_id)) + skl_allocate_plane_ddb_nv12(&iter, &wm->wm[level], + ddb_y, crtc_state->rel_data_rate_y[plane_id], + &wm->uv_wm[level], + ddb, crtc_state->rel_data_rate[plane_id]); + else skl_allocate_plane_ddb(&iter, &wm->wm[level], ddb, crtc_state->rel_data_rate[plane_id]); - } if (DISPLAY_VER(display) >= 30) { *min_ddb = wm->wm[0].min_ddb_alloc; From eb0ec2989e717a94f5e0bce9d73526a6fc598ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:30 +0200 Subject: [PATCH 23/68] drm/i915/wm: Nuke wm->uv_wm[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently keep around the full watermarks for the UV plane on pre-icl, even though the hardware doesn't need most of this information. The only thing we need to keep is the min_ddb_alloc for the UV plane. Move that into the main wm->wm[].min_ddb_alloc_uv alongside the other min_ddb_alloc (used for Y/RGB). This makes our state tracking match the hardware more closely, and avoids having to justify everwhere why uv_wm[] is being ignored. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-6-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- .../drm/i915/display/intel_display_types.h | 2 +- drivers/gpu/drm/i915/display/skl_watermark.c | 43 ++++++++----------- 2 files changed, 19 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 93b8b2f91484..e2496db1642a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -835,6 +835,7 @@ struct intel_pipe_wm { struct skl_wm_level { u16 min_ddb_alloc; + u16 min_ddb_alloc_uv; /* for pre-icl */ u16 blocks; u8 lines; bool enable; @@ -845,7 +846,6 @@ struct skl_wm_level { struct skl_plane_wm { struct skl_wm_level wm[8]; - struct skl_wm_level uv_wm[8]; struct skl_wm_level trans_wm; struct { struct skl_wm_level wm0; diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 7c4c42dde991..8b1b371fbfab 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -1356,14 +1356,13 @@ skl_check_wm_level(struct skl_wm_level *wm, const struct skl_ddb_entry *ddb) } static void -skl_check_wm_level_nv12(struct skl_wm_level *wm, struct skl_wm_level *uv_wm, - const struct skl_ddb_entry *ddb_y, const struct skl_ddb_entry *ddb) +skl_check_wm_level_nv12(struct skl_wm_level *wm, + const struct skl_ddb_entry *ddb_y, + const struct skl_ddb_entry *ddb) { if (wm->min_ddb_alloc > skl_ddb_entry_size(ddb_y) || - uv_wm->min_ddb_alloc > skl_ddb_entry_size(ddb)) { + wm->min_ddb_alloc_uv > skl_ddb_entry_size(ddb)) memset(wm, 0, sizeof(*wm)); - memset(uv_wm, 0, sizeof(*uv_wm)); - } } static bool skl_need_wm_copy_wa(struct intel_display *display, int level, @@ -1427,11 +1426,10 @@ static void skl_allocate_plane_ddb_nv12(struct skl_plane_ddb_iter *iter, const struct skl_wm_level *wm, struct skl_ddb_entry *ddb_y, u64 data_rate_y, - const struct skl_wm_level *uv_wm, struct skl_ddb_entry *ddb, u64 data_rate) { _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc, ddb_y, data_rate_y); - _skl_allocate_plane_ddb(iter, uv_wm->min_ddb_alloc, ddb, data_rate); + _skl_allocate_plane_ddb(iter, wm->min_ddb_alloc_uv, ddb, data_rate); } static int @@ -1499,7 +1497,7 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, } blocks += wm->wm[level].min_ddb_alloc; - blocks += wm->uv_wm[level].min_ddb_alloc; + blocks += wm->wm[level].min_ddb_alloc_uv; } if (blocks <= iter.size) { @@ -1543,7 +1541,6 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, crtc_state->nv12_planes & BIT(plane_id)) skl_allocate_plane_ddb_nv12(&iter, &wm->wm[level], ddb_y, crtc_state->rel_data_rate_y[plane_id], - &wm->uv_wm[level], ddb, crtc_state->rel_data_rate[plane_id]); else skl_allocate_plane_ddb(&iter, &wm->wm[level], @@ -1573,9 +1570,7 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state, if (DISPLAY_VER(display) < 11 && crtc_state->nv12_planes & BIT(plane_id)) - skl_check_wm_level_nv12(&wm->wm[level], - &wm->uv_wm[level], - ddb_y, ddb); + skl_check_wm_level_nv12(&wm->wm[level], ddb_y, ddb); else skl_check_wm_level(&wm->wm[level], ddb); @@ -2084,9 +2079,11 @@ static int skl_build_plane_wm_uv(struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, struct intel_plane *plane) { + struct intel_display *display = to_intel_display(crtc_state); struct skl_plane_wm *wm = &crtc_state->wm.skl.raw.planes[plane->id]; + struct skl_wm_level uv_wm[ARRAY_SIZE(wm->wm)] = {}; struct skl_wm_params wm_params; - int ret; + int ret, level; /* uv plane watermarks must also be validated for NV12/Planar */ ret = skl_compute_plane_wm_params(crtc_state, plane_state, @@ -2094,7 +2091,14 @@ static int skl_build_plane_wm_uv(struct intel_crtc_state *crtc_state, if (ret) return ret; - skl_compute_wm_levels(crtc_state, plane, &wm_params, wm->uv_wm); + skl_compute_wm_levels(crtc_state, plane, &wm_params, uv_wm); + + /* + * Only keep the min_ddb_alloc for UV as + * the hardware needs nothing else. + */ + for (level = 0; level < display->wm.num_levels; level++) + wm->wm[level].min_ddb_alloc_uv = uv_wm[level].min_ddb_alloc; return 0; } @@ -2317,7 +2321,6 @@ static int skl_wm_check_vblank(struct intel_crtc_state *crtc_state) * thing as bad via min_ddb_alloc=U16_MAX? */ wm->wm[level].enable = false; - wm->uv_wm[level].enable = false; } } @@ -2388,11 +2391,6 @@ static bool skl_plane_wm_equals(struct intel_display *display, int level; for (level = 0; level < display->wm.num_levels; level++) { - /* - * We don't check uv_wm as the hardware doesn't actually - * use it. It only gets used for calculating the required - * ddb allocation. - */ if (!skl_wm_level_equals(&wm1->wm[level], &wm2->wm[level])) return false; } @@ -2753,11 +2751,6 @@ static bool skl_plane_selected_wm_equals(struct intel_plane *plane, int level; for (level = 0; level < display->wm.num_levels; level++) { - /* - * We don't check uv_wm as the hardware doesn't actually - * use it. It only gets used for calculating the required - * ddb allocation. - */ if (!skl_wm_level_equals(skl_plane_wm_level(old_pipe_wm, plane->id, level), skl_plane_wm_level(new_pipe_wm, plane->id, level))) return false; From e2a5a5b8876137df75506ca9272619813f958b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:31 +0200 Subject: [PATCH 24/68] drm/i915/wm: s/skl_print_plane_changes()/skl_print_plane_wm_changes()/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename skl_print_plane_changes() to skl_print_plane_wm_changes() to better reflect what it does. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-7-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 8b1b371fbfab..6c8dab847ae2 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2602,11 +2602,12 @@ static char enast(bool enable) } static noinline_for_stack void -skl_print_plane_changes(struct intel_display *display, - struct intel_plane *plane, - const struct skl_plane_wm *old_wm, - const struct skl_plane_wm *new_wm) +skl_print_plane_wm_changes(struct intel_plane *plane, + const struct skl_plane_wm *old_wm, + const struct skl_plane_wm *new_wm) { + struct intel_display *display = to_intel_display(plane); + drm_dbg_kms(display->drm, "[PLANE:%d:%s] level %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm" " -> %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm\n", @@ -2738,7 +2739,7 @@ skl_print_wm_changes(struct intel_atomic_state *state) if (skl_plane_wm_equals(display, old_wm, new_wm)) continue; - skl_print_plane_changes(display, plane, old_wm, new_wm); + skl_print_plane_wm_changes(plane, old_wm, new_wm); } } } From 27e58f7614533c0ba48d5919032283732f5342b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:32 +0200 Subject: [PATCH 25/68] drm/i915/wm: Extract skl_print_plane_ddb_changes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have skl_print_plane_wm_changes() but the DDB counterpart is just inline in the main loop. Extract it into a function. We'll have a second use for this soon. The "ddb" part is already parametrized in anticipation of the second user. v2: Use prink field width for ddb_name alignment Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-8-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 6c8dab847ae2..6ad0546c928f 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2601,6 +2601,21 @@ static char enast(bool enable) return enable ? '*' : ' '; } +static void +skl_print_plane_ddb_changes(struct intel_plane *plane, + const struct skl_ddb_entry *old, + const struct skl_ddb_entry *new, + const char *ddb_name) +{ + struct intel_display *display = to_intel_display(plane); + + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] %5s (%4d - %4d) -> (%4d - %4d), size %4d -> %4d\n", + plane->base.base.id, plane->base.name, ddb_name, + old->start, old->end, new->start, new->end, + skl_ddb_entry_size(old), skl_ddb_entry_size(new)); +} + static noinline_for_stack void skl_print_plane_wm_changes(struct intel_plane *plane, const struct skl_plane_wm *old_wm, @@ -2722,11 +2737,8 @@ skl_print_wm_changes(struct intel_atomic_state *state) if (skl_ddb_entry_equal(old, new)) continue; - drm_dbg_kms(display->drm, - "[PLANE:%d:%s] ddb (%4d - %4d) -> (%4d - %4d), size %4d -> %4d\n", - plane->base.base.id, plane->base.name, - old->start, old->end, new->start, new->end, - skl_ddb_entry_size(old), skl_ddb_entry_size(new)); + + skl_print_plane_ddb_changes(plane, old, new, "ddb"); } for_each_intel_plane_on_crtc(display->drm, crtc, plane) { From e4ab44d8b28db868955d0da5dcadae5ef99a1dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:33 +0200 Subject: [PATCH 26/68] drm/i915/wm: Include ddb_y in skl_print_wm_changes() on pre-icl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-icl doesn't use a separate hardware plane for Y scanout, and instead it's all handled magically by the hardware. We do still need to allocate DDB space for the Y color plane though (PLANE_NV12_BUF_CFG). Include that information in the debugs so that we know where it ended up. On icl+ the equivalent information is dumped as the hardware Y plane's normal ddb allocation. v2: Use prink field width for ddb_name alignment Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-9-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 6ad0546c928f..e9cfbb7c9a77 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2735,10 +2735,17 @@ skl_print_wm_changes(struct intel_atomic_state *state) old = &old_crtc_state->wm.skl.plane_ddb[plane_id]; new = &new_crtc_state->wm.skl.plane_ddb[plane_id]; - if (skl_ddb_entry_equal(old, new)) + if (!skl_ddb_entry_equal(old, new)) + skl_print_plane_ddb_changes(plane, old, new, "ddb"); + + if (DISPLAY_VER(display) >= 11) continue; - skl_print_plane_ddb_changes(plane, old, new, "ddb"); + old = &old_crtc_state->wm.skl.plane_ddb_y[plane_id]; + new = &new_crtc_state->wm.skl.plane_ddb_y[plane_id]; + + if (!skl_ddb_entry_equal(old, new)) + skl_print_plane_ddb_changes(plane, old, new, "ddb_y"); } for_each_intel_plane_on_crtc(display->drm, crtc, plane) { From 39f4b29d9e7359d8a5a4d176f09623ad963d4d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 19 Mar 2026 13:40:34 +0200 Subject: [PATCH 27/68] drm/i915/wm: Include .min_ddb_alloc_uv in the wm dumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We include the Y/RGB .min_ddb_alloc in the wm state change dumps. Do the same for .min_ddb_alloc_uv, on the platforms where it is used. Also adjust the whitespace in the other debug prints to keep the values for each wm level lined up across all the lines. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260319114034.7093-10-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/skl_watermark.c | 30 +++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index e9cfbb7c9a77..d45b3bcc6ef0 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -2624,7 +2624,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, struct intel_display *display = to_intel_display(plane); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] level %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm" + "[PLANE:%d:%s] level %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm" " -> %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm,%cstwm\n", plane->base.base.id, plane->base.name, enast(old_wm->wm[0].enable), enast(old_wm->wm[1].enable), @@ -2643,7 +2643,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, enast(new_wm->sagv.trans_wm.enable)); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] lines %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%4d" + "[PLANE:%d:%s] lines %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%4d" " -> %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%4d\n", plane->base.base.id, plane->base.name, enast(old_wm->wm[0].ignore_lines), old_wm->wm[0].lines, @@ -2670,7 +2670,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, enast(new_wm->sagv.trans_wm.ignore_lines), new_wm->sagv.trans_wm.lines); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] blocks %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" + "[PLANE:%d:%s] blocks %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d\n", plane->base.base.id, plane->base.name, old_wm->wm[0].blocks, old_wm->wm[1].blocks, @@ -2689,7 +2689,7 @@ skl_print_plane_wm_changes(struct intel_plane *plane, new_wm->sagv.trans_wm.blocks); drm_dbg_kms(display->drm, - "[PLANE:%d:%s] min_ddb %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" + "[PLANE:%d:%s] min_ddb %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d\n", plane->base.base.id, plane->base.name, old_wm->wm[0].min_ddb_alloc, old_wm->wm[1].min_ddb_alloc, @@ -2706,6 +2706,28 @@ skl_print_plane_wm_changes(struct intel_plane *plane, new_wm->trans_wm.min_ddb_alloc, new_wm->sagv.wm0.min_ddb_alloc, new_wm->sagv.trans_wm.min_ddb_alloc); + + if (DISPLAY_VER(display) >= 11) + return; + + drm_dbg_kms(display->drm, + "[PLANE:%d:%s] min_ddb_uv %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d" + " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%5d\n", + plane->base.base.id, plane->base.name, + old_wm->wm[0].min_ddb_alloc_uv, old_wm->wm[1].min_ddb_alloc_uv, + old_wm->wm[2].min_ddb_alloc_uv, old_wm->wm[3].min_ddb_alloc_uv, + old_wm->wm[4].min_ddb_alloc_uv, old_wm->wm[5].min_ddb_alloc_uv, + old_wm->wm[6].min_ddb_alloc_uv, old_wm->wm[7].min_ddb_alloc_uv, + old_wm->trans_wm.min_ddb_alloc_uv, + old_wm->sagv.wm0.min_ddb_alloc_uv, + old_wm->sagv.trans_wm.min_ddb_alloc_uv, + new_wm->wm[0].min_ddb_alloc_uv, new_wm->wm[1].min_ddb_alloc_uv, + new_wm->wm[2].min_ddb_alloc_uv, new_wm->wm[3].min_ddb_alloc_uv, + new_wm->wm[4].min_ddb_alloc_uv, new_wm->wm[5].min_ddb_alloc_uv, + new_wm->wm[6].min_ddb_alloc_uv, new_wm->wm[7].min_ddb_alloc_uv, + new_wm->trans_wm.min_ddb_alloc_uv, + new_wm->sagv.wm0.min_ddb_alloc_uv, + new_wm->sagv.trans_wm.min_ddb_alloc_uv); } static void From fb69d0076e687421188bc8103ab0e8e5825b1df1 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 20 Mar 2026 11:29:00 +0200 Subject: [PATCH 28/68] drm/i915/dp_tunnel: Fix error handling when clearing stream BW in atomic state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clearing the DP tunnel stream BW in the atomic state involves getting the tunnel group state, which can fail. Handle the error accordingly. This fixes at least one issue where drm_dp_tunnel_atomic_set_stream_bw() failed to get the tunnel group state returning -EDEADLK, which wasn't handled. This lead to the ctx->contended warn later in modeset_lock() while taking a WW mutex for another object in the same atomic state, and thus within the same already contended WW context. Moving intel_crtc_state_alloc() later would avoid freeing saved_state on the error path; this stable patch leaves that simplification for a follow-up. Cc: Uma Shankar Cc: Ville Syrjälä Cc: # v6.9+ Fixes: a4efae87ecb2 ("drm/i915/dp: Compute DP tunnel BW during encoder state computation") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7617 Reviewed-by: Michał Grzelak Reviewed-by: Uma Shankar Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260320092900.13210-1-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 8 +++++++- .../gpu/drm/i915/display/intel_dp_tunnel.c | 20 +++++++++++++------ .../gpu/drm/i915/display/intel_dp_tunnel.h | 11 ++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index ee501009a251..882db77c0bbc 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4640,6 +4640,7 @@ intel_crtc_prepare_cleared_state(struct intel_atomic_state *state, struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_crtc_state *saved_state; + int err; saved_state = intel_crtc_state_alloc(crtc); if (!saved_state) @@ -4648,7 +4649,12 @@ intel_crtc_prepare_cleared_state(struct intel_atomic_state *state, /* free the old crtc_state->hw members */ intel_crtc_free_hw_state(crtc_state); - intel_dp_tunnel_atomic_clear_stream_bw(state, crtc_state); + err = intel_dp_tunnel_atomic_clear_stream_bw(state, crtc_state); + if (err) { + kfree(saved_state); + + return err; + } /* FIXME: before the switch to atomic started, a new pipe_config was * kzalloc'd. Code that depends on any field being zero should be diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index 1fd1ac8d556d..7363c9817297 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -659,19 +659,27 @@ int intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, * * Clear any DP tunnel stream BW requirement set by * intel_dp_tunnel_atomic_compute_stream_bw(). + * + * Returns 0 in case of success, a negative error code otherwise. */ -void intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, - struct intel_crtc_state *crtc_state) +int intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + int err; if (!crtc_state->dp_tunnel_ref.tunnel) - return; + return 0; + + err = drm_dp_tunnel_atomic_set_stream_bw(&state->base, + crtc_state->dp_tunnel_ref.tunnel, + crtc->pipe, 0); + if (err) + return err; - drm_dp_tunnel_atomic_set_stream_bw(&state->base, - crtc_state->dp_tunnel_ref.tunnel, - crtc->pipe, 0); drm_dp_tunnel_ref_put(&crtc_state->dp_tunnel_ref); + + return 0; } /** diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h index 7f0f720e8dca..10ab9eebcef6 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h @@ -40,8 +40,8 @@ int intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, struct intel_dp *intel_dp, const struct intel_connector *connector, struct intel_crtc_state *crtc_state); -void intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, - struct intel_crtc_state *crtc_state); +int intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state); int intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state, struct intel_crtc *crtc); @@ -88,9 +88,12 @@ intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, return 0; } -static inline void +static inline int intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, - struct intel_crtc_state *crtc_state) {} + struct intel_crtc_state *crtc_state) +{ + return 0; +} static inline int intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state, From 3df7e2feb8f5706eb6d00d043b9613d15c140d38 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:34 +0000 Subject: [PATCH 29/68] drm/i915/lt_phy: Dump missing PLL state parameters Dump missing PLL structure members ssc_enabled and tbt_mode in order to enhance debugging. v2: Drop addr_lsb and addr_msb printouts Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-2-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index eced8493e566..f768804122c1 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2141,7 +2141,9 @@ void intel_lt_phy_dump_hw_state(struct intel_display *display, { int i, j; - drm_dbg_kms(display->drm, "lt_phy_pll_hw_state:\n"); + drm_dbg_kms(display->drm, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", + hw_state->ssc_enabled, hw_state->tbt_mode); + for (i = 0; i < 3; i++) { drm_dbg_kms(display->drm, "config[%d] = 0x%.4x,\n", i, hw_state->config[i]); From 78ca669ca5f742eea84495f5f5e2e3c1aed82372 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:35 +0000 Subject: [PATCH 30/68] drm/i915/lt_phy: Add check if PLL is enabled Add check for PLL enabling and return early if PLL is not enabled. v2: Use PCLK PLL ACK bit to check if PLL is enabled (Suraj) v3: Check only if PCLK PLL ACK bit for lane 0 is enabled (Suraj) Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-3-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index f768804122c1..e1c95f58b6ae 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2176,6 +2176,14 @@ intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, return false; } +static bool intel_lt_phy_pll_is_enabled(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_LANE_PCLK_PLL_ACK(0); +} + void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, struct intel_lt_phy_pll_state *pll_state) @@ -2185,6 +2193,9 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct ref_tracker *wakeref; int i, j, k; + if (!intel_lt_phy_pll_is_enabled(encoder)) + return; + pll_state->tbt_mode = intel_tc_port_in_tbt_alt_mode(enc_to_dig_port(encoder)); if (pll_state->tbt_mode) return; From 0bd5b45c92c67ca43263d324d153de855e1fc6ba Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:36 +0000 Subject: [PATCH 31/68] drm/i915/lt_phy: Add PLL information for xe3plpd Start bringing in xe3plpd as part of dpll framework. The work is started by adding PLL information and related function hooks. v2: Fix xe3plpd type (Suraj) Remove empty line between BSpec link and Signed-off-by (Suraj) BSpec: 74304 Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-4-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index f35a9252f4e1..4185c8e136da 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4571,6 +4571,25 @@ static const struct intel_dpll_mgr mtl_pll_mgr = { .compare_hw_state = mtl_compare_hw_state, }; +static const struct intel_dpll_funcs xe3plpd_pll_funcs = { +}; + +static const struct dpll_info xe3plpd_plls[] = { + { .name = "DPLL 0", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + /* TODO: Add TBT */ + { .name = "TC PLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL1, }, + { .name = "TC PLL 2", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL2, }, + { .name = "TC PLL 3", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL3, }, + { .name = "TC PLL 4", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL4, }, + {} +}; + +__maybe_unused +static const struct intel_dpll_mgr xe3plpd_pll_mgr = { + .dpll_info = xe3plpd_plls, +}; + /** * intel_dpll_init - Initialize DPLLs * @display: intel_display device From f52bbb00deaaa137271217e158537151f6c792b6 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:37 +0000 Subject: [PATCH 32/68] drm/i915/lt_phy: Refactor LT PHY PLL handling to use explicit PLL state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LT PHY implementation currently pulls PLL and port_clock information directly from the CRTC state. This ties the PHY programming logic too tightly to the CRTC state and makes it harder to clearly express the PHY’s own PLL configuration. Introduce an explicit "struct intel_lt_phy_pll_state" argument for the PHY functions and update callers accordingly. No functional change is intended — this is a preparatory cleanup for to bring LT PHY PLL handling as part of PLL framework. v2: DP, HDMI 2.0, and HDMI FRL modes are port of the VDR configuration 0 register. These modes are defined by bits 2:0. Decode these to differentiate DP and HDMI modes when programming PLL's. (Imre, Suraj) v3: Pass port_clock as argument instead of recalculating it (Suraj) v4: Fix checkpatch warning of line length exceeding 100 columns BSpec: 744921 Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-5-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 67 ++++++++++++++------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index e1c95f58b6ae..2d52242cb3fc 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -32,6 +32,7 @@ INTEL_LT_PHY_LANE0) #define MODE_DP 3 #define MODE_HDMI_20 4 +#define MODE_HDMI_FRL 5 #define Q32_TO_INT(x) ((x) >> 32) #define Q32_TO_FRAC(x) ((x) & 0xFFFFFFFF) #define DCO_MIN_FREQ_MHZ 11850 @@ -1176,9 +1177,30 @@ intel_lt_phy_lane_reset(struct intel_encoder *encoder, intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_phy_pulse_status, 0); } +static bool intel_lt_phy_is_hdmi(const struct intel_lt_phy_pll_state *ltpll) +{ + u8 mode = REG_FIELD_GET8(LT_PHY_VDR_MODE_ENCODING_MASK, ltpll->config[0]); + + if (mode == MODE_HDMI_20 || mode == MODE_HDMI_FRL) + return true; + + return false; +} + +static bool intel_lt_phy_is_dp(const struct intel_lt_phy_pll_state *ltpll) +{ + u8 mode = REG_FIELD_GET8(LT_PHY_VDR_MODE_ENCODING_MASK, ltpll->config[0]); + + if (mode == MODE_DP) + return true; + + return false; +} + static void intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state, + const struct intel_lt_phy_pll_state *ltpll, + int port_clock, bool lane_reversal) { struct intel_display *display = to_intel_display(encoder); @@ -1195,17 +1217,16 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, * but since the register bits still remain the same we use * the same definition */ - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) && - intel_hdmi_is_frl(crtc_state->port_clock)) + if (intel_lt_phy_is_hdmi(ltpll) && intel_hdmi_is_frl(port_clock)) val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_DIV18CLK); else val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); /* DP2.0 10G and 20G rates enable MPLLA*/ - if (crtc_state->port_clock == 1000000 || crtc_state->port_clock == 2000000) + if (port_clock == 1000000 || port_clock == 2000000) val |= XELPDP_SSC_ENABLE_PLLA; else - val |= crtc_state->dpll_hw_state.ltpll.ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | @@ -1248,7 +1269,8 @@ static u32 intel_lt_phy_get_dp_clock(u8 rate) static bool intel_lt_phy_config_changed(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + const struct intel_lt_phy_pll_state *ltpll, + u32 port_clock) { u8 val, rate; u32 clock; @@ -1262,9 +1284,9 @@ intel_lt_phy_config_changed(struct intel_encoder *encoder, * using 1.62 Gbps clock since PHY PLL defaults to that * otherwise we always need to reconfigure it. */ - if (intel_crtc_has_dp_encoder(crtc_state)) { + if (intel_lt_phy_is_dp(ltpll)) { clock = intel_lt_phy_get_dp_clock(rate); - if (crtc_state->port_clock == 1620000 && crtc_state->port_clock == clock) + if (port_clock == 1620000 && port_clock == clock) return false; } @@ -1759,41 +1781,41 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, static void intel_lt_phy_program_pll(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + const struct intel_lt_phy_pll_state *ltpll) { u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); int i, j, k; intel_lt_phy_write(encoder, owned_lane_mask, LT_PHY_VDR_0_CONFIG, - crtc_state->dpll_hw_state.ltpll.config[0], MB_WRITE_COMMITTED); + ltpll->config[0], MB_WRITE_COMMITTED); intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_1_CONFIG, - crtc_state->dpll_hw_state.ltpll.config[1], MB_WRITE_COMMITTED); + ltpll->config[1], MB_WRITE_COMMITTED); intel_lt_phy_write(encoder, owned_lane_mask, LT_PHY_VDR_2_CONFIG, - crtc_state->dpll_hw_state.ltpll.config[2], MB_WRITE_COMMITTED); + ltpll->config[2], MB_WRITE_COMMITTED); for (i = 0; i <= 12; i++) { intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_ADDR_MSB(i), - crtc_state->dpll_hw_state.ltpll.addr_msb[i], + ltpll->addr_msb[i], MB_WRITE_COMMITTED); intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_ADDR_LSB(i), - crtc_state->dpll_hw_state.ltpll.addr_lsb[i], + ltpll->addr_lsb[i], MB_WRITE_COMMITTED); for (j = 3, k = 0; j >= 0; j--, k++) intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_DATAY(i, j), - crtc_state->dpll_hw_state.ltpll.data[i][k], + ltpll->data[i][k], MB_WRITE_COMMITTED); } } static void intel_lt_phy_enable_disable_tx(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + const struct intel_lt_phy_pll_state *ltpll, + u8 lane_count) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); bool lane_reversal = dig_port->lane_reversal; - u8 lane_count = crtc_state->lane_count; bool is_dp_alt = intel_tc_port_in_dp_alt_mode(dig_port); enum intel_tc_pin_assignment tc_pin = @@ -1895,7 +1917,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_lane_reset(encoder, crtc_state->lane_count); /* 2. Program PORT_CLOCK_CTL register to configure clock muxes, gating, and SSC. */ - intel_lt_phy_program_port_clock_ctl(encoder, crtc_state, lane_reversal); + intel_lt_phy_program_port_clock_ctl(encoder, &crtc_state->dpll_hw_state.ltpll, + crtc_state->port_clock, lane_reversal); /* 3. Change owned PHY lanes power to Ready state. */ intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, @@ -1905,12 +1928,13 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * 4. Read the PHY message bus VDR register PHY_VDR_0_Config check enabled PLL type, * encoded rate and encoded mode. */ - if (intel_lt_phy_config_changed(encoder, crtc_state)) { + if (intel_lt_phy_config_changed(encoder, &crtc_state->dpll_hw_state.ltpll, + crtc_state->port_clock)) { /* * 5. Program the PHY internal PLL registers over PHY message bus for the desired * frequency and protocol type */ - intel_lt_phy_program_pll(encoder, crtc_state); + intel_lt_phy_program_pll(encoder, &crtc_state->dpll_hw_state.ltpll); /* 6. Use the P2P transaction flow */ /* @@ -2001,7 +2025,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, XELPDP_P0_STATE_ACTIVE); - intel_lt_phy_enable_disable_tx(encoder, crtc_state); + intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll, + crtc_state->lane_count); intel_lt_phy_transaction_end(encoder, wakeref); } From 6e1c3b80ee801d1450a20a5420e79f9460bc5f0b Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:38 +0000 Subject: [PATCH 33/68] drm/i915/lt_phy: Add lane_count to PLL state Cache lane count as part of PLL state. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-6-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.h | 1 + drivers/gpu/drm/i915/display/intel_lt_phy.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index 4cc14ce5eebe..d408ccf6f902 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -278,6 +278,7 @@ struct intel_lt_phy_pll_state { u8 config[3]; bool ssc_enabled; bool tbt_mode; + int lane_count; }; struct intel_dpll_hw_state { diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 2d52242cb3fc..3e83ac775d84 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1767,11 +1767,13 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, } crtc_state->dpll_hw_state.ltpll.ssc_enabled = intel_lt_phy_pll_is_ssc_enabled(crtc_state, encoder); + crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; return 0; } } if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) { + crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; return intel_lt_phy_calculate_hdmi_state(&crtc_state->dpll_hw_state.ltpll, crtc_state->port_clock); } @@ -1811,11 +1813,11 @@ intel_lt_phy_program_pll(struct intel_encoder *encoder, static void intel_lt_phy_enable_disable_tx(struct intel_encoder *encoder, - const struct intel_lt_phy_pll_state *ltpll, - u8 lane_count) + const struct intel_lt_phy_pll_state *ltpll) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); bool lane_reversal = dig_port->lane_reversal; + u8 lane_count = ltpll->lane_count; bool is_dp_alt = intel_tc_port_in_dp_alt_mode(dig_port); enum intel_tc_pin_assignment tc_pin = @@ -2025,8 +2027,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, XELPDP_P0_STATE_ACTIVE); - intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll, - crtc_state->lane_count); + intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll); intel_lt_phy_transaction_end(encoder, wakeref); } From 3ce06de38959fdadb2da7350918ff963069463fb Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:39 +0000 Subject: [PATCH 34/68] drm/i915/lt_phy: Add xe3plpd .compute_dplls hook Add compute dpll hook for xe3plpd platform and bring PLL state calculation to support PLL framework. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-7-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 65 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 17 +++-- drivers/gpu/drm/i915/display/intel_lt_phy.h | 3 +- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 8433e3ff0319..147baa777856 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1222,7 +1222,7 @@ static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *state, struct intel_display *display = to_intel_display(encoder); int ret; - ret = intel_lt_phy_pll_calc_state(crtc_state, encoder); + ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &crtc_state->dpll_hw_state); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 4185c8e136da..58c24e2164ca 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4585,9 +4585,74 @@ static const struct dpll_info xe3plpd_plls[] = { {} }; +static int xe3plpd_compute_non_tc_phy_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct icl_port_dpll *port_dpll = + &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + int ret; + + ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &port_dpll->hw_state); + if (ret) + return ret; + + /* this is mainly for the fastset check */ + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + + crtc_state->port_clock = intel_lt_phy_calc_port_clock(display, &port_dpll->hw_state.ltpll); + + return 0; +} + +static int xe3plpd_compute_tc_phy_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + struct icl_port_dpll *port_dpll; + int ret; + + /* TODO: Add state calculation for TBT PLL */ + + port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_MG_PHY]; + ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &port_dpll->hw_state); + if (ret) + return ret; + + /* this is mainly for the fastset check */ + if (old_crtc_state->intel_dpll && + old_crtc_state->intel_dpll->info->id == DPLL_ID_ICL_TBTPLL) + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + else + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_MG_PHY); + + crtc_state->port_clock = intel_lt_phy_calc_port_clock(display, &port_dpll->hw_state.ltpll); + + return 0; +} + +static int xe3plpd_compute_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + if (intel_encoder_is_tc(encoder)) + return xe3plpd_compute_tc_phy_dplls(state, crtc, encoder); + else + return xe3plpd_compute_non_tc_phy_dpll(state, crtc, encoder); +} + __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, + .compute_dplls = xe3plpd_compute_dplls, }; /** diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 3e83ac775d84..ed5cfd6641b1 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1745,12 +1745,15 @@ intel_lt_phy_calc_port_clock(struct intel_display *display, int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder) + struct intel_encoder *encoder, + struct intel_dpll_hw_state *hw_state) { struct intel_display *display = to_intel_display(crtc_state); const struct intel_lt_phy_pll_params *tables; int i; + memset(hw_state, 0, sizeof(*hw_state)); + tables = intel_lt_phy_pll_tables_get(crtc_state, encoder); if (!tables) return -EINVAL; @@ -1760,21 +1763,21 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, drm_WARN_ON(display->drm, !intel_dpll_clock_matches(clock, tables[i].clock_rate)); if (intel_dpll_clock_matches(crtc_state->port_clock, clock)) { - crtc_state->dpll_hw_state.ltpll = *tables[i].state; + hw_state->ltpll = *tables[i].state; if (intel_crtc_has_dp_encoder(crtc_state)) { if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) - crtc_state->dpll_hw_state.ltpll.config[2] = 1; + hw_state->ltpll.config[2] = 1; } - crtc_state->dpll_hw_state.ltpll.ssc_enabled = + hw_state->ltpll.ssc_enabled = intel_lt_phy_pll_is_ssc_enabled(crtc_state, encoder); - crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; + hw_state->ltpll.lane_count = crtc_state->lane_count; return 0; } } if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) { - crtc_state->dpll_hw_state.ltpll.lane_count = crtc_state->lane_count; - return intel_lt_phy_calculate_hdmi_state(&crtc_state->dpll_hw_state.ltpll, + hw_state->ltpll.lane_count = crtc_state->lane_count; + return intel_lt_phy_calculate_hdmi_state(&hw_state->ltpll, crtc_state->port_clock); } diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index db905668f86d..61ec0e5d8888 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -20,7 +20,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, void intel_lt_phy_pll_disable(struct intel_encoder *encoder); int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder); + struct intel_encoder *encoder, + struct intel_dpll_hw_state *hw_state); int intel_lt_phy_calc_port_clock(struct intel_display *display, const struct intel_lt_phy_pll_state *lt_state); void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, From 565604328606bf6c187007dc1b7dfe151bbc6ba9 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:40 +0000 Subject: [PATCH 35/68] drm/i915/lt_phy: Add xe3plpd .get_dplls hook Add .get_dplls function pointer for xe3plpd platforms to support dpll framework. Reuse the ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-8-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 58c24e2164ca..9aa8eb0a7d4a 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4653,6 +4653,7 @@ __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, .compute_dplls = xe3plpd_compute_dplls, + .get_dplls = mtl_get_dplls, }; /** From a04b8125db835418560fdab82ed47685ab77644d Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:41 +0000 Subject: [PATCH 36/68] drm/i915/lt_phy: Add xe3plpd .put_dplls hook Add .put_dplls function pointer to support xe3plpd platform on dpll framework. Reuse ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-9-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 9aa8eb0a7d4a..af2613eeaf92 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4654,6 +4654,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, .compute_dplls = xe3plpd_compute_dplls, .get_dplls = mtl_get_dplls, + .put_dplls = icl_put_dplls, }; /** From 1e8988c328e09550f683fa8adc51eb8553ab6fb8 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:42 +0000 Subject: [PATCH 37/68] drm/i915/lt_phy: Add xe3plpd .update_active_dpll hook Add .update_active_dpll function pointer to support dpll framework for xe3plpd platform. Reuse ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-10-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index af2613eeaf92..c1ed44b23bba 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4655,6 +4655,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .compute_dplls = xe3plpd_compute_dplls, .get_dplls = mtl_get_dplls, .put_dplls = icl_put_dplls, + .update_active_dpll = icl_update_active_dpll, }; /** From 658b554a8b0c6e8306d11f1e074e8445dc002d54 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:43 +0000 Subject: [PATCH 38/68] drm/i915/lt_phy: Add xe3plpd .update_dpll_ref_clks hook Add .update_dpll_ref_clks function pointer to xe3plpd platform to support dpll framework. Reuse ICL function pointer. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-11-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index c1ed44b23bba..b50f02303356 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4656,6 +4656,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .get_dplls = mtl_get_dplls, .put_dplls = icl_put_dplls, .update_active_dpll = icl_update_active_dpll, + .update_ref_clks = icl_update_dpll_ref_clks, }; /** From a8acd1a61a03191c6426e2bcd2f0b3b576d0d789 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:44 +0000 Subject: [PATCH 39/68] drm/i915/lt_phy: Add xe3plpd .dump_hw_state hook Add .dump_hw_state function pointer for xe3plpd platform to support dpll framework. While at it, switch to use drm_printer structure to print hw state information. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-12-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 5 ++--- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 7 +++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 16 ++++++++-------- drivers/gpu/drm/i915/display/intel_lt_phy.h | 3 ++- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 882db77c0bbc..dfc28af1ef88 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5070,15 +5070,14 @@ pipe_config_lt_phy_pll_mismatch(struct drm_printer *p, bool fastset, const struct intel_lt_phy_pll_state *a, const struct intel_lt_phy_pll_state *b) { - struct intel_display *display = to_intel_display(crtc); char *chipname = "LTPHY"; pipe_config_mismatch(p, fastset, crtc, name, chipname); drm_printf(p, "expected:\n"); - intel_lt_phy_dump_hw_state(display, a); + intel_lt_phy_dump_hw_state(p, a); drm_printf(p, "found:\n"); - intel_lt_phy_dump_hw_state(display, b); + intel_lt_phy_dump_hw_state(p, b); } bool diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index b50f02303356..26b78063dd94 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4649,6 +4649,12 @@ static int xe3plpd_compute_dplls(struct intel_atomic_state *state, return xe3plpd_compute_non_tc_phy_dpll(state, crtc, encoder); } +static void xe3plpd_dump_hw_state(struct drm_printer *p, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + intel_lt_phy_dump_hw_state(p, &dpll_hw_state->ltpll); +} + __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, @@ -4657,6 +4663,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .put_dplls = icl_put_dplls, .update_active_dpll = icl_update_active_dpll, .update_ref_clks = icl_update_dpll_ref_clks, + .dump_hw_state = xe3plpd_dump_hw_state, }; /** diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index ed5cfd6641b1..e9a0631a2012 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2165,23 +2165,23 @@ void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, intel_lt_phy_transaction_end(encoder, wakeref); } -void intel_lt_phy_dump_hw_state(struct intel_display *display, +void intel_lt_phy_dump_hw_state(struct drm_printer *p, const struct intel_lt_phy_pll_state *hw_state) { int i, j; - drm_dbg_kms(display->drm, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", - hw_state->ssc_enabled, hw_state->tbt_mode); + drm_printf(p, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", + hw_state->ssc_enabled, hw_state->tbt_mode); for (i = 0; i < 3; i++) { - drm_dbg_kms(display->drm, "config[%d] = 0x%.4x,\n", - i, hw_state->config[i]); + drm_printf(p, "config[%d] = 0x%.4x,\n", + i, hw_state->config[i]); } for (i = 0; i <= 12; i++) for (j = 3; j >= 0; j--) - drm_dbg_kms(display->drm, "vdr_data[%d][%d] = 0x%.4x,\n", - i, j, hw_state->data[i][j]); + drm_printf(p, "vdr_data[%d][%d] = 0x%.4x,\n", + i, j, hw_state->data[i][j]); } bool @@ -2336,7 +2336,7 @@ static void intel_lt_phy_pll_verify_clock(struct intel_display *display, drm_printf(&p, "PLL state %s (%s):\n", pll_state_name, is_precomputed_state ? "precomputed" : "computed"); - intel_lt_phy_dump_hw_state(display, pll_state); + intel_lt_phy_dump_hw_state(&p, pll_state); } static void intel_lt_phy_pll_verify_params(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index 61ec0e5d8888..b208bbd6f8ca 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -8,6 +8,7 @@ #include +struct drm_printer; struct intel_atomic_state; struct intel_display; struct intel_encoder; @@ -26,7 +27,7 @@ int intel_lt_phy_calc_port_clock(struct intel_display *display, const struct intel_lt_phy_pll_state *lt_state); void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); -void intel_lt_phy_dump_hw_state(struct intel_display *display, +void intel_lt_phy_dump_hw_state(struct drm_printer *p, const struct intel_lt_phy_pll_state *hw_state); bool intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, From 37115f773c8c16eca0dbcb9399aa435111a7b34e Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:45 +0000 Subject: [PATCH 40/68] drm/i915/lt_phy: Add xe3plpd .compare_hw_state hook Add .compare_hw_state function pointer for xe3plpd platform to support dpll framework. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-13-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 26b78063dd94..c1d7d9909544 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4655,6 +4655,15 @@ static void xe3plpd_dump_hw_state(struct drm_printer *p, intel_lt_phy_dump_hw_state(p, &dpll_hw_state->ltpll); } +static bool xe3plpd_compare_hw_state(const struct intel_dpll_hw_state *_a, + const struct intel_dpll_hw_state *_b) +{ + const struct intel_lt_phy_pll_state *a = &_a->ltpll; + const struct intel_lt_phy_pll_state *b = &_b->ltpll; + + return intel_lt_phy_pll_compare_hw_state(a, b); +} + __maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, @@ -4664,6 +4673,7 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .update_active_dpll = icl_update_active_dpll, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = xe3plpd_dump_hw_state, + .compare_hw_state = xe3plpd_compare_hw_state, }; /** From c62ba60b1098ff05528f13e75eca811b13e2c0c5 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:46 +0000 Subject: [PATCH 41/68] drm/i915/lt_phy: Add xe3plpd .get_hw_state hook Add .get_hw_state hook to xe3plpd platform for dpll framework and update intel_lt_phy_pll_readout_hw_state() function accordingly to support dpll framework. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-14-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 +++++++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 11 ++++++----- drivers/gpu/drm/i915/display/intel_lt_phy.h | 3 +-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 7f1576bfe4b0..dbf3f344e014 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4248,7 +4248,7 @@ static void xe3plpd_ddi_get_config(struct intel_encoder *encoder, { struct intel_display *display = to_intel_display(encoder); - intel_lt_phy_pll_readout_hw_state(encoder, crtc_state, &crtc_state->dpll_hw_state.ltpll); + intel_lt_phy_pll_readout_hw_state(encoder, &crtc_state->dpll_hw_state.ltpll); if (crtc_state->dpll_hw_state.ltpll.tbt_mode) crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index c1d7d9909544..6502916793f5 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4571,7 +4571,20 @@ static const struct intel_dpll_mgr mtl_pll_mgr = { .compare_hw_state = mtl_compare_hw_state, }; +static bool xe3plpd_pll_get_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (!encoder) + return false; + + return intel_lt_phy_pll_readout_hw_state(encoder, &dpll_hw_state->ltpll); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { + .get_hw_state = xe3plpd_pll_get_hw_state, }; static const struct dpll_info xe3plpd_plls[] = { diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index e9a0631a2012..1760e1a732dc 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2213,8 +2213,7 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) XELPDP_LANE_PCLK_PLL_ACK(0); } -void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state, +bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state) { u8 owned_lane_mask; @@ -2223,11 +2222,11 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, int i, j, k; if (!intel_lt_phy_pll_is_enabled(encoder)) - return; + return false; pll_state->tbt_mode = intel_tc_port_in_tbt_alt_mode(enc_to_dig_port(encoder)); if (pll_state->tbt_mode) - return; + return false; owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1; @@ -2245,6 +2244,8 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, } intel_lt_phy_transaction_end(encoder, wakeref); + + return true; } void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, @@ -2270,7 +2271,7 @@ void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, return; encoder = intel_get_crtc_new_encoder(state, new_crtc_state); - intel_lt_phy_pll_readout_hw_state(encoder, new_crtc_state, &pll_hw_state); + intel_lt_phy_pll_readout_hw_state(encoder, &pll_hw_state); dig_port = enc_to_dig_port(encoder); if (intel_tc_port_in_tbt_alt_mode(dig_port)) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index b208bbd6f8ca..0053bb5489e5 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -32,8 +32,7 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, bool intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, const struct intel_lt_phy_pll_state *b); -void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state, +bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state); void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, struct intel_crtc *crtc); From ff684c7eaca3da6f4436ef268da9ded7d0c4c4d4 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:47 +0000 Subject: [PATCH 42/68] drm/i915/lt_phy: Add xe3plpd .get_freq hook Add .get_freq function hook to support dpll framework for xe3plpd platform. v2: Restore port clock calculation (Suraj) Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-15-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 6502916793f5..412582e29ca6 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4583,8 +4583,21 @@ static bool xe3plpd_pll_get_hw_state(struct intel_display *display, return intel_lt_phy_pll_readout_hw_state(encoder, &dpll_hw_state->ltpll); } +static int xe3plpd_pll_get_freq(struct intel_display *display, + const struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return -EINVAL; + + return intel_lt_phy_calc_port_clock(display, &dpll_hw_state->ltpll); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { .get_hw_state = xe3plpd_pll_get_hw_state, + .get_freq = xe3plpd_pll_get_freq, }; static const struct dpll_info xe3plpd_plls[] = { From 27d911f87f96cd55461acf2468527f98eefce3ed Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:48 +0000 Subject: [PATCH 43/68] drm/i915/lt_phy: Add xe3plpd .crtc_get_dpll Add .crtc_get_dpll function pointer to support xe3plpd platform. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-16-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 147baa777856..e13a5e12109d 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1696,6 +1696,7 @@ static int i8xx_crtc_compute_clock(struct intel_atomic_state *state, static const struct intel_dpll_global_funcs xe3plpd_dpll_funcs = { .crtc_compute_clock = xe3plpd_crtc_compute_clock, + .crtc_get_dpll = hsw_crtc_get_dpll, }; static const struct intel_dpll_global_funcs mtl_dpll_funcs = { From 5ec58d714935c2671ffab58d8ac23b9224b66936 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:49 +0000 Subject: [PATCH 44/68] drm/i915/lt_phy: Add .enable_clock hook on DDI Enable PLL clock on DDI by moving part of the PLL enabling sequence into a DDI clock enabling function. v2: Reuse intel_mtl_pll_enable_clock for DDI clock enabling Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-17-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 ++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.c | 32 ++++++++----------- drivers/gpu/drm/i915/display/intel_lt_phy.h | 8 +++-- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index dbf3f344e014..93f62d995e96 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -5298,7 +5298,7 @@ void intel_ddi_init(struct intel_display *display, encoder->pipe_mask = ~0; if (HAS_LT_PHY(display)) { - encoder->enable_clock = intel_xe3plpd_pll_enable; + encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_xe3plpd_pll_disable; encoder->port_pll_type = intel_mtl_port_pll_type; encoder->get_config = xe3plpd_ddi_get_config; diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 412582e29ca6..54c7a255b3a5 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4595,7 +4595,20 @@ static int xe3plpd_pll_get_freq(struct intel_display *display, return intel_lt_phy_calc_port_clock(display, &dpll_hw_state->ltpll); } +static void xe3plpd_pll_enable(struct intel_display *display, + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_xe3plpd_pll_enable(encoder, pll, dpll_hw_state); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { + .enable = xe3plpd_pll_enable, .get_hw_state = xe3plpd_pll_get_hw_state, .get_freq = xe3plpd_pll_get_freq, }; diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 1760e1a732dc..dfcff3d6ad33 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1901,9 +1901,11 @@ intel_lt_phy_enable_disable_tx(struct intel_encoder *encoder, } void intel_lt_phy_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) { struct intel_display *display = to_intel_display(encoder); + int port_clock = intel_lt_phy_calc_port_clock(display, &dpll_hw_state->ltpll); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); bool lane_reversal = dig_port->lane_reversal; u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); @@ -1919,11 +1921,11 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, wakeref = intel_lt_phy_transaction_begin(encoder); /* 1. Enable MacCLK at default 162 MHz frequency. */ - intel_lt_phy_lane_reset(encoder, crtc_state->lane_count); + intel_lt_phy_lane_reset(encoder, dpll_hw_state->ltpll.lane_count); /* 2. Program PORT_CLOCK_CTL register to configure clock muxes, gating, and SSC. */ - intel_lt_phy_program_port_clock_ctl(encoder, &crtc_state->dpll_hw_state.ltpll, - crtc_state->port_clock, lane_reversal); + intel_lt_phy_program_port_clock_ctl(encoder, &dpll_hw_state->ltpll, + port_clock, lane_reversal); /* 3. Change owned PHY lanes power to Ready state. */ intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, @@ -1933,13 +1935,12 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * 4. Read the PHY message bus VDR register PHY_VDR_0_Config check enabled PLL type, * encoded rate and encoded mode. */ - if (intel_lt_phy_config_changed(encoder, &crtc_state->dpll_hw_state.ltpll, - crtc_state->port_clock)) { + if (intel_lt_phy_config_changed(encoder, &dpll_hw_state->ltpll, port_clock)) { /* * 5. Program the PHY internal PLL registers over PHY message bus for the desired * frequency and protocol type */ - intel_lt_phy_program_pll(encoder, &crtc_state->dpll_hw_state.ltpll); + intel_lt_phy_program_pll(encoder, &dpll_hw_state->ltpll); /* 6. Use the P2P transaction flow */ /* @@ -1971,8 +1972,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * Change. We handle this step in bxt_set_cdclk(). */ /* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */ - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), - crtc_state->port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); /* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port), @@ -2019,7 +2019,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, lane_phy_pulse_status, lane_phy_pulse_status); } else { - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), crtc_state->port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); } /* @@ -2030,7 +2030,7 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask, XELPDP_P0_STATE_ACTIVE); - intel_lt_phy_enable_disable_tx(encoder, &crtc_state->dpll_hw_state.ltpll); + intel_lt_phy_enable_disable_tx(encoder, &dpll_hw_state->ltpll); intel_lt_phy_transaction_end(encoder, wakeref); } @@ -2288,14 +2288,10 @@ void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, } void intel_xe3plpd_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) { - 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); - else - intel_lt_phy_pll_enable(encoder, crtc_state); + intel_lt_phy_pll_enable(encoder, pll, dpll_hw_state); } void intel_xe3plpd_pll_disable(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index 0053bb5489e5..d8d5c2064b6b 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -11,13 +11,16 @@ struct drm_printer; struct intel_atomic_state; struct intel_display; +struct intel_dpll; +struct intel_dpll_hw_state; struct intel_encoder; struct intel_crtc_state; struct intel_crtc; struct intel_lt_phy_pll_state; void intel_lt_phy_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state); + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state); void intel_lt_phy_pll_disable(struct intel_encoder *encoder); int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, @@ -40,7 +43,8 @@ int intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state, u32 frequency_khz); void intel_xe3plpd_pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state); + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state); void intel_xe3plpd_pll_disable(struct intel_encoder *encoder); void intel_lt_phy_verify_plls(struct intel_display *display); From cfe8715427dad345b3ce78096081354201ad18af Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:50 +0000 Subject: [PATCH 45/68] drm/i915/lt_phy: Add .disable_clock hook on DDI Add new pll_disable_clock functions so that they can be hooked up to dpll->disable. This is just a wrapper over the exitisting intel_xe3plpd_pll_disable to make it compatible With dpll->disable function v2: Revise commit message (Suraj) Drop wrapper for TBT clock disabling and reuse intel_mtl_pll_disable_clock() for DDI clock disabling hook (Suraj) Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-18-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 93f62d995e96..3cc8c681c352 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -5299,7 +5299,7 @@ void intel_ddi_init(struct intel_display *display, if (HAS_LT_PHY(display)) { encoder->enable_clock = intel_mtl_pll_enable_clock; - encoder->disable_clock = intel_xe3plpd_pll_disable; + encoder->disable_clock = intel_mtl_pll_disable_clock; encoder->port_pll_type = intel_mtl_port_pll_type; encoder->get_config = xe3plpd_ddi_get_config; } else if (DISPLAY_VER(display) >= 14) { diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 54c7a255b3a5..28c560417409 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4607,8 +4607,20 @@ static void xe3plpd_pll_enable(struct intel_display *display, intel_xe3plpd_pll_enable(encoder, pll, dpll_hw_state); } +static void xe3plpd_pll_disable(struct intel_display *display, + struct intel_dpll *pll) +{ + struct intel_encoder *encoder = get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_xe3plpd_pll_disable(encoder); +} + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { .enable = xe3plpd_pll_enable, + .disable = xe3plpd_pll_disable, .get_hw_state = xe3plpd_pll_get_hw_state, .get_freq = xe3plpd_pll_get_freq, }; From 7049d9a773f3ec8a15eb873e2017ed5287b8c96c Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:51 +0000 Subject: [PATCH 46/68] drm/i915/lt_phy: Dump lane count for HW state To increase debuggability add lane count as part of HW state dump. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-19-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 4 ++-- 1 file changed, 2 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 dfcff3d6ad33..62719082efda 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2170,8 +2170,8 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, { int i, j; - drm_printf(p, "lt_phy_pll_hw_state: ssc enabled: %d, tbt mode: %d\n", - hw_state->ssc_enabled, hw_state->tbt_mode); + 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); for (i = 0; i < 3; i++) { drm_printf(p, "config[%d] = 0x%.4x,\n", From ef5aa934a3c962fcd29b41926d41a86d8274239f Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:52 +0000 Subject: [PATCH 47/68] drm/i915/lt_phy: Readout lane count Readout lane count back from HW. Reuse existing function for Cx0 for LT PHY case with minor modification to add lanes as function parameters. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-20-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 10 ++++++---- drivers/gpu/drm/i915/display/intel_cx0_phy.h | 1 + drivers/gpu/drm/i915/display/intel_lt_phy.c | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 6a471c021c0e..7e59409bbf01 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2180,7 +2180,7 @@ static int intel_c10pll_calc_state(const struct intel_crtc_state *crtc_state, return 0; } -static int readout_enabled_lane_count(struct intel_encoder *encoder) +int intel_readout_lane_count(struct intel_encoder *encoder, int lane0, int lane1) { struct intel_display *display = to_intel_display(encoder); u8 enabled_tx_lane_count = 0; @@ -2212,7 +2212,7 @@ static int readout_enabled_lane_count(struct intel_encoder *encoder) max_tx_lane_count = round_up(max_tx_lane_count, 2); for (tx_lane = 0; tx_lane < max_tx_lane_count; tx_lane++) { - u8 phy_lane_mask = tx_lane < 2 ? INTEL_CX0_LANE0 : INTEL_CX0_LANE1; + u8 phy_lane_mask = tx_lane < 2 ? lane0 : lane1; int tx = tx_lane % 2 + 1; u8 val; @@ -2252,7 +2252,8 @@ static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, */ intel_c10_msgbus_access_begin(encoder, lane); - cx0pll_state->lane_count = readout_enabled_lane_count(encoder); + cx0pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_CX0_LANE0, + INTEL_CX0_LANE1); for (i = 0; i < ARRAY_SIZE(pll_state->pll); i++) pll_state->pll[i] = intel_cx0_read(encoder, lane, PHY_C10_VDR_PLL(i)); @@ -2707,7 +2708,8 @@ static void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, wakeref = intel_cx0_phy_transaction_begin(encoder); - cx0pll_state->lane_count = readout_enabled_lane_count(encoder); + cx0pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_CX0_LANE0, + INTEL_CX0_LANE1); /* 1. Read VDR params and current context selection */ intel_c20_readout_vdr_params(encoder, &pll_state->vdr, &cntx); diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 1d4480b8bf39..1428e7a5a318 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -28,6 +28,7 @@ struct intel_hdmi; void intel_cx0_clear_response_ready_flag(struct intel_encoder *encoder, int lane); bool intel_encoder_is_c10phy(struct intel_encoder *encoder); +int intel_readout_lane_count(struct intel_encoder *encoder, int lane0, int lane1); void intel_mtl_pll_enable(struct intel_encoder *encoder, struct intel_dpll *pll, const struct intel_dpll_hw_state *dpll_hw_state); diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 62719082efda..e9fce044b99d 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2232,6 +2232,8 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1; wakeref = intel_lt_phy_transaction_begin(encoder); + pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_LT_PHY_LANE0, + INTEL_LT_PHY_LANE1); pll_state->config[0] = intel_lt_phy_read(encoder, lane, LT_PHY_VDR_0_CONFIG); pll_state->config[1] = intel_lt_phy_read(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_1_CONFIG); pll_state->config[2] = intel_lt_phy_read(encoder, lane, LT_PHY_VDR_2_CONFIG); From c071495ccd89955ca6c54608bd8d5dc31574ed0a Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:53 +0000 Subject: [PATCH 48/68] drm/i915/lt_phy: Get encoder configuration for xe3plpd platform Reuse mtl_ddi_*_get_config functions now that all hooks are in place. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-21-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 3cc8c681c352..fe024250d350 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4243,21 +4243,6 @@ void intel_ddi_get_clock(struct intel_encoder *encoder, &crtc_state->dpll_hw_state); } -static void xe3plpd_ddi_get_config(struct intel_encoder *encoder, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(encoder); - - intel_lt_phy_pll_readout_hw_state(encoder, &crtc_state->dpll_hw_state.ltpll); - - if (crtc_state->dpll_hw_state.ltpll.tbt_mode) - crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder); - else - crtc_state->port_clock = - intel_lt_phy_calc_port_clock(display, &crtc_state->dpll_hw_state.ltpll); - intel_ddi_get_config(encoder, crtc_state); -} - static bool icl_ddi_tc_pll_is_tbt(const struct intel_dpll *pll) { return pll->info->id == DPLL_ID_ICL_TBTPLL; @@ -5301,7 +5286,10 @@ void intel_ddi_init(struct intel_display *display, encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_mtl_pll_disable_clock; encoder->port_pll_type = intel_mtl_port_pll_type; - encoder->get_config = xe3plpd_ddi_get_config; + if (intel_encoder_is_tc(encoder)) + encoder->get_config = mtl_ddi_tc_phy_get_config; + else + encoder->get_config = mtl_ddi_non_tc_phy_get_config; } else if (DISPLAY_VER(display) >= 14) { encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_mtl_pll_disable_clock; From a60d70847c5badbe624b0a6a175448ed4ad1073f Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:54 +0000 Subject: [PATCH 49/68] drm/i915/lt_phy: Add xe3plpd Thunderbolt PLL hooks Add the PLL hooks for the TBT PLL on xe3plpd. These are simple stubs similar to the TBT PLL on earlier platforms, since this PLL is always on from the display POV - so no PLL enable/disable programming is required as opposed to the non-TBT PLLs - and the clocks for different link rates are enabled/disabled at a different level, via the intel_encoder::enable_clock()/disable_clock() interface. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-22-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 13 +++++++++++-- drivers/gpu/drm/i915/display/intel_lt_phy.c | 18 ++++++++++++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.h | 4 ++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 28c560417409..534cc691979f 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4618,6 +4618,13 @@ static void xe3plpd_pll_disable(struct intel_display *display, intel_xe3plpd_pll_disable(encoder); } +static const struct intel_dpll_funcs xe3plpd_tbt_pll_funcs = { + .enable = mtl_tbt_pll_enable, + .disable = mtl_tbt_pll_disable, + .get_hw_state = intel_lt_phy_tbt_pll_readout_hw_state, + .get_freq = mtl_tbt_pll_get_freq, +}; + static const struct intel_dpll_funcs xe3plpd_pll_funcs = { .enable = xe3plpd_pll_enable, .disable = xe3plpd_pll_disable, @@ -4628,7 +4635,8 @@ static const struct intel_dpll_funcs xe3plpd_pll_funcs = { static const struct dpll_info xe3plpd_plls[] = { { .name = "DPLL 0", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, { .name = "DPLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, - /* TODO: Add TBT */ + { .name = "TBT PLL", .funcs = &xe3plpd_tbt_pll_funcs, .id = DPLL_ID_ICL_TBTPLL, + .is_alt_port_dpll = true, .always_on = true }, { .name = "TC PLL 1", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL1, }, { .name = "TC PLL 2", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL2, }, { .name = "TC PLL 3", .funcs = &xe3plpd_pll_funcs, .id = DPLL_ID_ICL_MGPLL3, }, @@ -4671,7 +4679,8 @@ static int xe3plpd_compute_tc_phy_dplls(struct intel_atomic_state *state, struct icl_port_dpll *port_dpll; int ret; - /* TODO: Add state calculation for TBT PLL */ + port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + intel_lt_phy_tbt_pll_calc_state(&port_dpll->hw_state); port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_MG_PHY]; ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &port_dpll->hw_state); diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index e9fce044b99d..dd8b99f8821e 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1784,6 +1784,13 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, return -EINVAL; } +void intel_lt_phy_tbt_pll_calc_state(struct intel_dpll_hw_state *hw_state) +{ + memset(hw_state, 0, sizeof(*hw_state)); + + hw_state->ltpll.tbt_mode = true; +} + static void intel_lt_phy_program_pll(struct intel_encoder *encoder, const struct intel_lt_phy_pll_state *ltpll) @@ -2213,6 +2220,17 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) XELPDP_LANE_PCLK_PLL_ACK(0); } +bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *hw_state) +{ + memset(hw_state, 0, sizeof(*hw_state)); + + hw_state->ltpll.tbt_mode = true; + + return true; +} + bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state) { diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index d8d5c2064b6b..147ae431713d 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -26,6 +26,7 @@ int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder, struct intel_dpll_hw_state *hw_state); +void intel_lt_phy_tbt_pll_calc_state(struct intel_dpll_hw_state *hw_state); int intel_lt_phy_calc_port_clock(struct intel_display *display, const struct intel_lt_phy_pll_state *lt_state); void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder, @@ -35,6 +36,9 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, bool intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a, const struct intel_lt_phy_pll_state *b); +bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *hw_state); bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state); void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, From 35349695ada2d4888a267afbe1f920c069264aa5 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:55 +0000 Subject: [PATCH 50/68] drm/i915/lt_phy: Remove LT PHY specific state verification Remove LT PHY specific state verification as DPLL framework has state verification check. v2: Reuse intel_lt_phy_pll_compare_hw_state() as only config[0] and config[0] parameters are reliable with LT PHY (Suraj) v3: Rephrase handling of LT PHY case when verifying the state (CI) v4: Fix checkpatch warning of line length exceeding 100 columns Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-23-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 16 ++++++-- drivers/gpu/drm/i915/display/intel_lt_phy.c | 39 ------------------- drivers/gpu/drm/i915/display/intel_lt_phy.h | 2 - .../drm/i915/display/intel_modeset_verify.c | 1 - 4 files changed, 13 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 534cc691979f..421767dd8367 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -5075,6 +5075,7 @@ verify_single_dpll_state(struct intel_display *display, const struct intel_crtc_state *new_crtc_state) { struct intel_dpll_hw_state dpll_hw_state = {}; + bool pll_mismatch = false; u8 pipe_mask; bool active; @@ -5116,9 +5117,18 @@ verify_single_dpll_state(struct intel_display *display, "%s: pll enabled crtcs mismatch (expected 0x%x in 0x%x)\n", pll->info->name, pipe_mask, pll->state.pipe_mask); - if (INTEL_DISPLAY_STATE_WARN(display, - pll->on && memcmp(&pll->state.hw_state, &dpll_hw_state, - sizeof(dpll_hw_state)), + if (pll->on) { + const struct intel_dpll_mgr *dpll_mgr = display->dpll.mgr; + + if (HAS_LT_PHY(display)) + pll_mismatch = !dpll_mgr->compare_hw_state(&pll->state.hw_state, + &dpll_hw_state); + else + pll_mismatch = memcmp(&pll->state.hw_state, &dpll_hw_state, + sizeof(dpll_hw_state)); + } + + if (INTEL_DISPLAY_STATE_WARN(display, pll_mismatch, "%s: pll hw state mismatch\n", pll->info->name)) { struct drm_printer p = drm_dbg_printer(display->drm, DRM_UT_KMS, NULL); diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index dd8b99f8821e..5bbbc6182861 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2268,45 +2268,6 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, return true; } -void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct intel_display *display = to_intel_display(state); - struct intel_digital_port *dig_port; - const struct intel_crtc_state *new_crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_encoder *encoder; - struct intel_lt_phy_pll_state pll_hw_state = {}; - const struct intel_lt_phy_pll_state *pll_sw_state = &new_crtc_state->dpll_hw_state.ltpll; - - if (DISPLAY_VER(display) < 35) - return; - - if (!new_crtc_state->hw.active) - return; - - /* intel_get_crtc_new_encoder() only works for modeset/fastset commits */ - if (!intel_crtc_needs_modeset(new_crtc_state) && - !intel_crtc_needs_fastset(new_crtc_state)) - return; - - encoder = intel_get_crtc_new_encoder(state, new_crtc_state); - intel_lt_phy_pll_readout_hw_state(encoder, &pll_hw_state); - - dig_port = enc_to_dig_port(encoder); - if (intel_tc_port_in_tbt_alt_mode(dig_port)) - return; - - INTEL_DISPLAY_STATE_WARN(display, pll_hw_state.config[0] != pll_sw_state->config[0], - "[CRTC:%d:%s] mismatch in LT PHY PLL CONFIG 0: (expected 0x%04x, found 0x%04x)", - crtc->base.base.id, crtc->base.name, - pll_sw_state->config[0], pll_hw_state.config[0]); - INTEL_DISPLAY_STATE_WARN(display, pll_hw_state.config[2] != pll_sw_state->config[2], - "[CRTC:%d:%s] mismatch in LT PHY PLL CONFIG 2: (expected 0x%04x, found 0x%04x)", - crtc->base.base.id, crtc->base.name, - pll_sw_state->config[2], pll_hw_state.config[2]); -} - void intel_xe3plpd_pll_enable(struct intel_encoder *encoder, struct intel_dpll *pll, const struct intel_dpll_hw_state *dpll_hw_state) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index 147ae431713d..16de39484779 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -41,8 +41,6 @@ bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, struct intel_dpll_hw_state *hw_state); bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, struct intel_lt_phy_pll_state *pll_state); -void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state, - struct intel_crtc *crtc); int intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state, u32 frequency_khz); diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c index 12a00121c274..2ec17c2bfe0f 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c @@ -246,7 +246,6 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state, verify_crtc_state(state, crtc); intel_dpll_state_verify(state, crtc); intel_mpllb_state_verify(state, crtc); - intel_lt_phy_pll_state_verify(state, crtc); } void intel_modeset_verify_disabled(struct intel_atomic_state *state) From 29b37427cdc6a9cc37b060e9e9eea90965936bb5 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 10:14:15 +0000 Subject: [PATCH 51/68] drm/i915/lt_phy: Enable dpll framework for xe3plpd xe3plpd platform is supported by dpll framework remove a separate check for hw comparison and rely solely on dpll framework hw comparison. Finally, all required hooks are now in place so initialize PLL manager for xe3plpd platform and remove the redirections to the legacy code paths for clock enable/disable as well as state mismatch checks that are no longer needed. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312101415.2669387-1-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- drivers/gpu/drm/i915/display/intel_display.c | 31 ------------------- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 7 +++-- drivers/gpu/drm/i915/display/intel_lt_phy.c | 1 + 4 files changed, 6 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index fe024250d350..ebefa889bc8c 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -5285,7 +5285,7 @@ void intel_ddi_init(struct intel_display *display, if (HAS_LT_PHY(display)) { encoder->enable_clock = intel_mtl_pll_enable_clock; encoder->disable_clock = intel_mtl_pll_disable_clock; - encoder->port_pll_type = intel_mtl_port_pll_type; + encoder->port_pll_type = icl_ddi_tc_port_pll_type; if (intel_encoder_is_tc(encoder)) encoder->get_config = mtl_ddi_tc_phy_get_config; else diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index dfc28af1ef88..10b6c6fcb03f 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5063,23 +5063,6 @@ static bool allow_vblank_delay_fastset(const struct intel_crtc_state *old_crtc_s !intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DSI); } -static void -pipe_config_lt_phy_pll_mismatch(struct drm_printer *p, bool fastset, - const struct intel_crtc *crtc, - const char *name, - const struct intel_lt_phy_pll_state *a, - const struct intel_lt_phy_pll_state *b) -{ - char *chipname = "LTPHY"; - - pipe_config_mismatch(p, fastset, crtc, name, chipname); - - drm_printf(p, "expected:\n"); - intel_lt_phy_dump_hw_state(p, a); - drm_printf(p, "found:\n"); - intel_lt_phy_dump_hw_state(p, b); -} - bool intel_pipe_config_compare(const struct intel_crtc_state *current_config, const struct intel_crtc_state *pipe_config, @@ -5194,16 +5177,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, } \ } while (0) -#define PIPE_CONF_CHECK_PLL_LT(name) do { \ - if (!intel_lt_phy_pll_compare_hw_state(¤t_config->name, \ - &pipe_config->name)) { \ - pipe_config_lt_phy_pll_mismatch(&p, fastset, crtc, __stringify(name), \ - ¤t_config->name, \ - &pipe_config->name); \ - ret = false; \ - } \ -} while (0) - #define PIPE_CONF_CHECK_TIMINGS(name) do { \ PIPE_CONF_CHECK_I(name.crtc_hdisplay); \ PIPE_CONF_CHECK_I(name.crtc_htotal); \ @@ -5430,10 +5403,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, if (display->dpll.mgr || HAS_GMCH(display)) PIPE_CONF_CHECK_PLL(dpll_hw_state); - /* FIXME convert MTL+ platforms over to dpll_mgr */ - if (HAS_LT_PHY(display)) - PIPE_CONF_CHECK_PLL_LT(dpll_hw_state.ltpll); - PIPE_CONF_CHECK_X(dsi_pll.ctrl); PIPE_CONF_CHECK_X(dsi_pll.div); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 421767dd8367..f5d4f7146fbc 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4724,7 +4724,6 @@ static bool xe3plpd_compare_hw_state(const struct intel_dpll_hw_state *_a, return intel_lt_phy_pll_compare_hw_state(a, b); } -__maybe_unused static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .dpll_info = xe3plpd_plls, .compute_dplls = xe3plpd_compute_dplls, @@ -4750,9 +4749,11 @@ void intel_dpll_init(struct intel_display *display) mutex_init(&display->dpll.lock); - if (DISPLAY_VER(display) >= 35 || display->platform.dg2) - /* No shared DPLLs on NVL or DG2; port PLLs are part of the PHY */ + if (display->platform.dg2) + /* No shared DPLLs on DG2; port PLLs are part of the PHY */ dpll_mgr = NULL; + else if (DISPLAY_VER(display) >= 35) + dpll_mgr = &xe3plpd_pll_mgr; else if (DISPLAY_VER(display) >= 14) dpll_mgr = &mtl_pll_mgr; else if (display->platform.alderlake_p) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 5bbbc6182861..657ad5cb0eff 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -11,6 +11,7 @@ #include "intel_ddi_buf_trans.h" #include "intel_de.h" #include "intel_display.h" +#include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dpll.h" From 968e9fd188488d1a4b6981f39554d4117468b6ee Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 12 Mar 2026 08:06:57 +0000 Subject: [PATCH 52/68] drm/i915/lt_phy: Replace crtc compute clock The existing DPLL compute clock callback for the XE3PLPD platform (`xe3plpd_crtc_compute_clock`) was specific to that platform. Replace it with the more generic Haswell (`hsw_crtc_compute_clock`) implementation so that the compute clock path does not rely on the XE3PLPD hook. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260312080657.2648265-25-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll.c | 25 +---------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index e13a5e12109d..c7d37e74fbe9 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1212,29 +1212,6 @@ static int dg2_crtc_compute_clock(struct intel_atomic_state *state, return 0; } -static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct intel_crtc_state *crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_encoder *encoder = - intel_get_crtc_new_encoder(state, crtc_state); - struct intel_display *display = to_intel_display(encoder); - int ret; - - ret = intel_lt_phy_pll_calc_state(crtc_state, encoder, &crtc_state->dpll_hw_state); - if (ret) - return ret; - - /* TODO: Do the readback via intel_compute_shared_dplls() */ - crtc_state->port_clock = - intel_lt_phy_calc_port_clock(display, &crtc_state->dpll_hw_state.ltpll); - - crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state); - - return 0; -} - static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); @@ -1695,7 +1672,7 @@ static int i8xx_crtc_compute_clock(struct intel_atomic_state *state, } static const struct intel_dpll_global_funcs xe3plpd_dpll_funcs = { - .crtc_compute_clock = xe3plpd_crtc_compute_clock, + .crtc_compute_clock = hsw_crtc_compute_clock, .crtc_get_dpll = hsw_crtc_get_dpll, }; From 658b3c963de79ce802cd3b68bd0694ceeab4f105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Mar 2026 13:10:26 +0200 Subject: [PATCH 53/68] drm/i915/de: Introduce intel_de.c and move intel_de_{read,write}8() there MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_de_{read,write}8() aren't performance critical so having them as static inline is pointless. Introduce intel_de.c and move the implementation there. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260313111028.25159-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/display/intel_de.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_de.h | 22 +++------------------- drivers/gpu/drm/xe/Makefile | 1 + 4 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 drivers/gpu/drm/i915/display/intel_de.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index be976a90c5a6..b677720a1c2d 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -254,6 +254,7 @@ i915-y += \ display/intel_crtc_state_dump.o \ display/intel_cursor.o \ display/intel_dbuf_bw.o \ + display/intel_de.o \ display/intel_display.o \ display/intel_display_conversion.o \ display/intel_display_driver.o \ diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c new file mode 100644 index 000000000000..5348c1d51eb8 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include + +#include "intel_de.h" + +u8 intel_de_read8(struct intel_display *display, i915_reg_t reg) +{ + /* this is only used on VGA registers (possible on pre-g4x) */ + drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); + + return intel_uncore_read8(__to_uncore(display), reg); +} + +void intel_de_write8(struct intel_display *display, i915_reg_t reg, u8 val) +{ + drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); + + intel_uncore_write8(__to_uncore(display), reg, val); +} diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h index f30f3f8ebee1..8ca5904ba84e 100644 --- a/drivers/gpu/drm/i915/display/intel_de.h +++ b/drivers/gpu/drm/i915/display/intel_de.h @@ -6,8 +6,6 @@ #ifndef __INTEL_DE_H__ #define __INTEL_DE_H__ -#include - #include "intel_display_core.h" #include "intel_dmc_wl.h" #include "intel_dsb.h" @@ -19,6 +17,9 @@ static inline struct intel_uncore *__to_uncore(struct intel_display *display) return to_intel_uncore(display->drm); } +u8 intel_de_read8(struct intel_display *display, i915_reg_t reg); +void intel_de_write8(struct intel_display *display, i915_reg_t reg, u8 val); + static inline u32 intel_de_read(struct intel_display *display, i915_reg_t reg) { @@ -33,23 +34,6 @@ intel_de_read(struct intel_display *display, i915_reg_t reg) return val; } -static inline u8 -intel_de_read8(struct intel_display *display, i915_reg_t reg) -{ - /* this is only used on VGA registers (possible on pre-g4x) */ - drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); - - return intel_uncore_read8(__to_uncore(display), reg); -} - -static inline void -intel_de_write8(struct intel_display *display, i915_reg_t reg, u8 val) -{ - drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); - - intel_uncore_write8(__to_uncore(display), reg, val); -} - static inline u64 intel_de_read64_2x32(struct intel_display *display, i915_reg_t lower_reg, i915_reg_t upper_reg) diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 468599492af1..35d611adbb83 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -249,6 +249,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_dbuf_bw.o \ i915-display/intel_ddi.o \ i915-display/intel_ddi_buf_trans.o \ + i915-display/intel_de.o \ i915-display/intel_display.o \ i915-display/intel_display_conversion.o \ i915-display/intel_display_device.o \ From 56d2a47e6b495e7d382d00b91ce182ff2c6a3741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Mar 2026 13:10:27 +0200 Subject: [PATCH 54/68] drm/i915/de: Move intel_de_wait*() into intel_de.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_de_wait*() end up doing quite a bit of stuff, so the one function call overhead from them seems insignificant. Move the implementation intel_de.c. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260313111028.25159-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_de.c | 72 ++++++++++++++++++ drivers/gpu/drm/i915/display/intel_de.h | 99 +++++-------------------- 2 files changed, 92 insertions(+), 79 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c index 5348c1d51eb8..fce92535bd6a 100644 --- a/drivers/gpu/drm/i915/display/intel_de.c +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -7,6 +7,78 @@ #include "intel_de.h" +int intel_de_wait_us(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value) +{ + int ret; + + intel_dmc_wl_get(display, reg); + + ret = __intel_wait_for_register(__to_uncore(display), reg, mask, + value, timeout_us, 0, out_value); + + intel_dmc_wl_put(display, reg); + + return ret; +} + +int intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value) +{ + int ret; + + intel_dmc_wl_get(display, reg); + + ret = __intel_wait_for_register(__to_uncore(display), reg, mask, + value, 2, timeout_ms, out_value); + + intel_dmc_wl_put(display, reg); + + return ret; +} + +int intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value) +{ + return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, + value, 2, timeout_ms, out_value); +} + +int intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value) +{ + return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, + value, timeout_us, 0, out_value); +} + +int intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us) +{ + return intel_de_wait_us(display, reg, mask, mask, timeout_us, NULL); +} + +int intel_de_wait_for_clear_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us) +{ + return intel_de_wait_us(display, reg, mask, 0, timeout_us, NULL); +} + +int intel_de_wait_for_set_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms) +{ + return intel_de_wait_ms(display, reg, mask, mask, timeout_ms, NULL); +} + +int intel_de_wait_for_clear_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms) +{ + return intel_de_wait_ms(display, reg, mask, 0, timeout_ms, NULL); +} + u8 intel_de_read8(struct intel_display *display, i915_reg_t reg) { /* this is only used on VGA registers (possible on pre-g4x) */ diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h index 8ca5904ba84e..f87b84ab9d6d 100644 --- a/drivers/gpu/drm/i915/display/intel_de.h +++ b/drivers/gpu/drm/i915/display/intel_de.h @@ -86,85 +86,26 @@ intel_de_rmw(struct intel_display *display, i915_reg_t reg, u32 clear, u32 set) return val; } -static inline int -intel_de_wait_us(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_us, - u32 *out_value) -{ - int ret; - - intel_dmc_wl_get(display, reg); - - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); - - intel_dmc_wl_put(display, reg); - - return ret; -} - -static inline int -intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_ms, - u32 *out_value) -{ - int ret; - - intel_dmc_wl_get(display, reg); - - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); - - intel_dmc_wl_put(display, reg); - - return ret; -} - -static inline int -intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_ms, - u32 *out_value) -{ - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); -} - -static inline int -intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, - u32 mask, u32 value, unsigned int timeout_us, - u32 *out_value) -{ - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); -} - -static inline int -intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_us) -{ - return intel_de_wait_us(display, reg, mask, mask, timeout_us, NULL); -} - -static inline int -intel_de_wait_for_clear_us(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_us) -{ - return intel_de_wait_us(display, reg, mask, 0, timeout_us, NULL); -} - -static inline int -intel_de_wait_for_set_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_ms) -{ - return intel_de_wait_ms(display, reg, mask, mask, timeout_ms, NULL); -} - -static inline int -intel_de_wait_for_clear_ms(struct intel_display *display, i915_reg_t reg, - u32 mask, unsigned int timeout_ms) -{ - return intel_de_wait_ms(display, reg, mask, 0, timeout_ms, NULL); -} +int intel_de_wait_us(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value); +int intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value); +int intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_ms, + u32 *out_value); +int intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, + u32 mask, u32 value, unsigned int timeout_us, + u32 *out_value); +int intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us); +int intel_de_wait_for_clear_us(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_us); +int intel_de_wait_for_set_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms); +int intel_de_wait_for_clear_ms(struct intel_display *display, i915_reg_t reg, + u32 mask, unsigned int timeout_ms); /* * Unlocked mmio-accessors, think carefully before using these. From ff854b32b604526100c8468f8915150bd4387288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 23 Mar 2026 11:43:04 +0200 Subject: [PATCH 55/68] drm/i915/de: Implement register polling in the display code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan is to move all the mmio stuff into the display code itself. As a first step implement the register polling in intel_de.c. Currently i915 and xe implement this stuff in slightly different ways, so there are some functional changes here. Try to go for a reasonable middle ground between the i915 and xe implementations: - the exponential backoff limit is the simpler approach taken by i915 (== just clamp the max sleep duration to 1 ms) - the fast vs. slow timeout handling is similar to i915 where we first try the fast timeout and then again the slow timeout if the condition still isn't satisfied. xe just adds up the timeouts together, which is a bit weird. - the atomic wait variant uses udelay() like xe, whereas i915 has no udelay()s in its atomic loop. As a compromise go for a fixed 1 usec delay for short waits, instead of the somewhat peculiar xe behaviour where it effectively just does one iteration of the loop. - keep the "use udelay() for < 10 usec waits" logic (which more or less mirrors fsleep()), but include an explicit might_sleep() even for these short waits when called from a non-atomic intel_de_wait*() function. This should prevent people from calling the non-atomic functions from the wrong place. Eventually we may want to switch over to poll_timeout*(), but that lacks the exponential backoff, so a bit too radical to change in one go. v2: Initialize ret in intel_de_wait_for_register() to avoid a warning from the compiler. This is actually a false positive since we always have fast_timeout_us!=0 when slow_timeout_us!=0, but the compiler can't see that Reviewed-by: Jani Nikula Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260323094304.8171-1-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/display/intel_de.c | 99 +++++++++++++++++-- .../drm/xe/compat-i915-headers/intel_uncore.h | 31 ------ 2 files changed, 91 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c index fce92535bd6a..d2a418da2d54 100644 --- a/drivers/gpu/drm/i915/display/intel_de.c +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -3,10 +3,85 @@ * Copyright © 2026 Intel Corporation */ +#include + #include #include "intel_de.h" +static int __intel_de_wait_for_register(struct intel_display *display, + i915_reg_t reg, u32 mask, u32 value, + unsigned int timeout_us, + u32 (*read)(struct intel_display *display, i915_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; + int wait = 10; + u32 reg_value; + int ret; + + might_sleep_if(!is_atomic); + + if (timeout_us <= 10) { + is_atomic = true; + wait = 1; + } + + for (;;) { + bool expired = ktime_after(ktime_get_raw(), end); + + /* guarantee the condition is evaluated after timeout expired */ + barrier(); + + reg_value = read(display, reg); + if ((reg_value & mask) == value) { + ret = 0; + break; + } + + if (expired) { + ret = -ETIMEDOUT; + break; + } + + if (is_atomic) + udelay(wait); + else + usleep_range(wait, wait << 1); + + if (wait < wait_max) + wait <<= 1; + } + + if (out_val) + *out_val = reg_value; + + return ret; +} + +static int intel_de_wait_for_register(struct intel_display *display, + i915_reg_t reg, u32 mask, u32 value, + unsigned int fast_timeout_us, + unsigned int slow_timeout_us, + u32 (*read)(struct intel_display *display, i915_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, i915_reg_t reg, u32 mask, u32 value, unsigned int timeout_us, u32 *out_value) @@ -15,8 +90,10 @@ int intel_de_wait_us(struct intel_display *display, i915_reg_t reg, intel_dmc_wl_get(display, reg); - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); + ret = intel_de_wait_for_register(display, reg, mask, value, + timeout_us, 0, + intel_de_read, + out_value, false); intel_dmc_wl_put(display, reg); @@ -31,8 +108,10 @@ int intel_de_wait_ms(struct intel_display *display, i915_reg_t reg, intel_dmc_wl_get(display, reg); - ret = __intel_wait_for_register(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); + ret = intel_de_wait_for_register(display, reg, mask, value, + 2, timeout_ms * 1000, + intel_de_read, + out_value, false); intel_dmc_wl_put(display, reg); @@ -43,16 +122,20 @@ int intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg, u32 mask, u32 value, unsigned int timeout_ms, u32 *out_value) { - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, 2, timeout_ms, out_value); + return intel_de_wait_for_register(display, reg, mask, value, + 2, timeout_ms * 1000, + intel_de_read_fw, + out_value, false); } int intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg, u32 mask, u32 value, unsigned int timeout_us, u32 *out_value) { - return __intel_wait_for_register_fw(__to_uncore(display), reg, mask, - value, timeout_us, 0, out_value); + return intel_de_wait_for_register(display, reg, mask, value, + timeout_us, 0, + intel_de_read_fw, + out_value, true); } int intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg, diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h index a8cfd65119e0..08d7ab933672 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h @@ -98,37 +98,6 @@ static inline u32 intel_uncore_rmw(struct intel_uncore *uncore, return xe_mmio_rmw32(__compat_uncore_to_mmio(uncore), reg, clear, set); } -static inline int -__intel_wait_for_register(struct intel_uncore *uncore, i915_reg_t i915_reg, - u32 mask, u32 value, unsigned int fast_timeout_us, - unsigned int slow_timeout_ms, u32 *out_value) -{ - struct xe_reg reg = XE_REG(i915_mmio_reg_offset(i915_reg)); - bool atomic; - - /* - * Replicate the behavior from i915 here, in which sleep is not - * performed if slow_timeout_ms == 0. This is necessary because - * of some paths in display code where waits are done in atomic - * context. - */ - atomic = !slow_timeout_ms && fast_timeout_us > 0; - - return xe_mmio_wait32(__compat_uncore_to_mmio(uncore), reg, mask, value, - fast_timeout_us + 1000 * slow_timeout_ms, - out_value, atomic); -} - -static inline int -__intel_wait_for_register_fw(struct intel_uncore *uncore, i915_reg_t i915_reg, - u32 mask, u32 value, unsigned int fast_timeout_us, - unsigned int slow_timeout_ms, u32 *out_value) -{ - return __intel_wait_for_register(uncore, i915_reg, mask, value, - fast_timeout_us, slow_timeout_ms, - out_value); -} - static inline u32 intel_uncore_read_fw(struct intel_uncore *uncore, i915_reg_t i915_reg) { From c80c68e777587330e5d89b4c294a32fe7365879c Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:25 +0200 Subject: [PATCH 56/68] drm/i915/display: move clock-gating init for IBX to display Add a new function in the display code to help initialize clock-gating without reading display PCH registers directly from non-display code. This adds a mini-framework to deal with display-specific PCH registers and uses it for IBX as a start. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-2-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 24 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_pch.h | 1 + drivers/gpu/drm/i915/intel_clock_gating.c | 13 ++---------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index 65359a36df48..65812b720bda 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -5,6 +5,8 @@ #include +#include "intel_de.h" +#include "intel_display_regs.h" #include "intel_display_core.h" #include "intel_display_utils.h" #include "intel_pch.h" @@ -214,6 +216,28 @@ intel_pch_type(const struct intel_display *display, unsigned short id) } } +static void intel_pch_ibx_init_clock_gating(struct intel_display *display) +{ + /* + * On Ibex Peak and Cougar Point, we need to disable clock + * gating for the panel power sequencer or it will fail to + * start up when no ports are active. + */ + intel_de_write(display, SOUTH_DSPCLK_GATE_D, + PCH_DPLSUNIT_CLOCK_GATE_DISABLE); +} + +void intel_pch_init_clock_gating(struct intel_display *display) +{ + switch (INTEL_PCH_TYPE(display)) { + case PCH_IBX: + intel_pch_ibx_init_clock_gating(display); + break; + default: + break; + } +} + static bool intel_is_virt_pch(unsigned short id, unsigned short svendor, unsigned short sdevice) { diff --git a/drivers/gpu/drm/i915/display/intel_pch.h b/drivers/gpu/drm/i915/display/intel_pch.h index 19cac7412d0a..aa971fa141e7 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.h +++ b/drivers/gpu/drm/i915/display/intel_pch.h @@ -52,5 +52,6 @@ enum intel_pch { #define HAS_PCH_SPLIT(display) (INTEL_PCH_TYPE(display) != PCH_NONE) void intel_pch_detect(struct intel_display *display); +void intel_pch_init_clock_gating(struct intel_display *display); #endif /* __INTEL_PCH__ */ diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 68a6f94f2a37..c0382607224d 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -33,6 +33,7 @@ #include "display/intel_display.h" #include "display/intel_display_core.h" #include "display/intel_display_regs.h" +#include "display/intel_pch.h" #include "gt/intel_engine_regs.h" #include "gt/intel_gt.h" #include "gt/intel_gt_mcr.h" @@ -124,16 +125,6 @@ static void glk_init_clock_gating(struct drm_i915_private *i915) PWM1_GATING_DIS | PWM2_GATING_DIS); } -static void ibx_init_clock_gating(struct drm_i915_private *i915) -{ - /* - * On Ibex Peak and Cougar Point, we need to disable clock - * gating for the panel power sequencer or it will fail to - * start up when no ports are active. - */ - intel_uncore_write(&i915->uncore, SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); -} - static void g4x_disable_trickle_feed(struct drm_i915_private *dev_priv) { struct intel_display *display = dev_priv->display; @@ -202,7 +193,7 @@ static void ilk_init_clock_gating(struct drm_i915_private *i915) g4x_disable_trickle_feed(i915); - ibx_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); } static void cpt_init_clock_gating(struct drm_i915_private *i915) From d129bce3fc820d8f2a4b966a38533b2f0f689ba5 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:26 +0200 Subject: [PATCH 57/68] drm/i915: move CPT clock gating init into intel_pch Move the CPT PCH clock gating programming into intel_pch_init_clock_gating() and switch the corresponding IVB callers to the display-specific code. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-3-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 40 +++++++++++++++++++++++ drivers/gpu/drm/i915/intel_clock_gating.c | 39 ++-------------------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index 65812b720bda..bcd66bdf2a22 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -6,6 +6,7 @@ #include #include "intel_de.h" +#include "intel_display.h" #include "intel_display_regs.h" #include "intel_display_core.h" #include "intel_display_utils.h" @@ -227,12 +228,51 @@ static void intel_pch_ibx_init_clock_gating(struct intel_display *display) PCH_DPLSUNIT_CLOCK_GATE_DISABLE); } +static void intel_pch_cpt_init_clock_gating(struct intel_display *display) +{ + enum pipe pipe; + u32 val; + + /* + * On Ibex Peak and Cougar Point, we need to disable clock + * gating for the panel power sequencer or it will fail to + * start up when no ports are active. + */ + intel_de_write(display, SOUTH_DSPCLK_GATE_D, + PCH_DPLSUNIT_CLOCK_GATE_DISABLE | + PCH_DPLUNIT_CLOCK_GATE_DISABLE | + PCH_CPUNIT_CLOCK_GATE_DISABLE); + intel_de_rmw(display, SOUTH_CHICKEN2, 0, DPLS_EDP_PPS_FIX_DIS); + + /* The below fixes the weird display corruption, a few pixels shifted + * downward, on (only) LVDS of some HP laptops with IVY. + */ + for_each_pipe(display, pipe) { + val = intel_de_read(display, TRANS_CHICKEN2(pipe)); + val |= TRANS_CHICKEN2_TIMING_OVERRIDE; + val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; + if (display->vbt.fdi_rx_polarity_inverted) + val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; + val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; + val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_MODESWITCH; + intel_de_write(display, TRANS_CHICKEN2(pipe), val); + } + + /* WADP0ClockGatingDisable */ + for_each_pipe(display, pipe) + intel_de_write(display, TRANS_CHICKEN1(pipe), + TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); +} + void intel_pch_init_clock_gating(struct intel_display *display) { switch (INTEL_PCH_TYPE(display)) { case PCH_IBX: intel_pch_ibx_init_clock_gating(display); break; + case PCH_CPT: + intel_pch_cpt_init_clock_gating(display); + break; default: break; } diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index c0382607224d..0218196d183a 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -196,41 +196,6 @@ static void ilk_init_clock_gating(struct drm_i915_private *i915) intel_pch_init_clock_gating(i915->display); } -static void cpt_init_clock_gating(struct drm_i915_private *i915) -{ - struct intel_display *display = i915->display; - enum pipe pipe; - u32 val; - - /* - * On Ibex Peak and Cougar Point, we need to disable clock - * gating for the panel power sequencer or it will fail to - * start up when no ports are active. - */ - intel_uncore_write(&i915->uncore, SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE | - PCH_DPLUNIT_CLOCK_GATE_DISABLE | - PCH_CPUNIT_CLOCK_GATE_DISABLE); - intel_uncore_rmw(&i915->uncore, SOUTH_CHICKEN2, 0, DPLS_EDP_PPS_FIX_DIS); - /* The below fixes the weird display corruption, a few pixels shifted - * downward, on (only) LVDS of some HP laptops with IVY. - */ - for_each_pipe(display, pipe) { - val = intel_uncore_read(&i915->uncore, TRANS_CHICKEN2(pipe)); - val |= TRANS_CHICKEN2_TIMING_OVERRIDE; - val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; - if (display->vbt.fdi_rx_polarity_inverted) - val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; - val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; - val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_MODESWITCH; - intel_uncore_write(&i915->uncore, TRANS_CHICKEN2(pipe), val); - } - /* WADP0ClockGatingDisable */ - for_each_pipe(display, pipe) { - intel_uncore_write(&i915->uncore, TRANS_CHICKEN1(pipe), - TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); - } -} - static void gen6_check_mch_setup(struct drm_i915_private *i915) { u32 tmp; @@ -296,7 +261,7 @@ static void gen6_init_clock_gating(struct drm_i915_private *i915) g4x_disable_trickle_feed(i915); - cpt_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); gen6_check_mch_setup(i915); } @@ -536,7 +501,7 @@ static void ivb_init_clock_gating(struct drm_i915_private *i915) GEN6_MBC_SNPCR_MED); if (!HAS_PCH_NOP(display)) - cpt_init_clock_gating(i915); + intel_pch_init_clock_gating(display); gen6_check_mch_setup(i915); } From cf37495ad17db876c28a824c003133c2e103cd59 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:27 +0200 Subject: [PATCH 58/68] drm/i915: move LPT clock gating init into intel_pch Move the LPT PCH clock gating programming into intel_pch_init_clock_gating() and switch the corresponding Haswell/Broadwell callers to the display-specific code. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-4-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 19 +++++++++++++++++++ drivers/gpu/drm/i915/intel_clock_gating.c | 21 ++------------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index bcd66bdf2a22..b7fade66b1da 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -264,6 +264,21 @@ static void intel_pch_cpt_init_clock_gating(struct intel_display *display) TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); } +static void intel_pch_lpt_init_clock_gating(struct intel_display *display) +{ + /* + * TODO: this bit should only be enabled when really needed, then + * disabled when not needed anymore in order to save power. + */ + if (HAS_PCH_LPT_LP(display)) + intel_de_rmw(display, SOUTH_DSPCLK_GATE_D, 0, + PCH_LP_PARTITION_LEVEL_DISABLE); + + /* WADPOClockGatingDisable:hsw */ + intel_de_rmw(display, TRANS_CHICKEN1(PIPE_A), 0, + TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); +} + void intel_pch_init_clock_gating(struct intel_display *display) { switch (INTEL_PCH_TYPE(display)) { @@ -273,6 +288,10 @@ void intel_pch_init_clock_gating(struct intel_display *display) case PCH_CPT: intel_pch_cpt_init_clock_gating(display); break; + case PCH_LPT_H: + case PCH_LPT_LP: + intel_pch_lpt_init_clock_gating(display); + break; default: break; } diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 0218196d183a..4c19028a9e20 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -266,23 +266,6 @@ static void gen6_init_clock_gating(struct drm_i915_private *i915) gen6_check_mch_setup(i915); } -static void lpt_init_clock_gating(struct drm_i915_private *i915) -{ - struct intel_display *display = i915->display; - - /* - * TODO: this bit should only be enabled when really needed, then - * disabled when not needed anymore in order to save power. - */ - if (HAS_PCH_LPT_LP(display)) - intel_uncore_rmw(&i915->uncore, SOUTH_DSPCLK_GATE_D, - 0, PCH_LP_PARTITION_LEVEL_DISABLE); - - /* WADPOClockGatingDisable:hsw */ - intel_uncore_rmw(&i915->uncore, TRANS_CHICKEN1(PIPE_A), - 0, TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); -} - static void gen8_set_l3sqc_credits(struct drm_i915_private *i915, int general_prio_credits, int high_prio_credits) @@ -422,7 +405,7 @@ static void bdw_init_clock_gating(struct drm_i915_private *i915) intel_uncore_rmw(&i915->uncore, CHICKEN_PAR2_1, 0, KVM_CONFIG_CHANGE_NOTIFICATION_SELECT); - lpt_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); /* WaDisableDopClockGating:bdw * @@ -456,7 +439,7 @@ static void hsw_init_clock_gating(struct drm_i915_private *i915) /* WaSwitchSolVfFArbitrationPriority:hsw */ intel_uncore_rmw(&i915->uncore, GAM_ECOCHK, 0, HSW_ECOCHK_ARB_PRIO_SOL); - lpt_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); } static void ivb_init_clock_gating(struct drm_i915_private *i915) From f86b08bf1766e9883bc75a66aeca7d737bf4c093 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Mar 2026 10:04:28 +0200 Subject: [PATCH 59/68] drm/i915: move CNP clock gating init into intel_pch Move the CNP PCH clock gating programming into intel_pch_init_clock_gating() and switch the corresponding CFL/CML caller to the display-specific code. Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260324080441.154609-5-luciano.coelho@intel.com Signed-off-by: Luca Coelho --- drivers/gpu/drm/i915/display/intel_pch.c | 10 ++++++++++ drivers/gpu/drm/i915/intel_clock_gating.c | 13 +------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c index b7fade66b1da..d2c1b1751838 100644 --- a/drivers/gpu/drm/i915/display/intel_pch.c +++ b/drivers/gpu/drm/i915/display/intel_pch.c @@ -279,6 +279,13 @@ static void intel_pch_lpt_init_clock_gating(struct intel_display *display) TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); } +static void intel_pch_cnp_init_clock_gating(struct intel_display *display) +{ + /* Display WA #1181 WaSouthDisplayDisablePWMCGEGating: cnp */ + intel_de_rmw(display, SOUTH_DSPCLK_GATE_D, 0, + CNP_PWM_CGE_GATING_DISABLE); +} + void intel_pch_init_clock_gating(struct intel_display *display) { switch (INTEL_PCH_TYPE(display)) { @@ -292,6 +299,9 @@ void intel_pch_init_clock_gating(struct intel_display *display) case PCH_LPT_LP: intel_pch_lpt_init_clock_gating(display); break; + case PCH_CNP: + intel_pch_cnp_init_clock_gating(display); + break; default: break; } diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 4c19028a9e20..ee2489a2fbe7 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -299,20 +299,9 @@ static void dg2_init_clock_gating(struct drm_i915_private *i915) SGSI_SIDECLK_DIS); } -static void cnp_init_clock_gating(struct drm_i915_private *i915) -{ - struct intel_display *display = i915->display; - - if (!HAS_PCH_CNP(display)) - return; - - /* Display WA #1181 WaSouthDisplayDisablePWMCGEGating: cnp */ - intel_uncore_rmw(&i915->uncore, SOUTH_DSPCLK_GATE_D, 0, CNP_PWM_CGE_GATING_DISABLE); -} - static void cfl_init_clock_gating(struct drm_i915_private *i915) { - cnp_init_clock_gating(i915); + intel_pch_init_clock_gating(i915->display); gen9_init_clock_gating(i915); /* WAC6entrylatency:cfl */ From 1de647abdfda9dc307503d0a85152161850ba52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Fri, 20 Mar 2026 10:04:03 +0200 Subject: [PATCH 60/68] drm/i915/psr: Fixes for Dell XPS DA14260 quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dell seems to be changing device ID even within same device model. Due to this we need to ignore device ID when applying quirk for Dell XPS 14 DA14260. Do this by adding DEVICE_ID_ANY and assign it to Dell XPS 14 DA14260 quirk. Also apply the quirk only for eDP Panel Replay. Fixes: 45c77d4bf8d4 ("drm/i915/psr: Disable Panel Replay on Dell XPS 14 DA14260 as a quirk") Cc: Mika Kahola Signed-off-by: Jouni Högander Reviewed-by: Mika Kahola Link: https://patch.msgid.link/20260320080403.1396926-1-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 3 ++- drivers/gpu/drm/i915/display/intel_quirks.c | 16 ++++++++++------ drivers/gpu/drm/i915/display/intel_quirks.h | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index b319e5bd6274..2f1b48cd8efd 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -610,7 +610,8 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp, struct intel_conn if (intel_dp->mst_detect == DRM_DP_MST) return; - if (intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY)) { + if (intel_dp_is_edp(intel_dp) && + intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY)) { drm_dbg_kms(display->drm, "Panel Replay support not currently available for this setup\n"); return; diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c index 8f1bf8f418ec..883f297d4b83 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.c +++ b/drivers/gpu/drm/i915/display/intel_quirks.c @@ -86,11 +86,11 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display *display) drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n"); } -static void quirk_disable_panel_replay(struct intel_dp *intel_dp) +static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY); + intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY); drm_info(display->drm, "Applying disable Panel Replay quirk\n"); } @@ -116,6 +116,8 @@ struct intel_dpcd_quirk { #define SINK_DEVICE_ID_ANY SINK_DEVICE_ID(0, 0, 0, 0, 0, 0) +#define DEVICE_ID_ANY 0 + /* For systems that don't have a meaningful PCI subdevice/subvendor ID */ struct intel_dmi_quirk { void (*hook)(struct intel_display *display); @@ -261,11 +263,11 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { }, /* Dell XPS 14 DA14260 */ { - .device = 0xb080, + .device = DEVICE_ID_ANY, .subsystem_vendor = 0x1028, .subsystem_device = 0x0db9, .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), - .hook = quirk_disable_panel_replay, + .hook = quirk_disable_edp_panel_replay, }, }; @@ -277,7 +279,8 @@ void intel_init_quirks(struct intel_display *display) for (i = 0; i < ARRAY_SIZE(intel_quirks); i++) { struct intel_quirk *q = &intel_quirks[i]; - if (d->device == q->device && + if ((d->device == q->device || + q->device == DEVICE_ID_ANY) && (d->subsystem_vendor == q->subsystem_vendor || q->subsystem_vendor == PCI_ANY_ID) && (d->subsystem_device == q->subsystem_device || @@ -300,7 +303,8 @@ void intel_init_dpcd_quirks(struct intel_dp *intel_dp, for (i = 0; i < ARRAY_SIZE(intel_dpcd_quirks); i++) { const struct intel_dpcd_quirk *q = &intel_dpcd_quirks[i]; - if (d->device == q->device && + if ((d->device == q->device || + q->device == DEVICE_ID_ANY) && (d->subsystem_vendor == q->subsystem_vendor || q->subsystem_vendor == PCI_ANY_ID) && (d->subsystem_device == q->subsystem_device || diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h index 77e490caed0d..83214eb94b0c 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.h +++ b/drivers/gpu/drm/i915/display/intel_quirks.h @@ -21,7 +21,7 @@ enum intel_quirk_id { QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK, QUIRK_FW_SYNC_LEN, QUIRK_EDP_LIMIT_RATE_HBR2, - QUIRK_DISABLE_PANEL_REPLAY, + QUIRK_DISABLE_EDP_PANEL_REPLAY, }; void intel_init_quirks(struct intel_display *display); From 059b7b5b79009af10eeeb24fb942de6fb6bea677 Mon Sep 17 00:00:00 2001 From: Christoph Manszewski Date: Wed, 25 Mar 2026 22:23:42 +0100 Subject: [PATCH 61/68] mailmap: update email address for Christoph Manszewski Since I am moving from intel, map the intel mail to my personal Gmail address. Signed-off-by: Christoph Manszewski Signed-off-by: Joonas Lahtinen Link: https://patch.msgid.link/20260325212342.4388-1-c.manszewski@gmail.com --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index e1cf6bb85d33..9dbd3a2b4961 100644 --- a/.mailmap +++ b/.mailmap @@ -196,6 +196,7 @@ Christophe Leroy Christophe Ricard Christopher Obbard Christoph Hellwig +Christoph Manszewski Chuck Lever Chuck Lever Chuck Lever From 0b475e91ecc2313207196c6d7fd5c53e1a878525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:10 +0200 Subject: [PATCH 62/68] drm/i915/dsi: Don't do DSC horizontal timing adjustments in command mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop adjusting the horizontal timing values based on the compression ratio in command mode. Bspec seems to be telling us to do this only in video mode, and this is also how the Windows driver does things. This should also fix a div-by-zero on some machines because the adjusted htotal ends up being so small that we end up with line_time_us==0 when trying to determine the vtotal value in command mode. Note that this doesn't actually make the display on the Huawei Matebook E work, but at least the kernel no longer explodes when the driver loads. Cc: stable@vger.kernel.org Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12045 Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-2-ville.syrjala@linux.intel.com Fixes: 53693f02d80e ("drm/i915/dsi: account for DSC in horizontal timings") Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index c04327979678..a763f2b13ff2 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -888,7 +888,7 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder, * non-compressed link speeds, and simplifies down to the ratio between * compressed and non-compressed bpp. */ - if (crtc_state->dsc.compression_enable) { + if (is_vid_mode(intel_dsi) && crtc_state->dsc.compression_enable) { mul = fxp_q4_to_int(crtc_state->dsc.compressed_bpp_x16); div = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); } @@ -1502,7 +1502,7 @@ static void gen11_dsi_get_timings(struct intel_encoder *encoder, struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - if (pipe_config->dsc.compressed_bpp_x16) { + if (is_vid_mode(intel_dsi) && pipe_config->dsc.compressed_bpp_x16) { int div = fxp_q4_to_int(pipe_config->dsc.compressed_bpp_x16); int mul = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); From ca7fc6a8ae28eaec8c194dc5f8db03f928b2e454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:11 +0200 Subject: [PATCH 63/68] drm/i915/dsi: s/eotp_pkt/eot_pkt/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eotp == "End of Transmission Packet". Drop the redundant extra 'p' from 'eotp_pkt', and make the thing a boolean while at it. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 2 +- drivers/gpu/drm/i915/display/intel_dsi.h | 3 +-- drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 4 ++-- drivers/gpu/drm/i915/display/vlv_dsi.c | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index a763f2b13ff2..6ea37929198c 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -711,7 +711,7 @@ gen11_dsi_configure_transcoder(struct intel_encoder *encoder, dsi_trans = dsi_port_to_transcoder(port); tmp = intel_de_read(display, DSI_TRANS_FUNC_CONF(dsi_trans)); - if (intel_dsi->eotp_pkt) + if (intel_dsi->eot_pkt) tmp &= ~EOTP_DISABLED; else tmp |= EOTP_DISABLED; diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index 489d26ffd235..8e39d2b52c54 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -80,8 +80,7 @@ struct intel_dsi { /* NON_BURST_SYNC_PULSE, NON_BURST_SYNC_EVENTS, or BURST_MODE */ int video_mode; - /* eot for MIPI_EOT_DISABLE register */ - u8 eotp_pkt; + bool eot_pkt; u8 clock_stop; u8 escape_clk_div; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 18755a8e613d..51f6a5b82cb2 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -718,7 +718,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) "burst" : ""); drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); - drm_printf(&p, "Eot %s\n", str_enabled_disabled(intel_dsi->eotp_pkt)); + drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); drm_printf(&p, "Clockstop %s\n", str_enabled_disabled(!intel_dsi->clock_stop)); drm_printf(&p, "Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); if (intel_dsi->dual_link == DSI_DUAL_LINK_FRONT_BACK) @@ -770,7 +770,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); - intel_dsi->eotp_pkt = mipi_config->eot_pkt_disabled ? 0 : 1; + intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; intel_dsi->clock_stop = mipi_config->enable_clk_stop ? 1 : 0; intel_dsi->lane_count = mipi_config->lane_cnt + 1; intel_dsi->pixel_format = diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index 36591d724638..d4db73c184e5 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -1367,7 +1367,7 @@ static void intel_dsi_prepare(struct intel_encoder *encoder, } tmp = 0; - if (intel_dsi->eotp_pkt == 0) + if (!intel_dsi->eot_pkt) tmp |= EOT_DISABLE; if (intel_dsi->clock_stop) tmp |= CLOCKSTOP; From 81ec9556ad69444899e8255652ec80972c09df14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:12 +0200 Subject: [PATCH 64/68] drm/i915/dsi: Make 'clock_stop' boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DSI 'clock_stop' parameter is a boolean, so use a real 'bool' for it. And pimp the debug print while at it. Note that we also remove the incorrect negation of the value in the debug print. That has been there since the code was introduced in commit 2ab8b458c6a1 ("drm/i915: Add support for Generic MIPI panel driver"). An earlier version of the patch https://lore.kernel.org/intel-gfx/1397454507-10273-5-git-send-email-shobhit.kumar@intel.com/ got it right, but looks like it got fumbled while dealing with other review comments. v2: Highlight the removal of the '!' (Jani) Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-4-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dsi.h | 2 +- drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index 8e39d2b52c54..0023ac341aa0 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -81,7 +81,7 @@ struct intel_dsi { int video_mode; bool eot_pkt; - u8 clock_stop; + bool clock_stop; u8 escape_clk_div; u8 dual_link; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 51f6a5b82cb2..23da7f5f9578 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -719,7 +719,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); - drm_printf(&p, "Clockstop %s\n", str_enabled_disabled(!intel_dsi->clock_stop)); + drm_printf(&p, "Clock stop during BLLP %s\n", str_enabled_disabled(intel_dsi->clock_stop)); drm_printf(&p, "Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); if (intel_dsi->dual_link == DSI_DUAL_LINK_FRONT_BACK) drm_printf(&p, "Dual link: DSI_DUAL_LINK_FRONT_BACK\n"); @@ -771,7 +771,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; - intel_dsi->clock_stop = mipi_config->enable_clk_stop ? 1 : 0; + intel_dsi->clock_stop = mipi_config->enable_clk_stop; intel_dsi->lane_count = mipi_config->lane_cnt + 1; intel_dsi->pixel_format = vbt_to_dsi_pixel_format(mipi_config->videomode_color_format); From 765a2635cd257545e732eee13f1e75774c0b79f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:13 +0200 Subject: [PATCH 65/68] drm/i915/dsi: Fill BLLPs with blanking packets if requested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TGL/ADL DSI can be configured to fill all BLLPs with blanking packets. Currently we enable that always, but the VBT actually tells us whether this is desired or not. Hook that up. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-5-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 9 +++++---- drivers/gpu/drm/i915/display/icl_dsi_regs.h | 2 +- drivers/gpu/drm/i915/display/intel_dsi.h | 1 + drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 2 ++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index 6ea37929198c..45ba02486c56 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -765,10 +765,11 @@ gen11_dsi_configure_transcoder(struct intel_encoder *encoder, } } - if (DISPLAY_VER(display) >= 12) { - if (is_vid_mode(intel_dsi)) - tmp |= BLANKING_PACKET_ENABLE; - } + if (DISPLAY_VER(display) >= 12 && + is_vid_mode(intel_dsi) && intel_dsi->blanking_pkt) + tmp |= BLANKING_PACKET_ENABLE; + else + tmp &= ~BLANKING_PACKET_ENABLE; /* program DSI operation mode */ if (is_vid_mode(intel_dsi)) { diff --git a/drivers/gpu/drm/i915/display/icl_dsi_regs.h b/drivers/gpu/drm/i915/display/icl_dsi_regs.h index b601b7632339..641e8f0b8cdb 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi_regs.h +++ b/drivers/gpu/drm/i915/display/icl_dsi_regs.h @@ -232,7 +232,7 @@ #define CALIBRATION_DISABLED (0x0 << 4) #define CALIBRATION_ENABLED_INITIAL_ONLY (0x2 << 4) #define CALIBRATION_ENABLED_INITIAL_PERIODIC (0x3 << 4) -#define BLANKING_PACKET_ENABLE (1 << 2) +#define BLANKING_PACKET_ENABLE (1 << 2) /* tgl+ */ #define S3D_ORIENTATION_LANDSCAPE (1 << 1) #define EOTP_DISABLED (1 << 0) diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index 0023ac341aa0..f55d48e43af1 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -80,6 +80,7 @@ struct intel_dsi { /* NON_BURST_SYNC_PULSE, NON_BURST_SYNC_EVENTS, or BURST_MODE */ int video_mode; + bool blanking_pkt; bool eot_pkt; bool clock_stop; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 23da7f5f9578..c544871dac0b 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -718,6 +718,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) "burst" : ""); drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); + drm_printf(&p, "Blanking packets during BLLP %s\n", str_enabled_disabled(intel_dsi->blanking_pkt)); drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); drm_printf(&p, "Clock stop during BLLP %s\n", str_enabled_disabled(intel_dsi->clock_stop)); drm_printf(&p, "Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); @@ -770,6 +771,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); + intel_dsi->blanking_pkt = mipi_config->blanking_packets_during_bllp; intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; intel_dsi->clock_stop = mipi_config->enable_clk_stop; intel_dsi->lane_count = mipi_config->lane_cnt + 1; From e8a7efa81d734e1c8f4d91e658a162ea32f39dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:14 +0200 Subject: [PATCH 66/68] drm/i915/dsi: Place clock into LP during LPM if requested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TGL/ADL DSI can be configured to place the clock lane into LP state during LPM, if otherwise configured for continuous HS clock. Hook that up. VBT tells us whether this should be done. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-6-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/icl_dsi.c | 6 ++++++ drivers/gpu/drm/i915/display/icl_dsi_regs.h | 1 + drivers/gpu/drm/i915/display/intel_dsi.h | 1 + drivers/gpu/drm/i915/display/intel_dsi_vbt.c | 2 ++ 4 files changed, 10 insertions(+) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index 45ba02486c56..afbaa0465842 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -729,6 +729,12 @@ gen11_dsi_configure_transcoder(struct intel_encoder *encoder, else tmp |= CLK_HS_CONTINUOUS; + if (DISPLAY_VER(display) >= 12 && + intel_dsi->lp_clock_during_lpm) + tmp |= LP_CLK_DURING_LPM; + else + tmp &= ~LP_CLK_DURING_LPM; + /* configure buffer threshold limit to minimum */ tmp &= ~PIX_BUF_THRESHOLD_MASK; tmp |= PIX_BUF_THRESHOLD_1_4; diff --git a/drivers/gpu/drm/i915/display/icl_dsi_regs.h b/drivers/gpu/drm/i915/display/icl_dsi_regs.h index 641e8f0b8cdb..55ab57adcb0f 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi_regs.h +++ b/drivers/gpu/drm/i915/display/icl_dsi_regs.h @@ -227,6 +227,7 @@ #define CLK_ENTER_LP_AFTER_DATA (0x0 << 8) #define CLK_HS_OR_LP (0x2 << 8) #define CLK_HS_CONTINUOUS (0x3 << 8) +#define LP_CLK_DURING_LPM (1 << 7) /* tgl+ */ #define LINK_CALIBRATION_MASK (0x3 << 4) #define LINK_CALIBRATION_SHIFT 4 #define CALIBRATION_DISABLED (0x0 << 4) diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index f55d48e43af1..9fcdabbf3740 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -80,6 +80,7 @@ struct intel_dsi { /* NON_BURST_SYNC_PULSE, NON_BURST_SYNC_EVENTS, or BURST_MODE */ int video_mode; + bool lp_clock_during_lpm; bool blanking_pkt; bool eot_pkt; bool clock_stop; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index c544871dac0b..fe12041e913c 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -718,6 +718,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) "burst" : ""); drm_printf(&p, "Burst mode ratio %d\n", intel_dsi->burst_mode_ratio); drm_printf(&p, "Reset timer %d\n", intel_dsi->rst_timer_val); + drm_printf(&p, "LP clock during LPM %s\n", str_enabled_disabled(intel_dsi->lp_clock_during_lpm)); drm_printf(&p, "Blanking packets during BLLP %s\n", str_enabled_disabled(intel_dsi->blanking_pkt)); drm_printf(&p, "EoT packet %s\n", str_enabled_disabled(intel_dsi->eot_pkt)); drm_printf(&p, "Clock stop during BLLP %s\n", str_enabled_disabled(intel_dsi->clock_stop)); @@ -771,6 +772,7 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) drm_dbg_kms(display->drm, "\n"); + intel_dsi->lp_clock_during_lpm = mipi_config->lp_clock_during_lpm; intel_dsi->blanking_pkt = mipi_config->blanking_packets_during_bllp; intel_dsi->eot_pkt = !mipi_config->eot_pkt_disabled; intel_dsi->clock_stop = mipi_config->enable_clk_stop; From fde38b106d50f59f904c72544aa34e41adb14fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 23 Mar 2026 12:16:08 +0200 Subject: [PATCH 67/68] drm/i915/selftests: Nuke live_forcewake_domains selftest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The live_forcewake_domains selftest doesn't really test anything particularly sensible. It only runs on platforms that have RMbus unclaimer error detection, but that only catches display registers which the test doesn't even access. I suppose if we really wanted to we might try to make the test exercise the GT FIFO instead by writing GT registers as fast as possible, and then checking GTFIFODBG to see if the FIFO has overflowed. But dunno if there's much point in that. I think a GT FIFO overflow might even be fatal to the machine. So in its current for the test doesn't really make sense, and it's in the way of moving all the RMbus noclaim stuff to the display driver side. So let's just get rid of it. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260323101609.8391-2-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- drivers/gpu/drm/i915/selftests/intel_uncore.c | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/drivers/gpu/drm/i915/selftests/intel_uncore.c b/drivers/gpu/drm/i915/selftests/intel_uncore.c index 507bf42a1aaf..514d2200751b 100644 --- a/drivers/gpu/drm/i915/selftests/intel_uncore.c +++ b/drivers/gpu/drm/i915/selftests/intel_uncore.c @@ -272,67 +272,6 @@ static int live_forcewake_ops(void *arg) return err; } -static int live_forcewake_domains(void *arg) -{ -#define FW_RANGE 0x40000 - struct intel_gt *gt = arg; - struct intel_uncore *uncore = gt->uncore; - struct drm_i915_private *i915 = gt->i915; - struct intel_display *display = i915->display; - unsigned long *valid; - u32 offset; - int err; - - if (!HAS_FPGA_DBG_UNCLAIMED(display) && - !IS_VALLEYVIEW(i915) && - !IS_CHERRYVIEW(i915)) - return 0; - - /* - * This test may lockup the machine or cause GPU hangs afterwards. - */ - if (!IS_ENABLED(CONFIG_DRM_I915_SELFTEST_BROKEN)) - return 0; - - valid = bitmap_zalloc(FW_RANGE, GFP_KERNEL); - if (!valid) - return -ENOMEM; - - intel_uncore_forcewake_get(uncore, FORCEWAKE_ALL); - - check_for_unclaimed_mmio(uncore); - for (offset = 0; offset < FW_RANGE; offset += 4) { - i915_reg_t reg = { offset }; - - intel_uncore_posting_read_fw(uncore, reg); - if (!check_for_unclaimed_mmio(uncore)) - set_bit(offset, valid); - } - - intel_uncore_forcewake_put(uncore, FORCEWAKE_ALL); - - err = 0; - for_each_set_bit(offset, valid, FW_RANGE) { - i915_reg_t reg = { offset }; - - iosf_mbi_punit_acquire(); - intel_uncore_forcewake_reset(uncore); - iosf_mbi_punit_release(); - - check_for_unclaimed_mmio(uncore); - - intel_uncore_posting_read_fw(uncore, reg); - if (check_for_unclaimed_mmio(uncore)) { - pr_err("Unclaimed mmio read to register 0x%04x\n", - offset); - err = -EINVAL; - } - } - - bitmap_free(valid); - return err; -} - static int live_fw_table(void *arg) { struct intel_gt *gt = arg; @@ -348,7 +287,6 @@ int intel_uncore_live_selftests(struct drm_i915_private *i915) static const struct i915_subtest tests[] = { SUBTEST(live_fw_table), SUBTEST(live_forcewake_ops), - SUBTEST(live_forcewake_domains), }; return intel_gt_live_subtests(tests, to_gt(i915)); From e012fa31f90de0928d85ab22d9cc5fc8fe84c5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 23 Mar 2026 12:16:09 +0200 Subject: [PATCH 68/68] drm/i915/uncore: Do GT FIFO checks in early sanitize and forcewake get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're mixing up the GT FIFO debug checks (overflows and such) with RMbus unclaimed register checks. The two are quite different things as RMbus is only relevant for display registers, and the GT FIFO only relevant for GT registers. Split the GT FIFO debugs out from the unclaimed register logic and just do the checks during forcewake_get() and early init. That is still sufficient to detect if any errors have happened. Any errors would anyway be caused by overflowing the FIFO rather than accessing specific registers, so trying to figure out exactly when the error happened isn't particularly useful. To fix such issues we'd rather have to do something to slow down the rate at which registers are accessed (eg. increase GT_FIFO_NUM_RESERVED_ENTRIES or something). Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260323101609.8391-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/intel_uncore.c | 72 ++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 5b698d4d7a7f..170e83a8c9fc 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -399,6 +399,35 @@ static void fw_domains_get_with_thread_status(struct intel_uncore *uncore, __gen6_gt_wait_for_thread_c0(uncore); } +static void +gen6_check_for_fifo_debug(struct intel_uncore *uncore) +{ + u32 fifodbg; + + fifodbg = __raw_uncore_read32(uncore, GTFIFODBG); + + if (unlikely(fifodbg)) { + drm_dbg(&uncore->i915->drm, "GTFIFODBG = 0x08%x\n", fifodbg); + __raw_uncore_write32(uncore, GTFIFODBG, fifodbg); + } +} + +static void +fw_domains_get_normal_fifo(struct intel_uncore *uncore, + enum forcewake_domains fw_domains) +{ + gen6_check_for_fifo_debug(uncore); + fw_domains_get_normal(uncore, fw_domains); +} + +static void +fw_domains_get_with_thread_status_fifo(struct intel_uncore *uncore, + enum forcewake_domains fw_domains) +{ + gen6_check_for_fifo_debug(uncore); + fw_domains_get_with_thread_status(uncore, fw_domains); +} + static inline u32 fifo_free_entries(struct intel_uncore *uncore) { u32 count = __raw_uncore_read32(uncore, GTFIFOCTL); @@ -561,21 +590,6 @@ vlv_check_for_unclaimed_mmio(struct intel_uncore *uncore) return true; } -static bool -gen6_check_for_fifo_debug(struct intel_uncore *uncore) -{ - u32 fifodbg; - - fifodbg = __raw_uncore_read32(uncore, GTFIFODBG); - - if (unlikely(fifodbg)) { - drm_dbg(&uncore->i915->drm, "GTFIFODBG = 0x08%x\n", fifodbg); - __raw_uncore_write32(uncore, GTFIFODBG, fifodbg); - } - - return fifodbg; -} - static bool check_for_unclaimed_mmio(struct intel_uncore *uncore) { @@ -592,9 +606,6 @@ check_for_unclaimed_mmio(struct intel_uncore *uncore) if (intel_uncore_has_dbg_unclaimed(uncore)) ret |= vlv_check_for_unclaimed_mmio(uncore); - if (intel_uncore_has_fifo(uncore)) - ret |= gen6_check_for_fifo_debug(uncore); - return ret; } @@ -611,6 +622,9 @@ static void forcewake_early_sanitize(struct intel_uncore *uncore, GT_FIFO_CTL_RC6_POLICY_STALL); } + if (intel_uncore_has_fifo(uncore)) + gen6_check_for_fifo_debug(uncore); + iosf_mbi_punit_acquire(); intel_uncore_forcewake_reset(uncore); if (restore_forcewake) { @@ -2155,6 +2169,14 @@ static const struct intel_uncore_fw_get uncore_get_thread_status = { .force_wake_get = fw_domains_get_with_thread_status }; +static const struct intel_uncore_fw_get uncore_get_normal_fifo = { + .force_wake_get = fw_domains_get_normal_fifo, +}; + +static const struct intel_uncore_fw_get uncore_get_thread_status_fifo = { + .force_wake_get = fw_domains_get_with_thread_status_fifo +}; + static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) { struct drm_i915_private *i915 = uncore->i915; @@ -2218,13 +2240,19 @@ static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA, FORCEWAKE_MEDIA_GEN9, FORCEWAKE_ACK_MEDIA_GEN9); } else if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) { - uncore->fw_get_funcs = &uncore_get_normal; + if (intel_uncore_has_fifo(uncore)) + uncore->fw_get_funcs = &uncore_get_normal_fifo; + else + uncore->fw_get_funcs = &uncore_get_normal; fw_domain_init(uncore, FW_DOMAIN_ID_RENDER, FORCEWAKE_VLV, FORCEWAKE_ACK_VLV); fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA, FORCEWAKE_MEDIA_VLV, FORCEWAKE_ACK_MEDIA_VLV); } else if (IS_HASWELL(i915) || IS_BROADWELL(i915)) { - uncore->fw_get_funcs = &uncore_get_thread_status; + if (intel_uncore_has_fifo(uncore)) + uncore->fw_get_funcs = &uncore_get_thread_status_fifo; + else + uncore->fw_get_funcs = &uncore_get_thread_status; fw_domain_init(uncore, FW_DOMAIN_ID_RENDER, FORCEWAKE_MT, FORCEWAKE_ACK_HSW); } else if (IS_IVYBRIDGE(i915)) { @@ -2239,7 +2267,7 @@ static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) * (correctly) interpreted by the test below as MT * forcewake being disabled. */ - uncore->fw_get_funcs = &uncore_get_thread_status; + uncore->fw_get_funcs = &uncore_get_thread_status_fifo; /* We need to init first for ECOBUS access and then * determine later if we want to reinit, in case of MT access is @@ -2270,7 +2298,7 @@ static int intel_uncore_fw_domains_init(struct intel_uncore *uncore) FORCEWAKE, FORCEWAKE_ACK); } } else if (GRAPHICS_VER(i915) == 6) { - uncore->fw_get_funcs = &uncore_get_thread_status; + uncore->fw_get_funcs = &uncore_get_thread_status_fifo; fw_domain_init(uncore, FW_DOMAIN_ID_RENDER, FORCEWAKE, FORCEWAKE_ACK); }