From e89aacd1ecdd3d13e8f347aa082687878621e03c Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 14 Jan 2026 10:49:05 -0800 Subject: [PATCH 0001/1735] drm/xe: Reduce LRC timestamp stuck message on VFs to notice An LRC timestamp getting stuck is a somewhat normal occurrence. If a single VF submits a job that does not get timesliced, the LRC timestamp will not increment. Reduce the LRC timestamp stuck message on VFs to notice (same log level as job timeout) to avoid false CI bugs in tests where a VF submits a job that does not get timesliced. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7032 Fixes: bb63e7257e63 ("drm/xe: Avoid toggling schedule state to check LRC timestamp in TDR") Suggested-by: Daniele Ceraolo Spurio Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260114184905.4189026-1-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_guc_submit.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index a27ea931b956..1b2f66f4425b 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1317,9 +1317,14 @@ static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job) ctx_timestamp = lower_32_bits(xe_lrc_timestamp(q->lrc[0])); if (ctx_timestamp == job->sample_timestamp) { - xe_gt_warn(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, timestamp stuck", - xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job), - q->guc->id); + if (IS_SRIOV_VF(gt_to_xe(gt))) + xe_gt_notice(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, timestamp stuck", + xe_sched_job_seqno(job), + xe_sched_job_lrc_seqno(job), q->guc->id); + else + xe_gt_warn(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, timestamp stuck", + xe_sched_job_seqno(job), + xe_sched_job_lrc_seqno(job), q->guc->id); return xe_sched_invalidate_job(job, 0); } From 889ff8dd4679ae7b608f79ebbbd511a3b8b315c1 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:18 +0200 Subject: [PATCH 0002/1735] drm/i915/dsc: Track the detaild DSC slice configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a way to track the detailed DSC pipes-per-line, streams-per-pipe, slices-per-stream configuration instead of the current streams-per-pipe and slices-per-line value. This way describes the slice configuration in a clearer way, for instance providing a 2 pipes-per-line x 2 streams-per-pipe x 2 slices-per-stream = 8 slices-per-line view, instead of the current, coarser 2 streams-per-pipe, 8 slices-per-line view, the former better reflecting that each DSC stream engine has 2 slices. This also let's optimizing the configuration in a simpler/clearer way, for instance using 1 stream x 2 slices, or 1 stream x 4 slices instead of the current 2 stream x 1 slice, or 2 streams x 2 slices configuration (so that 1 DSC stream engine can be powered off in each pipe). Follow-up changes will convert the current slices-per-line computation logic to compute instead the above detailed slice configuration. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-2-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display_types.h | 5 +++++ drivers/gpu/drm/i915/display/intel_vdsc.c | 5 +++++ drivers/gpu/drm/i915/display/intel_vdsc.h | 2 ++ 3 files changed, 12 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 113e43bc1f6d..7d1654f60094 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1333,6 +1333,11 @@ struct intel_crtc_state { bool compression_enabled_on_link; bool compression_enable; int num_streams; + struct intel_dsc_slice_config { + int pipes_per_line; + int streams_per_pipe; + int slices_per_stream; + } slice_config; /* Compressed Bpp in U6.4 format (first 4 bits for fractional part) */ u16 compressed_bpp_x16; u8 slice_count; diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index 5493082f30a7..f8e4b2aa6c17 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -35,6 +35,11 @@ bool intel_dsc_source_support(const struct intel_crtc_state *crtc_state) return true; } +int intel_dsc_line_slice_count(const struct intel_dsc_slice_config *config) +{ + return config->pipes_per_line * config->streams_per_pipe * config->slices_per_stream; +} + static bool is_pipe_dsc(struct intel_crtc *crtc, enum transcoder cpu_transcoder) { struct intel_display *display = to_intel_display(crtc); diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.h b/drivers/gpu/drm/i915/display/intel_vdsc.h index 99f64ac54b27..e61116d5297c 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.h +++ b/drivers/gpu/drm/i915/display/intel_vdsc.h @@ -13,9 +13,11 @@ struct drm_printer; enum transcoder; struct intel_crtc; struct intel_crtc_state; +struct intel_dsc_slice_config; struct intel_encoder; bool intel_dsc_source_support(const struct intel_crtc_state *crtc_state); +int intel_dsc_line_slice_count(const struct intel_dsc_slice_config *config); void intel_uncompressed_joiner_enable(const struct intel_crtc_state *crtc_state); void intel_dsc_enable(const struct intel_crtc_state *crtc_state); void intel_dsc_disable(const struct intel_crtc_state *crtc_state); From 2b8f5b5cb29786f03cd1805aad42da41ad7c51a4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:19 +0200 Subject: [PATCH 0003/1735] drm/i915/dsc: Track the DSC stream count in the DSC slice config state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the tracking for the DSC stream count from intel_crtc_state::dsc.num_streams to intel_crtc_state::dsc.slice_config.streams_per_pipe. While at it add a TODO comment to read out the full DSC configuration from HW including the pipes-per-line and slices-per-stream values. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-3-imre.deak@intel.com --- drivers/gpu/drm/i915/display/icl_dsi.c | 4 ++-- drivers/gpu/drm/i915/display/intel_display.c | 2 +- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/intel_dp.c | 6 +++--- drivers/gpu/drm/i915/display/intel_vdsc.c | 11 ++++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index dac781f54661..5b64e8d6e838 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1626,9 +1626,9 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder, /* FIXME: split only when necessary */ if (crtc_state->dsc.slice_count > 1) - crtc_state->dsc.num_streams = 2; + crtc_state->dsc.slice_config.streams_per_pipe = 2; else - crtc_state->dsc.num_streams = 1; + crtc_state->dsc.slice_config.streams_per_pipe = 1; /* FIXME: initialize from VBT */ vdsc_cfg->rc_model_size = DSC_RC_MODEL_SIZE_CONST; diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 81b3a6692ca2..7491e00e3858 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5459,7 +5459,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(dsc.config.nsl_bpg_offset); PIPE_CONF_CHECK_BOOL(dsc.compression_enable); - PIPE_CONF_CHECK_I(dsc.num_streams); + PIPE_CONF_CHECK_I(dsc.slice_config.streams_per_pipe); PIPE_CONF_CHECK_I(dsc.compressed_bpp_x16); PIPE_CONF_CHECK_BOOL(splitter.enable); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 7d1654f60094..f223cabb185a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1332,7 +1332,6 @@ struct intel_crtc_state { /* Only used for state computation, not read out from the HW. */ bool compression_enabled_on_link; bool compression_enable; - int num_streams; struct intel_dsc_slice_config { int pipes_per_line; int streams_per_pipe; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 7e022c47e8ac..396c25d15af5 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2418,11 +2418,11 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, */ if (pipe_config->joiner_pipes && num_joined_pipes == 4 && pipe_config->dsc.slice_count == 12) - pipe_config->dsc.num_streams = 3; + pipe_config->dsc.slice_config.streams_per_pipe = 3; else if (pipe_config->joiner_pipes || pipe_config->dsc.slice_count > 1) - pipe_config->dsc.num_streams = 2; + pipe_config->dsc.slice_config.streams_per_pipe = 2; else - pipe_config->dsc.num_streams = 1; + pipe_config->dsc.slice_config.streams_per_pipe = 1; ret = intel_dp_dsc_compute_params(connector, pipe_config); if (ret < 0) { diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index f8e4b2aa6c17..4a3d505338cb 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -421,7 +421,7 @@ intel_dsc_power_domain(struct intel_crtc *crtc, enum transcoder cpu_transcoder) static int intel_dsc_get_vdsc_per_pipe(const struct intel_crtc_state *crtc_state) { - return crtc_state->dsc.num_streams; + return crtc_state->dsc.slice_config.streams_per_pipe; } int intel_dsc_get_num_vdsc_instances(const struct intel_crtc_state *crtc_state) @@ -1023,12 +1023,13 @@ void intel_dsc_get_config(struct intel_crtc_state *crtc_state) if (!crtc_state->dsc.compression_enable) goto out; + /* TODO: Read out slice_config.pipes_per_line/slices_per_stream as well */ if (dss_ctl1 & JOINER_ENABLE && dss_ctl2 & (VDSC2_ENABLE | SMALL_JOINER_CONFIG_3_ENGINES)) - crtc_state->dsc.num_streams = 3; + crtc_state->dsc.slice_config.streams_per_pipe = 3; else if (dss_ctl1 & JOINER_ENABLE && dss_ctl2 & VDSC1_ENABLE) - crtc_state->dsc.num_streams = 2; + crtc_state->dsc.slice_config.streams_per_pipe = 2; else - crtc_state->dsc.num_streams = 1; + crtc_state->dsc.slice_config.streams_per_pipe = 1; intel_dsc_get_pps_config(crtc_state); out: @@ -1042,7 +1043,7 @@ static void intel_vdsc_dump_state(struct drm_printer *p, int indent, "dsc-dss: compressed-bpp:" FXP_Q4_FMT ", slice-count: %d, num_streams: %d\n", FXP_Q4_ARGS(crtc_state->dsc.compressed_bpp_x16), crtc_state->dsc.slice_count, - crtc_state->dsc.num_streams); + crtc_state->dsc.slice_config.streams_per_pipe); } void intel_vdsc_state_dump(struct drm_printer *p, int indent, From 611cadd5c02b04ac461a5dddf3070ce4c1094829 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:20 +0200 Subject: [PATCH 0004/1735] drm/i915/dsi: Move initialization of DSI DSC streams-per-pipe to fill_dsc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the initialization of the DSI DSC streams-per-pipe value to fill_dsc() next to where the corresponding (per-line) slice_count value is initialized. This allows converting the initialization to use the detailed slice configuration state in follow-up changes. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-4-imre.deak@intel.com --- drivers/gpu/drm/i915/display/icl_dsi.c | 6 ------ drivers/gpu/drm/i915/display/intel_bios.c | 5 +++++ 2 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 5b64e8d6e838..c8e0333706c1 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1624,12 +1624,6 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder, if (crtc_state->pipe_bpp < 8 * 3) return -EINVAL; - /* FIXME: split only when necessary */ - if (crtc_state->dsc.slice_count > 1) - crtc_state->dsc.slice_config.streams_per_pipe = 2; - else - crtc_state->dsc.slice_config.streams_per_pipe = 1; - /* FIXME: initialize from VBT */ vdsc_cfg->rc_model_size = DSC_RC_MODEL_SIZE_CONST; diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index ae0b922d5bc3..9b4428472831 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -3575,10 +3575,14 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, * throughput etc. into account. * * Also, per spec DSI supports 1, 2, 3 or 4 horizontal slices. + * + * FIXME: split only when necessary */ if (dsc->slices_per_line & BIT(2)) { + crtc_state->dsc.slice_config.streams_per_pipe = 2; crtc_state->dsc.slice_count = 4; } else if (dsc->slices_per_line & BIT(1)) { + crtc_state->dsc.slice_config.streams_per_pipe = 2; crtc_state->dsc.slice_count = 2; } else { /* FIXME */ @@ -3586,6 +3590,7 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, drm_dbg_kms(display->drm, "VBT: Unsupported DSC slice count for DSI\n"); + crtc_state->dsc.slice_config.streams_per_pipe = 1; crtc_state->dsc.slice_count = 1; } From 57152a502ed2a4d8f0470321c9d482a76daa3cb9 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:21 +0200 Subject: [PATCH 0005/1735] drm/i915/dsi: Track the detailed DSC slice configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tracking for the DSI DSC pipes-per-line and slices-per-stream value in the slice config state and compute the current slices-per-line value using this slice config state. The slices-per-line value used atm will be removed by a follow-up change after converting all the places using it to use the detailed slice config instead. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-5-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_bios.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 9b4428472831..8fcfdb2e1c74 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -41,6 +41,7 @@ #include "intel_display_utils.h" #include "intel_gmbus.h" #include "intel_rom.h" +#include "intel_vdsc.h" #define _INTEL_BIOS_PRIVATE #include "intel_vbt_defs.h" @@ -3578,12 +3579,14 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, * * FIXME: split only when necessary */ + crtc_state->dsc.slice_config.pipes_per_line = 1; + if (dsc->slices_per_line & BIT(2)) { crtc_state->dsc.slice_config.streams_per_pipe = 2; - crtc_state->dsc.slice_count = 4; + crtc_state->dsc.slice_config.slices_per_stream = 2; } else if (dsc->slices_per_line & BIT(1)) { crtc_state->dsc.slice_config.streams_per_pipe = 2; - crtc_state->dsc.slice_count = 2; + crtc_state->dsc.slice_config.slices_per_stream = 1; } else { /* FIXME */ if (!(dsc->slices_per_line & BIT(0))) @@ -3591,9 +3594,11 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, "VBT: Unsupported DSC slice count for DSI\n"); crtc_state->dsc.slice_config.streams_per_pipe = 1; - crtc_state->dsc.slice_count = 1; + crtc_state->dsc.slice_config.slices_per_stream = 1; } + crtc_state->dsc.slice_count = intel_dsc_line_slice_count(&crtc_state->dsc.slice_config); + if (crtc_state->hw.adjusted_mode.crtc_hdisplay % crtc_state->dsc.slice_count != 0) drm_dbg_kms(display->drm, From fd1e610ca21861f582e57e26e5e5628d01c6c36d Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:22 +0200 Subject: [PATCH 0006/1735] drm/i915/dp: Track the detailed DSC slice configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tracking for the DP DSC pipes-per-line and slices-per-stream value in the slice config state and compute the current slices-per-line (slice_count) value using this slice config. The slices-per-line value used atm will be removed by a follow-up change after converting all the places using it to use the slice config instead. For now the slices-per-stream value is calculated based on the slices-per-line value (slice_count) calculated by the drm_dp_dsc_sink_max_slice_count() / intel_dp_dsc_get_slice_count() functions. In a follow-up change these functions will be converted to calculate the slices-per-stream value directly, along with the detailed slice configuration. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-6-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 32 ++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 396c25d15af5..3b62d16403f2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2357,6 +2357,7 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, &pipe_config->hw.adjusted_mode; int num_joined_pipes = intel_crtc_num_joined_pipes(pipe_config); bool is_mst = intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST); + int slices_per_line; int ret; /* @@ -2384,30 +2385,26 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, /* Calculate Slice count */ if (intel_dp_is_edp(intel_dp)) { - pipe_config->dsc.slice_count = + slices_per_line = drm_dp_dsc_sink_max_slice_count(connector->dp.dsc_dpcd, true); - if (!pipe_config->dsc.slice_count) { + if (!slices_per_line) { drm_dbg_kms(display->drm, "Unsupported Slice Count %d\n", - pipe_config->dsc.slice_count); + slices_per_line); return -EINVAL; } } else { - u8 dsc_dp_slice_count; - - dsc_dp_slice_count = + slices_per_line = intel_dp_dsc_get_slice_count(connector, adjusted_mode->crtc_clock, adjusted_mode->crtc_hdisplay, num_joined_pipes); - if (!dsc_dp_slice_count) { + if (!slices_per_line) { drm_dbg_kms(display->drm, "Compressed Slice Count not supported\n"); return -EINVAL; } - - pipe_config->dsc.slice_count = dsc_dp_slice_count; } /* * VDSC engine operates at 1 Pixel per clock, so if peak pixel rate @@ -2416,14 +2413,27 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, * In case of Ultrajoiner along with 12 slices we need to use 3 * VDSC instances. */ + pipe_config->dsc.slice_config.pipes_per_line = num_joined_pipes; + if (pipe_config->joiner_pipes && num_joined_pipes == 4 && - pipe_config->dsc.slice_count == 12) + slices_per_line == 12) pipe_config->dsc.slice_config.streams_per_pipe = 3; - else if (pipe_config->joiner_pipes || pipe_config->dsc.slice_count > 1) + else if (pipe_config->joiner_pipes || slices_per_line > 1) pipe_config->dsc.slice_config.streams_per_pipe = 2; else pipe_config->dsc.slice_config.streams_per_pipe = 1; + pipe_config->dsc.slice_config.slices_per_stream = + slices_per_line / + pipe_config->dsc.slice_config.pipes_per_line / + pipe_config->dsc.slice_config.streams_per_pipe; + + pipe_config->dsc.slice_count = + intel_dsc_line_slice_count(&pipe_config->dsc.slice_config); + + drm_WARN_ON(display->drm, + pipe_config->dsc.slice_count != slices_per_line); + ret = intel_dp_dsc_compute_params(connector, pipe_config); if (ret < 0) { drm_dbg_kms(display->drm, From e941eb107847f7a910e309799cdde49493b857cf Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:23 +0200 Subject: [PATCH 0007/1735] drm/i915/dsc: Switch to using intel_dsc_line_slice_count() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By now all the places are updated to track the DSC slice configuration in intel_crtc_state::dsc.slice_config, so calculate the slices-per-line value using that config, instead of using intel_crtc_state::dsc.slice_count caching the same value and remove the cached slice_count. v2: Rebase on latest drm-tip, converting another user of dsc.slice_count in intel_vdsc_min_cdclk(). Cc: Ankit Nautiyal Reviewed-by: Jouni Högander # v1 Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-7-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_bios.c | 6 ++---- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/intel_dp.c | 11 +++++------ drivers/gpu/drm/i915/display/intel_vdsc.c | 9 +++++---- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 8fcfdb2e1c74..a007fcf6e1a8 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -3597,14 +3597,12 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, crtc_state->dsc.slice_config.slices_per_stream = 1; } - crtc_state->dsc.slice_count = intel_dsc_line_slice_count(&crtc_state->dsc.slice_config); - if (crtc_state->hw.adjusted_mode.crtc_hdisplay % - crtc_state->dsc.slice_count != 0) + intel_dsc_line_slice_count(&crtc_state->dsc.slice_config) != 0) drm_dbg_kms(display->drm, "VBT: DSC hdisplay %d not divisible by slice count %d\n", crtc_state->hw.adjusted_mode.crtc_hdisplay, - crtc_state->dsc.slice_count); + intel_dsc_line_slice_count(&crtc_state->dsc.slice_config)); /* * The VBT rc_buffer_block_size and rc_buffer_size definitions diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index f223cabb185a..c7a8e475cb22 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1339,7 +1339,6 @@ struct intel_crtc_state { } slice_config; /* Compressed Bpp in U6.4 format (first 4 bits for fractional part) */ u16 compressed_bpp_x16; - u8 slice_count; struct drm_dsc_config config; } dsc; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 3b62d16403f2..3a12156cd6e5 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2032,12 +2032,14 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, } else { unsigned long bw_overhead_flags = pipe_config->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0; + int line_slice_count = + intel_dsc_line_slice_count(&pipe_config->dsc.slice_config); if (!is_bw_sufficient_for_dsc_config(intel_dp, link_rate, lane_count, adjusted_mode->crtc_clock, adjusted_mode->hdisplay, - pipe_config->dsc.slice_count, + line_slice_count, dsc_bpp_x16, bw_overhead_flags)) continue; @@ -2428,11 +2430,8 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, pipe_config->dsc.slice_config.pipes_per_line / pipe_config->dsc.slice_config.streams_per_pipe; - pipe_config->dsc.slice_count = - intel_dsc_line_slice_count(&pipe_config->dsc.slice_config); - drm_WARN_ON(display->drm, - pipe_config->dsc.slice_count != slices_per_line); + intel_dsc_line_slice_count(&pipe_config->dsc.slice_config) != slices_per_line); ret = intel_dp_dsc_compute_params(connector, pipe_config); if (ret < 0) { @@ -2450,7 +2449,7 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, "Compressed Bpp = " FXP_Q4_FMT " Slice Count = %d\n", pipe_config->pipe_bpp, FXP_Q4_ARGS(pipe_config->dsc.compressed_bpp_x16), - pipe_config->dsc.slice_count); + intel_dsc_line_slice_count(&pipe_config->dsc.slice_config)); return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index 4a3d505338cb..d213947103b5 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -283,8 +283,9 @@ int intel_dsc_compute_params(struct intel_crtc_state *pipe_config) int ret; vdsc_cfg->pic_width = pipe_config->hw.adjusted_mode.crtc_hdisplay; - vdsc_cfg->slice_width = DIV_ROUND_UP(vdsc_cfg->pic_width, - pipe_config->dsc.slice_count); + vdsc_cfg->slice_width = + DIV_ROUND_UP(vdsc_cfg->pic_width, + intel_dsc_line_slice_count(&pipe_config->dsc.slice_config)); err = intel_dsc_slice_dimensions_valid(pipe_config, vdsc_cfg); @@ -1042,7 +1043,7 @@ static void intel_vdsc_dump_state(struct drm_printer *p, int indent, drm_printf_indent(p, indent, "dsc-dss: compressed-bpp:" FXP_Q4_FMT ", slice-count: %d, num_streams: %d\n", FXP_Q4_ARGS(crtc_state->dsc.compressed_bpp_x16), - crtc_state->dsc.slice_count, + intel_dsc_line_slice_count(&crtc_state->dsc.slice_config), crtc_state->dsc.slice_config.streams_per_pipe); } @@ -1078,7 +1079,7 @@ int intel_vdsc_min_cdclk(const struct intel_crtc_state *crtc_state) struct intel_display *display = to_intel_display(crtc_state); int num_vdsc_instances = intel_dsc_get_num_vdsc_instances(crtc_state); int htotal = crtc_state->hw.adjusted_mode.crtc_htotal; - int dsc_slices = crtc_state->dsc.slice_count; + int dsc_slices = intel_dsc_line_slice_count(&crtc_state->dsc.slice_config); int pixel_rate; int min_cdclk; From 15f908bce5d90fcfbc0599e426530cc10e9dfef3 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:24 +0200 Subject: [PATCH 0008/1735] drm/i915/dp: Factor out intel_dp_dsc_min_slice_count() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out intel_dp_dsc_min_slice_count() for making intel_dp_dsc_get_slice_count() more readable and also to prepare for a follow-up change unifying the eDP and DP slice count/config computation. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-8-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 3a12156cd6e5..4455d0a2e59c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -959,14 +959,11 @@ u32 get_max_compressed_bpp_with_joiner(struct intel_display *display, return max_bpp; } -u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, - int mode_clock, int mode_hdisplay, - int num_joined_pipes) +static int intel_dp_dsc_min_slice_count(const struct intel_connector *connector, + int mode_clock, int mode_hdisplay) { struct intel_display *display = to_intel_display(connector); - u32 sink_slice_count_mask = - drm_dp_dsc_sink_slice_count_mask(connector->dp.dsc_dpcd, false); - u8 min_slice_count, i; + u8 min_slice_count; int max_slice_width; int tp_rgb_yuv444; int tp_yuv422_420; @@ -1025,6 +1022,20 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, DIV_ROUND_UP(mode_hdisplay, max_slice_width)); + return min_slice_count; +} + +u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, + int mode_clock, int mode_hdisplay, + int num_joined_pipes) +{ + struct intel_display *display = to_intel_display(connector); + int min_slice_count = + intel_dp_dsc_min_slice_count(connector, mode_clock, mode_hdisplay); + u32 sink_slice_count_mask = + drm_dp_dsc_sink_slice_count_mask(connector->dp.dsc_dpcd, false); + int i; + /* Find the closest match to the valid slice count values */ for (i = 0; i < ARRAY_SIZE(valid_dsc_slicecount); i++) { u8 test_slice_count = valid_dsc_slicecount[i] * num_joined_pipes; From 856428d1ce352b926d2eb026490503ec39424ac8 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:25 +0200 Subject: [PATCH 0009/1735] drm/i915/dp: Use int for DSC slice count variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no reason to use the more specific u8 type for slice count variables, use the more generic int type instead. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-9-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 4455d0a2e59c..bd35a2ba3042 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -963,7 +963,7 @@ static int intel_dp_dsc_min_slice_count(const struct intel_connector *connector, int mode_clock, int mode_hdisplay) { struct intel_display *display = to_intel_display(connector); - u8 min_slice_count; + int min_slice_count; int max_slice_width; int tp_rgb_yuv444; int tp_yuv422_420; @@ -1008,7 +1008,7 @@ static int intel_dp_dsc_min_slice_count(const struct intel_connector *connector, * slice and VDSC engine, whenever we approach close enough to max CDCLK */ if (mode_clock >= ((display->cdclk.max_cdclk_freq * 85) / 100)) - min_slice_count = max_t(u8, min_slice_count, 2); + min_slice_count = max(min_slice_count, 2); max_slice_width = drm_dp_dsc_sink_max_slice_width(connector->dp.dsc_dpcd); if (max_slice_width < DP_DSC_MIN_SLICE_WIDTH_VALUE) { @@ -1018,9 +1018,8 @@ static int intel_dp_dsc_min_slice_count(const struct intel_connector *connector, return 0; } /* Also take into account max slice width */ - min_slice_count = max_t(u8, min_slice_count, - DIV_ROUND_UP(mode_hdisplay, - max_slice_width)); + min_slice_count = max(min_slice_count, + DIV_ROUND_UP(mode_hdisplay, max_slice_width)); return min_slice_count; } @@ -1038,7 +1037,7 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, /* Find the closest match to the valid slice count values */ for (i = 0; i < ARRAY_SIZE(valid_dsc_slicecount); i++) { - u8 test_slice_count = valid_dsc_slicecount[i] * num_joined_pipes; + int test_slice_count = valid_dsc_slicecount[i] * num_joined_pipes; /* * 3 DSC Slices per pipe need 3 DSC engines, which is supported only From 0e6d7b6e502158d6a12725471d57450dcc0b3326 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:26 +0200 Subject: [PATCH 0010/1735] drm/i915/dp: Rename test_slice_count to slices_per_line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename test_slice_count to slices_per_line for clarity. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-10-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index bd35a2ba3042..57abc13a02d2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1037,7 +1037,7 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, /* Find the closest match to the valid slice count values */ for (i = 0; i < ARRAY_SIZE(valid_dsc_slicecount); i++) { - int test_slice_count = valid_dsc_slicecount[i] * num_joined_pipes; + int slices_per_line = valid_dsc_slicecount[i] * num_joined_pipes; /* * 3 DSC Slices per pipe need 3 DSC engines, which is supported only @@ -1047,7 +1047,7 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, (!HAS_DSC_3ENGINES(display) || num_joined_pipes != 4)) continue; - if (!(drm_dp_dsc_slice_count_to_mask(test_slice_count) & + if (!(drm_dp_dsc_slice_count_to_mask(slices_per_line) & sink_slice_count_mask)) continue; @@ -1059,11 +1059,11 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, if (num_joined_pipes > 1 && valid_dsc_slicecount[i] < 2) continue; - if (mode_hdisplay % test_slice_count) + if (mode_hdisplay % slices_per_line) continue; - if (min_slice_count <= test_slice_count) - return test_slice_count; + if (min_slice_count <= slices_per_line) + return slices_per_line; } /* Print slice count 1,2,4,..24 if bit#0,1,3,..23 is set in the mask. */ From da833bb4baf58a8c817818a064bc354429a8c091 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:27 +0200 Subject: [PATCH 0011/1735] drm/i915/dp: Simplify the DSC slice config loop's slices-per-pipe iteration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify the slice config loop in intel_dp_dsc_get_slice_count(), using the loop iterator as the slices-per-pipe value directly, instead of looking up the same value from an array. While at it move the code comment about the slice configuration closer to where the configuration is determined and clarify it a bit. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-11-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 33 ++++++++++--------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 57abc13a02d2..eff4ea998a94 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -107,20 +107,6 @@ /* Constants for DP DSC configurations */ static const u8 valid_dsc_bpp[] = {6, 8, 10, 12, 15}; -/* - * With Single pipe configuration, HW is capable of supporting maximum of: - * 2 slices per line for ICL, BMG - * 4 slices per line for other platforms. - * For now consider a max of 2 slices per line, which works for all platforms. - * With this we can have max of 4 DSC Slices per pipe. - * - * For higher resolutions where 12 slice support is required with - * ultrajoiner, only then each pipe can support 3 slices. - * - * #TODO Split this better to use 4 slices/dsc engine where supported. - */ -static const u8 valid_dsc_slicecount[] = {1, 2, 3, 4}; - /** * intel_dp_is_edp - is the given port attached to an eDP panel (either CPU or PCH) * @intel_dp: DP struct @@ -1033,17 +1019,24 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, intel_dp_dsc_min_slice_count(connector, mode_clock, mode_hdisplay); u32 sink_slice_count_mask = drm_dp_dsc_sink_slice_count_mask(connector->dp.dsc_dpcd, false); - int i; + int slices_per_pipe; - /* Find the closest match to the valid slice count values */ - for (i = 0; i < ARRAY_SIZE(valid_dsc_slicecount); i++) { - int slices_per_line = valid_dsc_slicecount[i] * num_joined_pipes; + /* + * Find the closest match to the valid slice count values + * + * Max HW DSC-per-pipe x slice-per-DSC (= slice-per-pipe) capability: + * ICL: 2x2 + * BMG: 2x2, or for ultrajoined 4 pipes: 3x1 + * TGL+: 2x4 (TODO: Add support for this) + */ + for (slices_per_pipe = 1; slices_per_pipe <= 4; slices_per_pipe++) { + int slices_per_line = slices_per_pipe * num_joined_pipes; /* * 3 DSC Slices per pipe need 3 DSC engines, which is supported only * with Ultrajoiner only for some platforms. */ - if (valid_dsc_slicecount[i] == 3 && + if (slices_per_pipe == 3 && (!HAS_DSC_3ENGINES(display) || num_joined_pipes != 4)) continue; @@ -1056,7 +1049,7 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, * So there should be at least 2 dsc slices per pipe, * whenever bigjoiner is enabled. */ - if (num_joined_pipes > 1 && valid_dsc_slicecount[i] < 2) + if (num_joined_pipes > 1 && slices_per_pipe < 2) continue; if (mode_hdisplay % slices_per_line) From 91f0a9497414443b708e84e60b3813ff119f2444 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:28 +0200 Subject: [PATCH 0012/1735] drm/i915/dsc: Add intel_dsc_get_slice_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add intel_dsc_get_slice_config() and move the logic to select a given slice configuration to that function from the configuration loop in intel_dp_dsc_get_slice_count(). The same functionality can be used by other outputs like DSI as well, done as a follow-up. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-12-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 22 ++++------- drivers/gpu/drm/i915/display/intel_vdsc.c | 47 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_vdsc.h | 4 ++ 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index eff4ea998a94..1d6009b99497 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1030,28 +1030,20 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, * TGL+: 2x4 (TODO: Add support for this) */ for (slices_per_pipe = 1; slices_per_pipe <= 4; slices_per_pipe++) { - int slices_per_line = slices_per_pipe * num_joined_pipes; + struct intel_dsc_slice_config config; + int slices_per_line; - /* - * 3 DSC Slices per pipe need 3 DSC engines, which is supported only - * with Ultrajoiner only for some platforms. - */ - if (slices_per_pipe == 3 && - (!HAS_DSC_3ENGINES(display) || num_joined_pipes != 4)) + if (!intel_dsc_get_slice_config(display, + num_joined_pipes, slices_per_pipe, + &config)) continue; + slices_per_line = intel_dsc_line_slice_count(&config); + if (!(drm_dp_dsc_slice_count_to_mask(slices_per_line) & sink_slice_count_mask)) continue; - /* - * Bigjoiner needs small joiner to be enabled. - * So there should be at least 2 dsc slices per pipe, - * whenever bigjoiner is enabled. - */ - if (num_joined_pipes > 1 && slices_per_pipe < 2) - continue; - if (mode_hdisplay % slices_per_line) continue; diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index d213947103b5..642a89270d8e 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -40,6 +40,53 @@ int intel_dsc_line_slice_count(const struct intel_dsc_slice_config *config) return config->pipes_per_line * config->streams_per_pipe * config->slices_per_stream; } +bool intel_dsc_get_slice_config(struct intel_display *display, + int pipes_per_line, int slices_per_pipe, + struct intel_dsc_slice_config *config) +{ + int streams_per_pipe; + + /* TODO: Add support for 8 slices per pipe on TGL+. */ + switch (slices_per_pipe) { + case 3: + /* + * 3 DSC Slices per pipe need 3 DSC engines, which is supported only + * with Ultrajoiner only for some platforms. + */ + if (!HAS_DSC_3ENGINES(display) || pipes_per_line != 4) + return false; + + streams_per_pipe = 3; + break; + case 4: + /* TODO: Consider using 1 DSC engine stream x 4 slices instead. */ + case 2: + /* TODO: Consider using 1 DSC engine stream x 2 slices instead. */ + streams_per_pipe = 2; + break; + case 1: + /* + * Bigjoiner needs small joiner to be enabled. + * So there should be at least 2 dsc slices per pipe, + * whenever bigjoiner is enabled. + */ + if (pipes_per_line > 1) + return false; + + streams_per_pipe = 1; + break; + default: + MISSING_CASE(slices_per_pipe); + return false; + } + + config->pipes_per_line = pipes_per_line; + config->streams_per_pipe = streams_per_pipe; + config->slices_per_stream = slices_per_pipe / streams_per_pipe; + + return true; +} + static bool is_pipe_dsc(struct intel_crtc *crtc, enum transcoder cpu_transcoder) { struct intel_display *display = to_intel_display(crtc); diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.h b/drivers/gpu/drm/i915/display/intel_vdsc.h index e61116d5297c..aeb17670307b 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.h +++ b/drivers/gpu/drm/i915/display/intel_vdsc.h @@ -13,11 +13,15 @@ struct drm_printer; enum transcoder; struct intel_crtc; struct intel_crtc_state; +struct intel_display; struct intel_dsc_slice_config; struct intel_encoder; bool intel_dsc_source_support(const struct intel_crtc_state *crtc_state); int intel_dsc_line_slice_count(const struct intel_dsc_slice_config *config); +bool intel_dsc_get_slice_config(struct intel_display *display, + int num_joined_pipes, int slice_per_pipe, + struct intel_dsc_slice_config *config); void intel_uncompressed_joiner_enable(const struct intel_crtc_state *crtc_state); void intel_dsc_enable(const struct intel_crtc_state *crtc_state); void intel_dsc_disable(const struct intel_crtc_state *crtc_state); From ba9f0bbecdc462ebf361f65f62842a1e85c8baa8 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:29 +0200 Subject: [PATCH 0013/1735] drm/i915/dsi: Use intel_dsc_get_slice_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use intel_dsc_get_slice_config() for DSI to compute the slice configuration based on the slices-per-line sink capability, instead of open-coding the same. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-13-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_bios.c | 25 ++++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index a007fcf6e1a8..2afc99a39429 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -3545,12 +3545,13 @@ bool intel_bios_is_dsi_present(struct intel_display *display, return false; } -static void fill_dsc(struct intel_crtc_state *crtc_state, +static bool fill_dsc(struct intel_crtc_state *crtc_state, struct dsc_compression_parameters_entry *dsc, int dsc_max_bpc) { struct intel_display *display = to_intel_display(crtc_state); struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config; + int slices_per_line; int bpc = 8; vdsc_cfg->dsc_version_major = dsc->version_major; @@ -3579,24 +3580,24 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, * * FIXME: split only when necessary */ - crtc_state->dsc.slice_config.pipes_per_line = 1; - if (dsc->slices_per_line & BIT(2)) { - crtc_state->dsc.slice_config.streams_per_pipe = 2; - crtc_state->dsc.slice_config.slices_per_stream = 2; + slices_per_line = 4; } else if (dsc->slices_per_line & BIT(1)) { - crtc_state->dsc.slice_config.streams_per_pipe = 2; - crtc_state->dsc.slice_config.slices_per_stream = 1; + slices_per_line = 2; } else { /* FIXME */ if (!(dsc->slices_per_line & BIT(0))) drm_dbg_kms(display->drm, "VBT: Unsupported DSC slice count for DSI\n"); - crtc_state->dsc.slice_config.streams_per_pipe = 1; - crtc_state->dsc.slice_config.slices_per_stream = 1; + slices_per_line = 1; } + if (drm_WARN_ON(display->drm, + !intel_dsc_get_slice_config(display, 1, slices_per_line, + &crtc_state->dsc.slice_config))) + return false; + if (crtc_state->hw.adjusted_mode.crtc_hdisplay % intel_dsc_line_slice_count(&crtc_state->dsc.slice_config) != 0) drm_dbg_kms(display->drm, @@ -3617,6 +3618,8 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, vdsc_cfg->block_pred_enable = dsc->block_prediction_enable; vdsc_cfg->slice_height = dsc->slice_height; + + return true; } /* FIXME: initially DSI specific */ @@ -3637,9 +3640,7 @@ bool intel_bios_get_dsc_params(struct intel_encoder *encoder, if (!devdata->dsc) return false; - fill_dsc(crtc_state, devdata->dsc, dsc_max_bpc); - - return true; + return fill_dsc(crtc_state, devdata->dsc, dsc_max_bpc); } } From 088d06bb17b024866401a20484017cd172ead3d8 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:30 +0200 Subject: [PATCH 0014/1735] drm/i915/dp: Unify DP and eDP slice count computation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify the DP and eDP slices-per-line computation. Atm eDP simply returns the maximum slices-per-line value supported by the sink, but using the same helper function for both cases still makes sense, since a follow-up change will compute the detailed slice config for both cases. Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-14-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 1d6009b99497..2c50e380fb39 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -949,11 +949,20 @@ static int intel_dp_dsc_min_slice_count(const struct intel_connector *connector, int mode_clock, int mode_hdisplay) { struct intel_display *display = to_intel_display(connector); + bool is_edp = + connector->base.connector_type == DRM_MODE_CONNECTOR_eDP; int min_slice_count; int max_slice_width; int tp_rgb_yuv444; int tp_yuv422_420; + /* + * TODO: allow using less than the maximum number of slices + * supported by the eDP sink, to allow using fewer DSC engines. + */ + if (is_edp) + return drm_dp_dsc_sink_max_slice_count(connector->dp.dsc_dpcd, true); + /* * TODO: Use the throughput value specific to the actual RGB/YUV * format of the output. @@ -1017,8 +1026,10 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, struct intel_display *display = to_intel_display(connector); int min_slice_count = intel_dp_dsc_min_slice_count(connector, mode_clock, mode_hdisplay); + bool is_edp = + connector->base.connector_type == DRM_MODE_CONNECTOR_eDP; u32 sink_slice_count_mask = - drm_dp_dsc_sink_slice_count_mask(connector->dp.dsc_dpcd, false); + drm_dp_dsc_sink_slice_count_mask(connector->dp.dsc_dpcd, is_edp); int slices_per_pipe; /* @@ -1471,9 +1482,13 @@ intel_dp_mode_valid(struct drm_connector *_connector, if (intel_dp_is_edp(intel_dp)) { dsc_max_compressed_bpp = drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd) >> 4; + dsc_slice_count = - drm_dp_dsc_sink_max_slice_count(connector->dp.dsc_dpcd, - true); + intel_dp_dsc_get_slice_count(connector, + target_clock, + mode->hdisplay, + num_joined_pipes); + dsc = dsc_max_compressed_bpp && dsc_slice_count; } else if (drm_dp_sink_supports_fec(connector->dp.fec_capability)) { unsigned long bw_overhead_flags = 0; @@ -2381,28 +2396,13 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, } /* Calculate Slice count */ - if (intel_dp_is_edp(intel_dp)) { - slices_per_line = - drm_dp_dsc_sink_max_slice_count(connector->dp.dsc_dpcd, - true); - if (!slices_per_line) { - drm_dbg_kms(display->drm, - "Unsupported Slice Count %d\n", - slices_per_line); - return -EINVAL; - } - } else { - slices_per_line = - intel_dp_dsc_get_slice_count(connector, - adjusted_mode->crtc_clock, - adjusted_mode->crtc_hdisplay, - num_joined_pipes); - if (!slices_per_line) { - drm_dbg_kms(display->drm, - "Compressed Slice Count not supported\n"); - return -EINVAL; - } - } + slices_per_line = intel_dp_dsc_get_slice_count(connector, + adjusted_mode->crtc_clock, + adjusted_mode->crtc_hdisplay, + num_joined_pipes); + if (!slices_per_line) + return -EINVAL; + /* * VDSC engine operates at 1 Pixel per clock, so if peak pixel rate * is greater than the maximum Cdclock and if slice count is even From 54cf7900c6eff4832a349806bef85e0e123aa626 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:31 +0200 Subject: [PATCH 0015/1735] drm/i915/dp: Add intel_dp_dsc_get_slice_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add intel_dp_dsc_get_slice_config() to compute the detailed slice configuration and determine the slices-per-line value (returned by intel_dp_dsc_get_slice_count()) using this function. v2: Fix incorrectly returning false from intel_dp_dsc_min_slice_count() due to rebase fail. (Jouni) Cc: Jouni Högander Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-15-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 31 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 2c50e380fb39..0ea9c4e3b7d3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1019,9 +1019,11 @@ static int intel_dp_dsc_min_slice_count(const struct intel_connector *connector, return min_slice_count; } -u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, - int mode_clock, int mode_hdisplay, - int num_joined_pipes) +static bool +intel_dp_dsc_get_slice_config(const struct intel_connector *connector, + int mode_clock, int mode_hdisplay, + int num_joined_pipes, + struct intel_dsc_slice_config *config_ret) { struct intel_display *display = to_intel_display(connector); int min_slice_count = @@ -1058,8 +1060,11 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, if (mode_hdisplay % slices_per_line) continue; - if (min_slice_count <= slices_per_line) - return slices_per_line; + if (min_slice_count <= slices_per_line) { + *config_ret = config; + + return true; + } } /* Print slice count 1,2,4,..24 if bit#0,1,3,..23 is set in the mask. */ @@ -1070,7 +1075,21 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, min_slice_count, (int)BITS_PER_TYPE(sink_slice_count_mask), &sink_slice_count_mask); - return 0; + return false; +} + +u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, + int mode_clock, int mode_hdisplay, + int num_joined_pipes) +{ + struct intel_dsc_slice_config config; + + if (!intel_dp_dsc_get_slice_config(connector, + mode_clock, mode_hdisplay, + num_joined_pipes, &config)) + return 0; + + return intel_dsc_line_slice_count(&config); } static bool source_can_output(struct intel_dp *intel_dp, From 4d636e0fc26b90be9c56961d8a631f26dcedf4a4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 14 Jan 2026 18:22:32 +0200 Subject: [PATCH 0016/1735] drm/i915/dp: Use intel_dp_dsc_get_slice_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify things by computing the detailed slice configuration using intel_dp_dsc_get_slice_config(), instead of open-coding the same. While at it add a TODO comment to intel_dp_dsc_compute_config() to explore if it's worth increasing the number of VDSC stream engines used, in order to reduce the minimum CDCLK required. v2: Add a TODO comment to intel_dp_dsc_compute_config() to explore if it's worth increasing the number of slices in order to use a lower CDCLK. (Jouni) Reviewed-by: Jouni Högander Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260114162232.92731-16-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 41 ++++++------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 0ea9c4e3b7d3..053443eea9d5 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1041,6 +1041,12 @@ intel_dp_dsc_get_slice_config(const struct intel_connector *connector, * ICL: 2x2 * BMG: 2x2, or for ultrajoined 4 pipes: 3x1 * TGL+: 2x4 (TODO: Add support for this) + * + * TODO: Explore if it's worth increasing the number of slices (from 1 + * to 2 or 3), so that multiple VDSC engines can be used, thus + * reducing the minimum CDCLK requirement, which in turn is determined + * by the 1 pixel per clock VDSC engine throughput in + * intel_vdsc_min_cdclk(). */ for (slices_per_pipe = 1; slices_per_pipe <= 4; slices_per_pipe++) { struct intel_dsc_slice_config config; @@ -2388,7 +2394,6 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, &pipe_config->hw.adjusted_mode; int num_joined_pipes = intel_crtc_num_joined_pipes(pipe_config); bool is_mst = intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST); - int slices_per_line; int ret; /* @@ -2414,39 +2419,11 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, } } - /* Calculate Slice count */ - slices_per_line = intel_dp_dsc_get_slice_count(connector, - adjusted_mode->crtc_clock, - adjusted_mode->crtc_hdisplay, - num_joined_pipes); - if (!slices_per_line) + if (!intel_dp_dsc_get_slice_config(connector, adjusted_mode->crtc_clock, + adjusted_mode->crtc_hdisplay, num_joined_pipes, + &pipe_config->dsc.slice_config)) return -EINVAL; - /* - * VDSC engine operates at 1 Pixel per clock, so if peak pixel rate - * is greater than the maximum Cdclock and if slice count is even - * then we need to use 2 VDSC instances. - * In case of Ultrajoiner along with 12 slices we need to use 3 - * VDSC instances. - */ - pipe_config->dsc.slice_config.pipes_per_line = num_joined_pipes; - - if (pipe_config->joiner_pipes && num_joined_pipes == 4 && - slices_per_line == 12) - pipe_config->dsc.slice_config.streams_per_pipe = 3; - else if (pipe_config->joiner_pipes || slices_per_line > 1) - pipe_config->dsc.slice_config.streams_per_pipe = 2; - else - pipe_config->dsc.slice_config.streams_per_pipe = 1; - - pipe_config->dsc.slice_config.slices_per_stream = - slices_per_line / - pipe_config->dsc.slice_config.pipes_per_line / - pipe_config->dsc.slice_config.streams_per_pipe; - - drm_WARN_ON(display->drm, - intel_dsc_line_slice_count(&pipe_config->dsc.slice_config) != slices_per_line); - ret = intel_dp_dsc_compute_params(connector, pipe_config); if (ret < 0) { drm_dbg_kms(display->drm, From c51595b3d25123cb98bd9b1d6f50e57cc6be592b Mon Sep 17 00:00:00 2001 From: Nakshtra Goyal Date: Tue, 13 Jan 2026 14:49:28 +0530 Subject: [PATCH 0017/1735] drm/xe/xe_query: Remove check for gt There's no need to check a userspace-provided GT ID (which may come from any tile) against the number of GTs that can be present on a single tile. The xe_device_get_gt() lookup already checks that the GT ID passed is valid for the current device.(Matt Roper) Signed-off-by: Nakshtra Goyal Reviewed-by: Matt Roper Link: https://patch.msgid.link/20260113091928.67446-1-nakshtra.goyal@intel.com Signed-off-by: Matt Roper --- drivers/gpu/drm/xe/xe_query.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c index 75490683bad2..b7b4261968e0 100644 --- a/drivers/gpu/drm/xe/xe_query.c +++ b/drivers/gpu/drm/xe/xe_query.c @@ -142,9 +142,6 @@ query_engine_cycles(struct xe_device *xe, return -EINVAL; eci = &resp.eci; - if (eci->gt_id >= xe->info.max_gt_per_tile) - return -EINVAL; - gt = xe_device_get_gt(xe, eci->gt_id); if (!gt) return -EINVAL; From c5a52cd04e24f0ae53fda26f74ab027b8c548e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Thu, 15 Jan 2026 09:00:39 +0200 Subject: [PATCH 0018/1735] drm/i915/psr: Don't enable Panel Replay on sink if globally disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With some panels informing support for Panel Replay we are observing problems if having Panel Replay enable bit set on sink when forced to use PSR instead of Panel Replay. Avoid these problems by not setting Panel Replay enable bit in sink when Panel Replay is globally disabled during link training. I.e. disabled by module parameter. The enable bit is still set when disabling Panel Replay via debugfs interface. Added note comment about this. Fixes: 68f3a505b367 ("drm/i915/psr: Enable Panel Replay on sink always when it's supported") Cc: Mika Kahola Cc: Jani Nikula Cc: Rodrigo Vivi Cc: # v6.15+ Signed-off-by: Jouni Högander Reviewed-by: Mika Kahola Link: https://patch.msgid.link/20260115070039.368965-1-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 91f4ac86c7ad..62208ffc5101 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -842,7 +842,12 @@ static void intel_psr_enable_sink(struct intel_dp *intel_dp, void intel_psr_panel_replay_enable_sink(struct intel_dp *intel_dp) { - if (CAN_PANEL_REPLAY(intel_dp)) + /* + * NOTE: We might want to trigger mode set when + * disabling/enabling Panel Replay via debugfs interface to + * ensure this bit is cleared/set accordingly. + */ + if (CAN_PANEL_REPLAY(intel_dp) && panel_replay_global_enabled(intel_dp)) drm_dp_dpcd_writeb(&intel_dp->aux, PANEL_REPLAY_CONFIG, DP_PANEL_REPLAY_ENABLE); } From bbd36787308413d8564e1b0498fe6c1f765fa6c1 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:03:32 -0800 Subject: [PATCH 0019/1735] drm/xe: Ban entire multi-queue group on any job timeout In multi-queue mode, we only have control over the entire group, so we cannot ban individual queues or signal fences until the whole group is removed from hardware. Implement banning of the entire group if any job within it times out. v2: - Fix lock inversion (Niranjana) - Initialize new queues in group to stopped v3: - Blindly call xe_exec_queue_multi_queue_primary (Niranjana) - More comments around temporary list when stopping (Niranjana) - Restart group on false timeout (Niranjana) Cc: Niranjana Vishwanathapura Signed-off-by: Matthew Brost Reviewed-by: Niranjana Vishwanathapura Link: https://patch.msgid.link/20260116220333.861850-2-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_exec_queue_types.h | 2 + drivers/gpu/drm/xe/xe_guc_submit.c | 120 ++++++++++++++++++----- 2 files changed, 97 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index 5fc516b0bb77..562ea75891ba 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -66,6 +66,8 @@ struct xe_exec_queue_group { bool sync_pending; /** @banned: Group banned */ bool banned; + /** @stopped: Group is stopped, protected by list_lock */ + bool stopped; }; /** diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 1b2f66f4425b..dee0f9004024 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -556,6 +556,72 @@ static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q) xe_sched_tdr_queue_imm(&q->guc->sched); } +static void xe_guc_exec_queue_group_stop(struct xe_exec_queue *q) +{ + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_exec_queue *eq, *next; + LIST_HEAD(tmp); + + xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)), + xe_exec_queue_is_multi_queue(q)); + + mutex_lock(&group->list_lock); + + /* + * Stop all future queues being from executing while group is stopped. + */ + group->stopped = true; + + list_for_each_entry_safe(eq, next, &group->list, multi_queue.link) + /* + * Refcount prevents an attempted removal from &group->list, + * temporary list allows safe iteration after dropping + * &group->list_lock. + */ + if (xe_exec_queue_get_unless_zero(eq)) + list_move_tail(&eq->multi_queue.link, &tmp); + + mutex_unlock(&group->list_lock); + + /* We cannot stop under list lock without getting inversions */ + xe_sched_submission_stop(&primary->guc->sched); + list_for_each_entry(eq, &tmp, multi_queue.link) + xe_sched_submission_stop(&eq->guc->sched); + + mutex_lock(&group->list_lock); + list_for_each_entry_safe(eq, next, &tmp, multi_queue.link) { + /* + * Corner where we got banned while stopping and not on + * &group->list + */ + if (READ_ONCE(group->banned)) + xe_guc_exec_queue_trigger_cleanup(eq); + + list_move_tail(&eq->multi_queue.link, &group->list); + xe_exec_queue_put(eq); + } + mutex_unlock(&group->list_lock); +} + +static void xe_guc_exec_queue_group_start(struct xe_exec_queue *q) +{ + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_exec_queue *eq; + + xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)), + xe_exec_queue_is_multi_queue(q)); + + xe_sched_submission_start(&primary->guc->sched); + + mutex_lock(&group->list_lock); + group->stopped = false; + list_for_each_entry(eq, &group->list, multi_queue.link) + xe_sched_submission_start(&eq->guc->sched); + mutex_unlock(&group->list_lock); +} + static void xe_guc_exec_queue_group_trigger_cleanup(struct xe_exec_queue *q) { struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); @@ -1414,7 +1480,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) { struct xe_sched_job *job = to_xe_sched_job(drm_job); struct drm_sched_job *tmp_job; - struct xe_exec_queue *q = job->q; + struct xe_exec_queue *q = job->q, *primary; struct xe_gpu_scheduler *sched = &q->guc->sched; struct xe_guc *guc = exec_queue_to_guc(q); const char *process_name = "no process"; @@ -1425,6 +1491,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); + primary = xe_exec_queue_multi_queue_primary(q); + /* * TDR has fired before free job worker. Common if exec queue * immediately closed after last fence signaled. Add back to pending @@ -1436,7 +1504,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) return DRM_GPU_SCHED_STAT_NO_HANG; /* Kill the run_job entry point */ - xe_sched_submission_stop(sched); + if (xe_exec_queue_is_multi_queue(q)) + xe_guc_exec_queue_group_stop(q); + else + xe_sched_submission_stop(sched); /* Must check all state after stopping scheduler */ skip_timeout_check = exec_queue_reset(q) || @@ -1451,14 +1522,6 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) if (xe_exec_queue_is_lr(q)) xe_gt_assert(guc_to_gt(guc), skip_timeout_check); - /* - * FIXME: In multi-queue scenario, the TDR must ensure that the whole - * multi-queue group is off the HW before signaling the fences to avoid - * possible memory corruptions. This means disabling scheduling on the - * primary queue before or during the secondary queue's TDR. Need to - * implement this in least obtrusive way. - */ - /* * If devcoredump not captured and GuC capture for the job is not ready * do manual capture first and decide later if we need to use it @@ -1485,10 +1548,11 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) set_exec_queue_banned(q); /* Kick job / queue off hardware */ - if (!wedged && (exec_queue_enabled(q) || exec_queue_pending_disable(q))) { + if (!wedged && (exec_queue_enabled(primary) || + exec_queue_pending_disable(primary))) { int ret; - if (exec_queue_reset(q)) + if (exec_queue_reset(primary)) err = -EIO; if (xe_uc_fw_is_running(&guc->fw)) { @@ -1497,8 +1561,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) * modifying state */ ret = wait_event_timeout(guc->ct.wq, - (!exec_queue_pending_enable(q) && - !exec_queue_pending_disable(q)) || + (!exec_queue_pending_enable(primary) && + !exec_queue_pending_disable(primary)) || xe_guc_read_stopped(guc) || vf_recovery(guc), HZ * 5); if (vf_recovery(guc)) @@ -1506,7 +1570,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) if (!ret || xe_guc_read_stopped(guc)) goto trigger_reset; - disable_scheduling(q, skip_timeout_check); + disable_scheduling(primary, skip_timeout_check); } /* @@ -1520,7 +1584,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) smp_rmb(); ret = wait_event_timeout(guc->ct.wq, !xe_uc_fw_is_running(&guc->fw) || - !exec_queue_pending_disable(q) || + !exec_queue_pending_disable(primary) || xe_guc_read_stopped(guc) || vf_recovery(guc), HZ * 5); if (vf_recovery(guc)) @@ -1530,11 +1594,11 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) if (!ret) xe_gt_warn(guc_to_gt(guc), "Schedule disable failed to respond, guc_id=%d", - q->guc->id); - xe_devcoredump(q, job, + primary->guc->id); + xe_devcoredump(primary, job, "Schedule disable failed to respond, guc_id=%d, ret=%d, guc_read=%d", - q->guc->id, ret, xe_guc_read_stopped(guc)); - xe_gt_reset_async(q->gt); + primary->guc->id, ret, xe_guc_read_stopped(guc)); + xe_gt_reset_async(primary->gt); xe_sched_tdr_queue_imm(sched); goto rearm; } @@ -1580,12 +1644,13 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) drm_sched_for_each_pending_job(tmp_job, &sched->base, NULL) xe_sched_job_set_error(to_xe_sched_job(tmp_job), -ECANCELED); - xe_sched_submission_start(sched); - - if (xe_exec_queue_is_multi_queue(q)) + if (xe_exec_queue_is_multi_queue(q)) { + xe_guc_exec_queue_group_start(q); xe_guc_exec_queue_group_trigger_cleanup(q); - else + } else { + xe_sched_submission_start(sched); xe_guc_exec_queue_trigger_cleanup(q); + } /* * We want the job added back to the pending list so it gets freed; this @@ -1599,7 +1664,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) * but there is not currently an easy way to do in DRM scheduler. With * some thought, do this in a follow up. */ - xe_sched_submission_start(sched); + if (xe_exec_queue_is_multi_queue(q)) + xe_guc_exec_queue_group_start(q); + else + xe_sched_submission_start(sched); handle_vf_resume: return DRM_GPU_SCHED_STAT_NO_HANG; } @@ -1965,6 +2033,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) INIT_LIST_HEAD(&q->multi_queue.link); mutex_lock(&group->list_lock); + if (group->stopped) + WRITE_ONCE(q->guc->sched.base.pause_submit, true); list_add_tail(&q->multi_queue.link, &group->list); mutex_unlock(&group->list_lock); } From 769d7774a1b82f8fde8ce1ff8e4d006e68d8c153 Mon Sep 17 00:00:00 2001 From: Niranjana Vishwanathapura Date: Fri, 16 Jan 2026 14:03:33 -0800 Subject: [PATCH 0020/1735] drm/xe/multi_queue: Enable multi_queue on xe3p_xpc xe3p_xpc supports multi_queue, enable it. v2: Rename multi_queue_enable_mask to multi_queue_engine_class_mask (Matt Brost) Signed-off-by: Niranjana Vishwanathapura Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260116220333.861850-3-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 09189ff3da44..c000c25b5af9 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -112,6 +112,8 @@ static const struct xe_graphics_desc graphics_xe3p_xpc = { .hw_engine_mask = GENMASK(XE_HW_ENGINE_BCS8, XE_HW_ENGINE_BCS1) | GENMASK(XE_HW_ENGINE_CCS3, XE_HW_ENGINE_CCS0), + .multi_queue_engine_class_mask = BIT(XE_ENGINE_CLASS_COPY) | + BIT(XE_ENGINE_CLASS_COMPUTE), }; static const struct xe_media_desc media_xem = { From 888c7f991ffe608a2c9ad9f9420e16c61adea79d Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:21 -0800 Subject: [PATCH 0021/1735] drm/xe: Add normalize_invalidation_range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the code that determines the alignment of TLB invalidation into a helper function — normalize_invalidation_range. This will be useful when adding context-based invalidations to the GuC TLB invalidation backend. Signed-off-by: Nirmoy Das Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-2-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_guc_tlb_inval.c | 71 +++++++++++++-------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index 774467befbb9..43f6dbcb2bc2 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -111,6 +111,38 @@ static int send_page_reclaim(struct xe_guc *guc, u32 seqno, G2H_LEN_DW_PAGE_RECLAMATION, 1); } +static u64 normalize_invalidation_range(struct xe_gt *gt, u64 *start, u64 *end) +{ + u64 orig_start = *start; + u64 length = *end - *start; + u64 align; + + if (length < SZ_4K) + length = SZ_4K; + + align = roundup_pow_of_two(length); + *start = ALIGN_DOWN(*start, align); + *end = ALIGN(*end, align); + length = align; + while (*start + length < *end) { + length <<= 1; + *start = ALIGN_DOWN(orig_start, length); + } + + if (length >= SZ_2M) { + length = max_t(u64, SZ_16M, length); + *start = ALIGN_DOWN(orig_start, length); + } + + xe_gt_assert(gt, length >= SZ_4K); + xe_gt_assert(gt, is_power_of_2(length)); + xe_gt_assert(gt, !(length & GENMASK(ilog2(SZ_16M) - 1, + ilog2(SZ_2M) + 1))); + xe_gt_assert(gt, IS_ALIGNED(*start, length)); + + return length; +} + /* * Ensure that roundup_pow_of_two(length) doesn't overflow. * Note that roundup_pow_of_two() operates on unsigned long, @@ -138,48 +170,15 @@ static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, length > MAX_RANGE_TLB_INVALIDATION_LENGTH) { action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_FULL); } else { - u64 orig_start = start; - u64 align; - - if (length < SZ_4K) - length = SZ_4K; - - /* - * We need to invalidate a higher granularity if start address - * is not aligned to length. When start is not aligned with - * length we need to find the length large enough to create an - * address mask covering the required range. - */ - align = roundup_pow_of_two(length); - start = ALIGN_DOWN(start, align); - end = ALIGN(end, align); - length = align; - while (start + length < end) { - length <<= 1; - start = ALIGN_DOWN(orig_start, length); - } - - /* - * Minimum invalidation size for a 2MB page that the hardware - * expects is 16MB - */ - if (length >= SZ_2M) { - length = max_t(u64, SZ_16M, length); - start = ALIGN_DOWN(orig_start, length); - } - - xe_gt_assert(gt, length >= SZ_4K); - xe_gt_assert(gt, is_power_of_2(length)); - xe_gt_assert(gt, !(length & GENMASK(ilog2(SZ_16M) - 1, - ilog2(SZ_2M) + 1))); - xe_gt_assert(gt, IS_ALIGNED(start, length)); + u64 normalize_len = normalize_invalidation_range(gt, &start, + &end); /* Flush on NULL case, Media is not required to modify flush due to no PPC so NOP */ action[len++] = MAKE_INVAL_OP_FLUSH(XE_GUC_TLB_INVAL_PAGE_SELECTIVE, !prl_sa); action[len++] = asid; action[len++] = lower_32_bits(start); action[len++] = upper_32_bits(start); - action[len++] = ilog2(length) - ilog2(SZ_4K); + action[len++] = ilog2(normalize_len) - ilog2(SZ_4K); } xe_gt_assert(gt, len <= MAX_TLB_INVALIDATION_LEN); From 444d78578e8a79537e527b50fda17d6aa2d30b79 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:22 -0800 Subject: [PATCH 0022/1735] drm/xe: Make usm.asid_to_vm allocation use GFP_NOWAIT Ensure the asid_to_vm lookup is reclaim-safe so it can be performed during TLB invalidations, which is necessary for context-based TLB invalidation support. Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-3-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index bbbc7e71b8ef..24647b128a17 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1653,7 +1653,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef) down_write(&xe->usm.lock); err = xa_alloc_cyclic(&xe->usm.asid_to_vm, &asid, vm, XA_LIMIT(1, XE_MAX_ASID - 1), - &xe->usm.next_asid, GFP_KERNEL); + &xe->usm.next_asid, GFP_NOWAIT); up_write(&xe->usm.lock); if (err < 0) goto err_close; From dea333b244818ab06253b8420a7534fd770eef36 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:23 -0800 Subject: [PATCH 0023/1735] drm/xe: Add has_ctx_tlb_inval to device info Add has_ctx_tlb_inval to device info indicating a device has context basd TLB invalidation. Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-4-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_device_types.h | 2 ++ drivers/gpu/drm/xe/xe_pci.c | 1 + drivers/gpu/drm/xe/xe_pci_types.h | 1 + 3 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index f689766adcb1..72453206267b 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -353,6 +353,8 @@ struct xe_device { u8 has_pre_prod_wa:1; /** @info.has_pxp: Device has PXP support */ u8 has_pxp:1; + /** @info.has_ctx_tlb_inval: Has context based TLB invalidations */ + u8 has_ctx_tlb_inval:1; /** @info.has_range_tlb_inval: Has range based TLB invalidations */ u8 has_range_tlb_inval:1; /** @info.has_soc_remapper_sysctrl: Has SoC remapper system controller */ diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index c000c25b5af9..f367479fe3fb 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -893,6 +893,7 @@ static int xe_info_init(struct xe_device *xe, xe->info.has_device_atomics_on_smem = 1; xe->info.has_range_tlb_inval = graphics_desc->has_range_tlb_inval; + xe->info.has_ctx_tlb_inval = graphics_desc->has_ctx_tlb_inval; xe->info.has_usm = graphics_desc->has_usm; xe->info.has_64bit_timestamp = graphics_desc->has_64bit_timestamp; diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h index 20acc5349ee6..7ccb0ab7a53b 100644 --- a/drivers/gpu/drm/xe/xe_pci_types.h +++ b/drivers/gpu/drm/xe/xe_pci_types.h @@ -72,6 +72,7 @@ struct xe_graphics_desc { u8 has_atomic_enable_pte_bit:1; u8 has_indirect_ring_state:1; u8 has_range_tlb_inval:1; + u8 has_ctx_tlb_inval:1; u8 has_usm:1; u8 has_64bit_timestamp:1; }; From 43c3e6eacb22c7bcf871bd0220a35a03b5aa0e5c Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:24 -0800 Subject: [PATCH 0024/1735] drm/xe: Add xe_device_asid_to_vm helper Introduce the xe_device_asid_to_vm helper, which can be used throughout the driver to resolve the VM from a given ASID. v4: - Move forward declare after includes (Stuart) Signed-off-by: Matthew Brost Reviewed-by: Matt Atwood Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-5-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_device.c | 25 +++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_device.h | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 495310a624b5..aad4aa53a51f 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1375,3 +1375,28 @@ const char *xe_wedged_mode_to_string(enum xe_wedged_mode mode) return ""; } } + +/** + * xe_device_asid_to_vm() - Find VM from ASID + * @xe: the &xe_device + * @asid: Address space ID + * + * Find a VM from ASID and take a reference to VM which caller must drop. + * Reclaim safe. + * + * Return: VM on success, ERR_PTR on failure + */ +struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid) +{ + struct xe_vm *vm; + + down_read(&xe->usm.lock); + vm = xa_load(&xe->usm.asid_to_vm, asid); + if (vm) + xe_vm_get(vm); + else + vm = ERR_PTR(-EINVAL); + up_read(&xe->usm.lock); + + return vm; +} diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h index 3740143790db..d25421e5181c 100644 --- a/drivers/gpu/drm/xe/xe_device.h +++ b/drivers/gpu/drm/xe/xe_device.h @@ -12,6 +12,8 @@ #include "xe_gt_types.h" #include "xe_sriov.h" +struct xe_vm; + static inline struct xe_device *to_xe_device(const struct drm_device *dev) { return container_of(dev, struct xe_device, drm); @@ -204,6 +206,8 @@ int xe_is_injection_active(void); bool xe_is_xe_file(const struct file *file); +struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid); + /* * Occasionally it is seen that the G2H worker starts running after a delay of more than * a second even after being queued and activated by the Linux workqueue subsystem. This From a3866ce7b1221353b795603bb8d0c81d81e60e65 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:25 -0800 Subject: [PATCH 0025/1735] drm/xe: Add vm to exec queues association Maintain a list of exec queues per vm which will be used by TLB invalidation code to do context-ID based tlb invalidations. v4: - More asserts (Stuart) - Per GT list (CI) - Skip adding / removal if context TLB invalidatiions not supported (Stuart) Signed-off-by: Nirmoy Das Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-6-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_device.h | 7 --- drivers/gpu/drm/xe/xe_device_types.h | 7 +++ drivers/gpu/drm/xe/xe_exec_queue.c | 7 ++- drivers/gpu/drm/xe/xe_exec_queue_types.h | 3 ++ drivers/gpu/drm/xe/xe_vm.c | 62 ++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_vm.h | 3 ++ drivers/gpu/drm/xe/xe_vm_types.h | 16 ++++++ 7 files changed, 97 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h index d25421e5181c..58d7d8b2fea3 100644 --- a/drivers/gpu/drm/xe/xe_device.h +++ b/drivers/gpu/drm/xe/xe_device.h @@ -62,13 +62,6 @@ static inline struct xe_tile *xe_device_get_root_tile(struct xe_device *xe) return &xe->tiles[0]; } -/* - * Highest GT/tile count for any platform. Used only for memory allocation - * sizing. Any logic looping over GTs or mapping userspace GT IDs into GT - * structures should use the per-platform xe->info.max_gt_per_tile instead. - */ -#define XE_MAX_GT_PER_TILE 2 - static inline struct xe_gt *xe_device_get_gt(struct xe_device *xe, u8 gt_id) { struct xe_tile *tile; diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index 72453206267b..34feef79fa4e 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -79,6 +79,13 @@ enum xe_wedged_mode { #define XE_GT1 1 #define XE_MAX_TILES_PER_DEVICE (XE_GT1 + 1) +/* + * Highest GT/tile count for any platform. Used only for memory allocation + * sizing. Any logic looping over GTs or mapping userspace GT IDs into GT + * structures should use the per-platform xe->info.max_gt_per_tile instead. + */ +#define XE_MAX_GT_PER_TILE 2 + #define XE_MAX_ASID (BIT(20)) #define IS_PLATFORM_STEP(_xe, _platform, min_step, max_step) \ diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index a940849bb6c7..a58968a0a781 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -152,8 +152,10 @@ static void __xe_exec_queue_free(struct xe_exec_queue *q) if (xe_exec_queue_is_multi_queue(q)) xe_exec_queue_group_cleanup(q); - if (q->vm) + if (q->vm) { + xe_vm_remove_exec_queue(q->vm, q); xe_vm_put(q->vm); + } if (q->xef) xe_file_put(q->xef); @@ -224,6 +226,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe, q->ring_ops = gt->ring_ops[hwe->class]; q->ops = gt->exec_queue_ops; INIT_LIST_HEAD(&q->lr.link); + INIT_LIST_HEAD(&q->vm_exec_queue_link); INIT_LIST_HEAD(&q->multi_gt_link); INIT_LIST_HEAD(&q->hw_engine_group_link); INIT_LIST_HEAD(&q->pxp.link); @@ -1203,6 +1206,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, } q->xef = xe_file_get(xef); + if (eci[0].engine_class != DRM_XE_ENGINE_CLASS_VM_BIND) + xe_vm_add_exec_queue(vm, q); /* user id alloc must always be last in ioctl to prevent UAF */ err = xa_alloc(&xef->exec_queue.xa, &id, q, xa_limit_32b, GFP_KERNEL); diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index 562ea75891ba..e30d295aaaae 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -207,6 +207,9 @@ struct xe_exec_queue { struct dma_fence *last_fence; } tlb_inval[XE_EXEC_QUEUE_TLB_INVAL_COUNT]; + /** @vm_exec_queue_link: Link to track exec queue within a VM's list of exec queues. */ + struct list_head vm_exec_queue_link; + /** @pxp: PXP info tracking */ struct { /** @pxp.type: PXP session type used by this queue */ diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 24647b128a17..e330c794b626 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1529,11 +1529,24 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef) INIT_WORK(&vm->destroy_work, vm_destroy_work_func); INIT_LIST_HEAD(&vm->preempt.exec_queues); + for (id = 0; id < XE_MAX_TILES_PER_DEVICE * XE_MAX_GT_PER_TILE; ++id) + INIT_LIST_HEAD(&vm->exec_queues.list[id]); if (flags & XE_VM_FLAG_FAULT_MODE) vm->preempt.min_run_period_ms = xe->min_run_period_pf_ms; else vm->preempt.min_run_period_ms = xe->min_run_period_lr_ms; + init_rwsem(&vm->exec_queues.lock); + if (IS_ENABLED(CONFIG_PROVE_LOCKING)) { + fs_reclaim_acquire(GFP_KERNEL); + might_lock(&vm->exec_queues.lock); + fs_reclaim_release(GFP_KERNEL); + + down_read(&vm->exec_queues.lock); + might_lock(&xe_root_mmio_gt(xe)->uc.guc.ct.lock); + up_read(&vm->exec_queues.lock); + } + for_each_tile(tile, xe, id) xe_range_fence_tree_init(&vm->rftree[id]); @@ -4569,3 +4582,52 @@ int xe_vm_alloc_cpu_addr_mirror_vma(struct xe_vm *vm, uint64_t start, uint64_t r return xe_vm_alloc_vma(vm, &map_req, false); } +/** + * xe_vm_add_exec_queue() - Add exec queue to VM + * @vm: The VM. + * @q: The exec_queue + * + * Add exec queue to VM, skipped if the device does not have context based TLB + * invalidations. + */ +void xe_vm_add_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) +{ + struct xe_device *xe = vm->xe; + + /* User VMs and queues only */ + xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_KERNEL)); + xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_PERMANENT)); + xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_VM)); + xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_MIGRATE)); + xe_assert(xe, vm->xef); + xe_assert(xe, vm == q->vm); + + if (!xe->info.has_ctx_tlb_inval) + return; + + down_write(&vm->exec_queues.lock); + list_add(&q->vm_exec_queue_link, &vm->exec_queues.list[q->gt->info.id]); + ++vm->exec_queues.count[q->gt->info.id]; + up_write(&vm->exec_queues.lock); +} + +/** + * xe_vm_remove_exec_queue() - Remove exec queue from VM + * @vm: The VM. + * @q: The exec_queue + * + * Remove exec queue from VM, skipped if the device does not have context based + * TLB invalidations. + */ +void xe_vm_remove_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) +{ + if (!vm->xe->info.has_ctx_tlb_inval) + return; + + down_write(&vm->exec_queues.lock); + if (!list_empty(&q->vm_exec_queue_link)) { + list_del(&q->vm_exec_queue_link); + --vm->exec_queues.count[q->gt->info.id]; + } + up_write(&vm->exec_queues.lock); +} diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index 6cc98df47291..288115c7844a 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -287,6 +287,9 @@ static inline struct dma_resv *xe_vm_resv(struct xe_vm *vm) void xe_vm_kill(struct xe_vm *vm, bool unlocked); +void xe_vm_add_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q); +void xe_vm_remove_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q); + /** * xe_vm_assert_held(vm) - Assert that the vm's reservation object is held. * @vm: The vm diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h index 437f64202f3b..43203e90ee3e 100644 --- a/drivers/gpu/drm/xe/xe_vm_types.h +++ b/drivers/gpu/drm/xe/xe_vm_types.h @@ -298,6 +298,22 @@ struct xe_vm { struct list_head pm_activate_link; } preempt; + /** @exec_queues: Manages list of exec queues attached to this VM, protected by lock. */ + struct { + /** + * @exec_queues.list: list of exec queues attached to this VM, + * per GT + */ + struct list_head list[XE_MAX_TILES_PER_DEVICE * XE_MAX_GT_PER_TILE]; + /** + * @exec_queues.count: count of exec queues attached to this VM, + * per GT + */ + int count[XE_MAX_TILES_PER_DEVICE * XE_MAX_GT_PER_TILE]; + /** @exec_queues.lock: lock to protect exec_queues list */ + struct rw_semaphore lock; + } exec_queues; + /** @um: unified memory state */ struct { /** @asid: address space ID, unique to each VM */ From 8d7a9f801ed72c6a2506bb26f2eec1f8245bface Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:26 -0800 Subject: [PATCH 0026/1735] drm/xe: Taint TLB invalidation seqno lock with GFP_KERNEL Taint TLB invalidation seqno lock with GFP_KERNEL as TLB invalidations can be in the path of reclaim (e.g., MMU notifiers). Signed-off-by: Matthew Brost Reviewed-by: Matt Atwood Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-7-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_tlb_inval.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.c b/drivers/gpu/drm/xe/xe_tlb_inval.c index e837888367c4..21fef337f29c 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_tlb_inval.c @@ -111,6 +111,16 @@ static void tlb_inval_fini(struct drm_device *drm, void *arg) xe_tlb_inval_reset(tlb_inval); } +static void primelockdep(struct xe_tlb_inval *tlb_inval) +{ + if (!IS_ENABLED(CONFIG_LOCKDEP)) + return; + + fs_reclaim_acquire(GFP_KERNEL); + might_lock(&tlb_inval->seqno_lock); + fs_reclaim_release(GFP_KERNEL); +} + /** * xe_gt_tlb_inval_init - Initialize TLB invalidation state * @gt: GT structure @@ -137,6 +147,8 @@ int xe_gt_tlb_inval_init_early(struct xe_gt *gt) if (err) return err; + primelockdep(tlb_inval); + tlb_inval->job_wq = drmm_alloc_ordered_workqueue(&xe->drm, "gt-tbl-inval-job-wq", WQ_MEM_RECLAIM); From edcc15f489c4c30667b78418228d6a84dbf6a464 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:27 -0800 Subject: [PATCH 0027/1735] drm/xe: Rename send_tlb_inval_ppgtt to send_tlb_inval_asid_ppgtt Context-based TLB invalidations have their own set of GuC TLB invalidation operations. Rename the current PPGTT invalidation function, which operates on ASIDs, to a more descriptive name that reflects its purpose. Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-8-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_guc_tlb_inval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index 43f6dbcb2bc2..a6a1c371a28e 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -150,9 +150,9 @@ static u64 normalize_invalidation_range(struct xe_gt *gt, u64 *start, u64 *end) */ #define MAX_RANGE_TLB_INVALIDATION_LENGTH (rounddown_pow_of_two(ULONG_MAX)) -static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, - u64 start, u64 end, u32 asid, - struct drm_suballoc *prl_sa) +static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, + u64 start, u64 end, u32 asid, + struct drm_suballoc *prl_sa) { #define MAX_TLB_INVALIDATION_LEN 7 struct xe_guc *guc = tlb_inval->private; @@ -219,7 +219,7 @@ static long tlb_inval_timeout_delay(struct xe_tlb_inval *tlb_inval) static const struct xe_tlb_inval_ops guc_tlb_inval_ops = { .all = send_tlb_inval_all, .ggtt = send_tlb_inval_ggtt, - .ppgtt = send_tlb_inval_ppgtt, + .ppgtt = send_tlb_inval_asid_ppgtt, .initialized = tlb_inval_initialized, .flush = tlb_inval_flush, .timeout_delay = tlb_inval_timeout_delay, From 2d93d5d53024257e686b3aa839e148cde776e35e Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:28 -0800 Subject: [PATCH 0028/1735] drm/xe: Add send_tlb_inval_ppgtt helper Extract the common code that issues a TLB invalidation H2G for PPGTTs into a helper function. This helper can be reused for both ASID-based and context-based TLB invalidations. Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-9-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_guc_tlb_inval.c | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index a6a1c371a28e..070d2e2cb7c9 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -150,20 +150,16 @@ static u64 normalize_invalidation_range(struct xe_gt *gt, u64 *start, u64 *end) */ #define MAX_RANGE_TLB_INVALIDATION_LENGTH (rounddown_pow_of_two(ULONG_MAX)) -static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, - u64 start, u64 end, u32 asid, - struct drm_suballoc *prl_sa) +static int send_tlb_inval_ppgtt(struct xe_guc *guc, u32 seqno, u64 start, + u64 end, u32 id, u32 type, + struct drm_suballoc *prl_sa) { #define MAX_TLB_INVALIDATION_LEN 7 - struct xe_guc *guc = tlb_inval->private; struct xe_gt *gt = guc_to_gt(guc); u32 action[MAX_TLB_INVALIDATION_LEN]; u64 length = end - start; int len = 0, err; - if (guc_to_xe(guc)->info.force_execlist) - return -ECANCELED; - action[len++] = XE_GUC_ACTION_TLB_INVALIDATION; action[len++] = !prl_sa ? seqno : TLB_INVALIDATION_SEQNO_INVALID; if (!gt_to_xe(gt)->info.has_range_tlb_inval || @@ -174,14 +170,15 @@ static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, &end); /* Flush on NULL case, Media is not required to modify flush due to no PPC so NOP */ - action[len++] = MAKE_INVAL_OP_FLUSH(XE_GUC_TLB_INVAL_PAGE_SELECTIVE, !prl_sa); - action[len++] = asid; + action[len++] = MAKE_INVAL_OP_FLUSH(type, !prl_sa); + action[len++] = id; action[len++] = lower_32_bits(start); action[len++] = upper_32_bits(start); action[len++] = ilog2(normalize_len) - ilog2(SZ_4K); } xe_gt_assert(gt, len <= MAX_TLB_INVALIDATION_LEN); +#undef MAX_TLB_INVALIDATION_LEN err = send_tlb_inval(guc, action, len); if (!err && prl_sa) @@ -189,6 +186,21 @@ static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, return err; } +static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, + u64 start, u64 end, u32 asid, + struct drm_suballoc *prl_sa) +{ + struct xe_guc *guc = tlb_inval->private; + + lockdep_assert_held(&tlb_inval->seqno_lock); + + if (guc_to_xe(guc)->info.force_execlist) + return -ECANCELED; + + return send_tlb_inval_ppgtt(guc, seqno, start, end, asid, + XE_GUC_TLB_INVAL_PAGE_SELECTIVE, prl_sa); +} + static bool tlb_inval_initialized(struct xe_tlb_inval *tlb_inval) { struct xe_guc *guc = tlb_inval->private; From 6b42b635d6a20fd418ecc7c4c3ad52ef99fe7227 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:29 -0800 Subject: [PATCH 0029/1735] drm/xe: Add xe_tlb_inval_idle helper Introduce the xe_tlb_inval_idle helper to detect whether any TLB invalidations are currently in flight. This is used in context-based TLB invalidations to determine whether dummy TLB invalidations need to be sent to maintain proper TLB invalidation fence ordering.. v2: - Implement xe_tlb_inval_idle based on pending list Signed-off-by: Matthew Brost Reviewed-by: Stuart Summers Tested-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-10-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_tlb_inval.c | 21 +++++++++++++++++++++ drivers/gpu/drm/xe/xe_tlb_inval.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.c b/drivers/gpu/drm/xe/xe_tlb_inval.c index 21fef337f29c..989fe0e7f8ee 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_tlb_inval.c @@ -41,11 +41,14 @@ static void xe_tlb_inval_fence_fini(struct xe_tlb_inval_fence *fence) static void xe_tlb_inval_fence_signal(struct xe_tlb_inval_fence *fence) { + struct xe_tlb_inval *tlb_inval = fence->tlb_inval; bool stack = test_bit(FENCE_STACK_BIT, &fence->base.flags); lockdep_assert_held(&fence->tlb_inval->pending_lock); list_del(&fence->link); + if (list_empty(&tlb_inval->pending_fences)) + cancel_delayed_work(&tlb_inval->fence_tdr); trace_xe_tlb_inval_fence_signal(fence->tlb_inval->xe, fence); xe_tlb_inval_fence_fini(fence); dma_fence_signal(&fence->base); @@ -465,3 +468,21 @@ void xe_tlb_inval_fence_init(struct xe_tlb_inval *tlb_inval, dma_fence_get(&fence->base); fence->tlb_inval = tlb_inval; } + +/** + * xe_tlb_inval_idle() - Initialize TLB invalidation is idle + * @tlb_inval: TLB invalidation client + * + * Check the TLB invalidation seqno to determine if it is idle (i.e., no TLB + * invalidations are in flight). Expected to be called in the backend after the + * fence has been added to the pending list, and takes this into account. + * + * Return: True if TLB invalidation client is idle, False otherwise + */ +bool xe_tlb_inval_idle(struct xe_tlb_inval *tlb_inval) +{ + lockdep_assert_held(&tlb_inval->seqno_lock); + + guard(spinlock_irq)(&tlb_inval->pending_lock); + return list_is_singular(&tlb_inval->pending_fences); +} diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.h b/drivers/gpu/drm/xe/xe_tlb_inval.h index 858d0690f995..62089254fa23 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval.h +++ b/drivers/gpu/drm/xe/xe_tlb_inval.h @@ -43,4 +43,6 @@ xe_tlb_inval_fence_wait(struct xe_tlb_inval_fence *fence) void xe_tlb_inval_done_handler(struct xe_tlb_inval *tlb_inval, int seqno); +bool xe_tlb_inval_idle(struct xe_tlb_inval *tlb_inval); + #endif /* _XE_TLB_INVAL_ */ From 628d59392cc571930f52e121892c7a72f7c1d65b Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:30 -0800 Subject: [PATCH 0030/1735] drm/xe: Add exec queue active vfunc If an exec queue is inactive (e.g., not registered or scheduling is disabled), TLB invalidations are not issued for that queue. Add a virtual function to determine the active state, which TLB invalidation logic can hook into. v5: - Operate on primary in active function Signed-off-by: Matthew Brost Tested-by: Stuart Summers Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-11-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_exec_queue_types.h | 2 ++ drivers/gpu/drm/xe/xe_execlist.c | 7 +++++++ drivers/gpu/drm/xe/xe_guc_submit.c | 9 +++++++++ 3 files changed, 18 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index e30d295aaaae..601e742c79ff 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -300,6 +300,8 @@ struct xe_exec_queue_ops { void (*resume)(struct xe_exec_queue *q); /** @reset_status: check exec queue reset status */ bool (*reset_status)(struct xe_exec_queue *q); + /** @active: check exec queue is active */ + bool (*active)(struct xe_exec_queue *q); }; #endif diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c index 8bf330aeaec0..005a5b2c36fe 100644 --- a/drivers/gpu/drm/xe/xe_execlist.c +++ b/drivers/gpu/drm/xe/xe_execlist.c @@ -468,6 +468,12 @@ static bool execlist_exec_queue_reset_status(struct xe_exec_queue *q) return false; } +static bool execlist_exec_queue_active(struct xe_exec_queue *q) +{ + /* NIY */ + return false; +} + static const struct xe_exec_queue_ops execlist_exec_queue_ops = { .init = execlist_exec_queue_init, .kill = execlist_exec_queue_kill, @@ -480,6 +486,7 @@ static const struct xe_exec_queue_ops execlist_exec_queue_ops = { .suspend_wait = execlist_exec_queue_suspend_wait, .resume = execlist_exec_queue_resume, .reset_status = execlist_exec_queue_reset_status, + .active = execlist_exec_queue_active, }; int xe_execlist_init(struct xe_gt *gt) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index dee0f9004024..456f549c16f6 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -2276,6 +2276,14 @@ static bool guc_exec_queue_reset_status(struct xe_exec_queue *q) return exec_queue_reset(q) || exec_queue_killed_or_banned_or_wedged(q); } +static bool guc_exec_queue_active(struct xe_exec_queue *q) +{ + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + + return exec_queue_enabled(primary) && + !exec_queue_pending_disable(primary); +} + /* * All of these functions are an abstraction layer which other parts of Xe can * use to trap into the GuC backend. All of these functions, aside from init, @@ -2295,6 +2303,7 @@ static const struct xe_exec_queue_ops guc_exec_queue_ops = { .suspend_wait = guc_exec_queue_suspend_wait, .resume = guc_exec_queue_resume, .reset_status = guc_exec_queue_reset_status, + .active = guc_exec_queue_active, }; static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q) From 6cdaa5346d6f3f6116e607e49c92c2401390c267 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 16 Jan 2026 14:17:31 -0800 Subject: [PATCH 0031/1735] drm/xe: Add context-based invalidation to GuC TLB invalidation backend Introduce context-based invalidation support to the GuC TLB invalidation backend. This is implemented by iterating over each exec queue per GT within a VM, skipping inactive queues, and issuing a context-based (GuC ID) H2G TLB invalidation. All H2G messages, except the final one, are sent with an invalid seqno, which the G2H handler drops to ensure the TLB invalidation fence is only signaled once all H2G messages are completed. A watermark mechanism is also added to switch between context-based TLB invalidations and full device-wide invalidations, as the return on investment for context-based invalidation diminishes when many exec queues are mapped. v2: - Fix checkpatch warnings v3: - Rebase on PRL - Use ref counting to avoid racing with deregisters v4: - Extra braces (Stuart) - Use per GT list (CI) - Reorder put Signed-off-by: Matthew Brost Tested-by: Stuart Summers Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260116221731.868657-12-matthew.brost@intel.com --- drivers/gpu/drm/xe/xe_guc_tlb_inval.c | 145 +++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index 070d2e2cb7c9..ced58f46f846 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -6,15 +6,19 @@ #include "abi/guc_actions_abi.h" #include "xe_device.h" +#include "xe_exec_queue.h" +#include "xe_exec_queue_types.h" #include "xe_gt_stats.h" #include "xe_gt_types.h" #include "xe_guc.h" #include "xe_guc_ct.h" +#include "xe_guc_exec_queue_types.h" #include "xe_guc_tlb_inval.h" #include "xe_force_wake.h" #include "xe_mmio.h" #include "xe_sa.h" #include "xe_tlb_inval.h" +#include "xe_vm.h" #include "regs/xe_guc_regs.h" @@ -156,10 +160,16 @@ static int send_tlb_inval_ppgtt(struct xe_guc *guc, u32 seqno, u64 start, { #define MAX_TLB_INVALIDATION_LEN 7 struct xe_gt *gt = guc_to_gt(guc); + struct xe_device *xe = guc_to_xe(guc); u32 action[MAX_TLB_INVALIDATION_LEN]; u64 length = end - start; int len = 0, err; + xe_gt_assert(gt, (type == XE_GUC_TLB_INVAL_PAGE_SELECTIVE && + !xe->info.has_ctx_tlb_inval) || + (type == XE_GUC_TLB_INVAL_PAGE_SELECTIVE_CTX && + xe->info.has_ctx_tlb_inval)); + action[len++] = XE_GUC_ACTION_TLB_INVALIDATION; action[len++] = !prl_sa ? seqno : TLB_INVALIDATION_SEQNO_INVALID; if (!gt_to_xe(gt)->info.has_range_tlb_inval || @@ -168,9 +178,11 @@ static int send_tlb_inval_ppgtt(struct xe_guc *guc, u32 seqno, u64 start, } else { u64 normalize_len = normalize_invalidation_range(gt, &start, &end); + bool need_flush = !prl_sa && + seqno != TLB_INVALIDATION_SEQNO_INVALID; /* Flush on NULL case, Media is not required to modify flush due to no PPC so NOP */ - action[len++] = MAKE_INVAL_OP_FLUSH(type, !prl_sa); + action[len++] = MAKE_INVAL_OP_FLUSH(type, need_flush); action[len++] = id; action[len++] = lower_32_bits(start); action[len++] = upper_32_bits(start); @@ -181,8 +193,10 @@ static int send_tlb_inval_ppgtt(struct xe_guc *guc, u32 seqno, u64 start, #undef MAX_TLB_INVALIDATION_LEN err = send_tlb_inval(guc, action, len); - if (!err && prl_sa) + if (!err && prl_sa) { + xe_gt_assert(gt, seqno != TLB_INVALIDATION_SEQNO_INVALID); err = send_page_reclaim(guc, seqno, xe_sa_bo_gpu_addr(prl_sa)); + } return err; } @@ -201,6 +215,114 @@ static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, XE_GUC_TLB_INVAL_PAGE_SELECTIVE, prl_sa); } +static int send_tlb_inval_ctx_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, + u64 start, u64 end, u32 asid, + struct drm_suballoc *prl_sa) +{ + struct xe_guc *guc = tlb_inval->private; + struct xe_device *xe = guc_to_xe(guc); + struct xe_exec_queue *q, *next, *last_q = NULL; + struct xe_vm *vm; + LIST_HEAD(tlb_inval_list); + int err = 0, id = guc_to_gt(guc)->info.id; + + lockdep_assert_held(&tlb_inval->seqno_lock); + + if (xe->info.force_execlist) + return -ECANCELED; + + vm = xe_device_asid_to_vm(xe, asid); + if (IS_ERR(vm)) + return PTR_ERR(vm); + + down_read(&vm->exec_queues.lock); + + /* + * XXX: Randomly picking a threshold for now. This will need to be + * tuned based on expected UMD queue counts and performance profiling. + */ +#define EXEC_QUEUE_COUNT_FULL_THRESHOLD 8 + if (vm->exec_queues.count[id] >= EXEC_QUEUE_COUNT_FULL_THRESHOLD) { + u32 action[] = { + XE_GUC_ACTION_TLB_INVALIDATION, + seqno, + MAKE_INVAL_OP(XE_GUC_TLB_INVAL_FULL), + }; + + err = send_tlb_inval(guc, action, ARRAY_SIZE(action)); + goto err_unlock; + } +#undef EXEC_QUEUE_COUNT_FULL_THRESHOLD + + /* + * Move exec queues to a temporary list to issue invalidations. The exec + * queue must active and a reference must be taken to prevent concurrent + * deregistrations. + * + * List modification is safe because we hold 'vm->exec_queues.lock' for + * reading, which prevents external modifications. Using a per-GT list + * is also safe since 'tlb_inval->seqno_lock' ensures no other GT users + * can enter this code path. + */ + list_for_each_entry_safe(q, next, &vm->exec_queues.list[id], + vm_exec_queue_link) { + if (q->ops->active(q) && xe_exec_queue_get_unless_zero(q)) { + last_q = q; + list_move_tail(&q->vm_exec_queue_link, &tlb_inval_list); + } + } + + if (!last_q) { + /* + * We can't break fence ordering for TLB invalidation jobs, if + * TLB invalidations are inflight issue a dummy invalidation to + * maintain ordering. Nor can we move safely the seqno_recv when + * returning -ECANCELED if TLB invalidations are in flight. Use + * GGTT invalidation as dummy invalidation given ASID + * invalidations are unsupported here. + */ + if (xe_tlb_inval_idle(tlb_inval)) + err = -ECANCELED; + else + err = send_tlb_inval_ggtt(tlb_inval, seqno); + goto err_unlock; + } + + list_for_each_entry_safe(q, next, &tlb_inval_list, vm_exec_queue_link) { + struct drm_suballoc *__prl_sa = NULL; + int __seqno = TLB_INVALIDATION_SEQNO_INVALID; + u32 type = XE_GUC_TLB_INVAL_PAGE_SELECTIVE_CTX; + + xe_assert(xe, q->vm == vm); + + if (err) + goto unref; + + if (last_q == q) { + __prl_sa = prl_sa; + __seqno = seqno; + } + + err = send_tlb_inval_ppgtt(guc, __seqno, start, end, + q->guc->id, type, __prl_sa); + +unref: + /* + * Must always return exec queue to original list / drop + * reference + */ + list_move_tail(&q->vm_exec_queue_link, + &vm->exec_queues.list[id]); + xe_exec_queue_put(q); + } + +err_unlock: + up_read(&vm->exec_queues.lock); + xe_vm_put(vm); + + return err; +} + static bool tlb_inval_initialized(struct xe_tlb_inval *tlb_inval) { struct xe_guc *guc = tlb_inval->private; @@ -228,7 +350,7 @@ static long tlb_inval_timeout_delay(struct xe_tlb_inval *tlb_inval) return hw_tlb_timeout + 2 * delay; } -static const struct xe_tlb_inval_ops guc_tlb_inval_ops = { +static const struct xe_tlb_inval_ops guc_tlb_inval_asid_ops = { .all = send_tlb_inval_all, .ggtt = send_tlb_inval_ggtt, .ppgtt = send_tlb_inval_asid_ppgtt, @@ -237,6 +359,15 @@ static const struct xe_tlb_inval_ops guc_tlb_inval_ops = { .timeout_delay = tlb_inval_timeout_delay, }; +static const struct xe_tlb_inval_ops guc_tlb_inval_ctx_ops = { + .ggtt = send_tlb_inval_ggtt, + .all = send_tlb_inval_all, + .ppgtt = send_tlb_inval_ctx_ppgtt, + .initialized = tlb_inval_initialized, + .flush = tlb_inval_flush, + .timeout_delay = tlb_inval_timeout_delay, +}; + /** * xe_guc_tlb_inval_init_early() - Init GuC TLB invalidation early * @guc: GuC object @@ -248,8 +379,14 @@ static const struct xe_tlb_inval_ops guc_tlb_inval_ops = { void xe_guc_tlb_inval_init_early(struct xe_guc *guc, struct xe_tlb_inval *tlb_inval) { + struct xe_device *xe = guc_to_xe(guc); + tlb_inval->private = guc; - tlb_inval->ops = &guc_tlb_inval_ops; + + if (xe->info.has_ctx_tlb_inval) + tlb_inval->ops = &guc_tlb_inval_ctx_ops; + else + tlb_inval->ops = &guc_tlb_inval_asid_ops; } /** From 1b85a9b04681423013bf6caeffde73aa6f29ab65 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Thu, 8 Jan 2026 18:11:40 +0530 Subject: [PATCH 0032/1735] drm/i915/vbt: Add edp pipe joiner enable/disable bits Add VBT support to enable/disable eDP Pipe Joiner feature. The OEMs can choose to enable/disable the feature from VBT. ARL - VBTs default this field to disabled. PTL+ - VBTs default this field to enabled. Bspec:20142 Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260108124141.1407760-2-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_bios.c | 4 ++++ drivers/gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_vbt_defs.h | 1 + 3 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 2afc99a39429..eb745a6cbc5e 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -1547,6 +1547,10 @@ parse_edp(struct intel_display *display, if (display->vbt.version >= 251) panel->vbt.edp.dsc_disable = panel_bool(edp->edp_dsc_disable, panel_type); + + if (display->vbt.version >= 261) + panel->vbt.edp.pipe_joiner_enable = + panel_bool(edp->pipe_joiner_enable, panel_type); } static void diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index c7a8e475cb22..08692e06d8e9 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -350,6 +350,7 @@ struct intel_vbt_panel_data { bool low_vswing; bool hobl; bool dsc_disable; + bool pipe_joiner_enable; } edp; struct { diff --git a/drivers/gpu/drm/i915/display/intel_vbt_defs.h b/drivers/gpu/drm/i915/display/intel_vbt_defs.h index 57fda5824c9c..0dc13d080e8a 100644 --- a/drivers/gpu/drm/i915/display/intel_vbt_defs.h +++ b/drivers/gpu/drm/i915/display/intel_vbt_defs.h @@ -1109,6 +1109,7 @@ struct bdb_edp { u16 edp_dsc_disable; /* 251+ */ u16 t6_delay_support; /* 260+ */ u16 link_idle_time[16]; /* 260+ */ + u16 pipe_joiner_enable; /* 261+ */ } __packed; /* From 650471948e495204f1f89731c71bcf48b3f81b08 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Thu, 8 Jan 2026 18:11:41 +0530 Subject: [PATCH 0033/1735] drm/i915/dp: Avoid joiner for eDP if not enabled in VBT For eDP, enable the Pipe Joiner feature only if VBT explicitly allows it. If VBT disables the feature, skip joiner for eDP, even if the hardware supports it. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/14616 Signed-off-by: Ankit Nautiyal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260108124141.1407760-3-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 053443eea9d5..79fd3b8d8b25 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -494,11 +494,16 @@ bool intel_dp_has_joiner(struct intel_dp *intel_dp) struct intel_display *display = to_intel_display(intel_dp); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *encoder = &intel_dig_port->base; + struct intel_connector *connector = intel_dp->attached_connector; /* eDP MSO is not compatible with joiner */ if (intel_dp->mso_link_count) return false; + if (intel_dp_is_edp(intel_dp) && + !connector->panel.vbt.edp.pipe_joiner_enable) + return false; + return DISPLAY_VER(display) >= 12 || (DISPLAY_VER(display) == 11 && encoder->port != PORT_A); From 33fd0375f1c31e20db08535aa9ac36df8156af8a Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Thu, 15 Jan 2026 17:09:48 +0530 Subject: [PATCH 0034/1735] drm/i915/casf: Disable CASF with joiner Disable CASF with joiner as it is not supported in hardware. v2: Replace dmesg_WARN with drm_dbg_kms. [Jani] v3: Modify commit message. [Suraj] Signed-off-by: Nemesa Garg Reviewed-by: Suraj Kandpal Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260115113948.641822-1-nemesa.garg@intel.com --- drivers/gpu/drm/i915/display/intel_casf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_casf.c b/drivers/gpu/drm/i915/display/intel_casf.c index 95339b496f24..0fe4398a1a4e 100644 --- a/drivers/gpu/drm/i915/display/intel_casf.c +++ b/drivers/gpu/drm/i915/display/intel_casf.c @@ -116,6 +116,12 @@ int intel_casf_compute_config(struct intel_crtc_state *crtc_state) return 0; } + /* CASF with joiner not supported in hardware */ + if (crtc_state->joiner_pipes) { + drm_dbg_kms(display->drm, "CASF not supported with joiner\n"); + return -EINVAL; + } + crtc_state->hw.casf_params.casf_enable = true; /* From bc5ecacbdc4d315f656988fefb48ecffecdd3c76 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:42 +0000 Subject: [PATCH 0035/1735] drm/i915/cx0: Move C10 port clock calculation Prepare removal of .clock member from pll state structures by moving intel_c10pll_calc_port_clock() function. No functional changes. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-2-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 54 ++++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 7288065d2461..5cd756321373 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2103,6 +2103,33 @@ static bool cx0pll_state_is_dp(const struct intel_cx0pll_state *pll_state) return c20pll_state_is_dp(&pll_state->c20); } +static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c10pll_state *pll_state) +{ + unsigned int frac_quot = 0, frac_rem = 0, frac_den = 1; + unsigned int multiplier, tx_clk_div, hdmi_div, refclk = 38400; + int tmpclk = 0; + + if (pll_state->pll[0] & C10_PLL0_FRACEN) { + frac_quot = pll_state->pll[12] << 8 | pll_state->pll[11]; + frac_rem = pll_state->pll[14] << 8 | pll_state->pll[13]; + frac_den = pll_state->pll[10] << 8 | pll_state->pll[9]; + } + + multiplier = (REG_FIELD_GET8(C10_PLL3_MULTIPLIERH_MASK, pll_state->pll[3]) << 8 | + pll_state->pll[2]) / 2 + 16; + + tx_clk_div = REG_FIELD_GET8(C10_PLL15_TXCLKDIV_MASK, pll_state->pll[15]); + hdmi_div = REG_FIELD_GET8(C10_PLL15_HDMIDIV_MASK, pll_state->pll[15]); + + tmpclk = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, (multiplier << 16) + frac_quot) + + DIV_ROUND_CLOSEST(refclk * frac_rem, frac_den), + 10 << (tx_clk_div + 16)); + tmpclk *= (hdmi_div ? 2 : 1); + + return tmpclk; +} + /* * TODO: Convert the following to align with intel_c20pll_find_table() and * intel_c20pll_calc_state_from_table(). @@ -2166,33 +2193,6 @@ static int intel_c10pll_calc_state(const struct intel_crtc_state *crtc_state, return 0; } -static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c10pll_state *pll_state) -{ - unsigned int frac_quot = 0, frac_rem = 0, frac_den = 1; - unsigned int multiplier, tx_clk_div, hdmi_div, refclk = 38400; - int tmpclk = 0; - - if (pll_state->pll[0] & C10_PLL0_FRACEN) { - frac_quot = pll_state->pll[12] << 8 | pll_state->pll[11]; - frac_rem = pll_state->pll[14] << 8 | pll_state->pll[13]; - frac_den = pll_state->pll[10] << 8 | pll_state->pll[9]; - } - - multiplier = (REG_FIELD_GET8(C10_PLL3_MULTIPLIERH_MASK, pll_state->pll[3]) << 8 | - pll_state->pll[2]) / 2 + 16; - - tx_clk_div = REG_FIELD_GET8(C10_PLL15_TXCLKDIV_MASK, pll_state->pll[15]); - hdmi_div = REG_FIELD_GET8(C10_PLL15_HDMIDIV_MASK, pll_state->pll[15]); - - tmpclk = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, (multiplier << 16) + frac_quot) + - DIV_ROUND_CLOSEST(refclk * frac_rem, frac_den), - 10 << (tx_clk_div + 16)); - tmpclk *= (hdmi_div ? 2 : 1); - - return tmpclk; -} - static int readout_enabled_lane_count(struct intel_encoder *encoder) { struct intel_display *display = to_intel_display(encoder); From c26ed2ec4d10d1fee7ef8f9987663765ee737487 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:43 +0000 Subject: [PATCH 0036/1735] drm/i915/cx0: Move C20 port clock calculation Prepare removal of .clock member from the pll state structure by moving intel_c20pll_calc_port_clock() function. No functional change. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-3-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 100 +++++++++---------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 5cd756321373..ac5e304ba306 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2130,6 +2130,56 @@ static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, return tmpclk; } +static bool intel_c20phy_use_mpllb(const struct intel_c20pll_state *state) +{ + return state->tx[0] & C20_PHY_USE_MPLLB; +} + +static int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c20pll_state *pll_state) +{ + unsigned int frac, frac_en, frac_quot, frac_rem, frac_den; + unsigned int multiplier, refclk = 38400; + unsigned int tx_clk_div; + unsigned int ref_clk_mpllb_div; + unsigned int fb_clk_div4_en; + unsigned int ref, vco; + unsigned int tx_rate_mult; + unsigned int tx_rate = REG_FIELD_GET(C20_PHY_TX_RATE, pll_state->tx[0]); + + if (intel_c20phy_use_mpllb(pll_state)) { + tx_rate_mult = 1; + frac_en = REG_FIELD_GET(C20_MPLLB_FRACEN, pll_state->mpllb[6]); + frac_quot = pll_state->mpllb[8]; + frac_rem = pll_state->mpllb[9]; + frac_den = pll_state->mpllb[7]; + multiplier = REG_FIELD_GET(C20_MULTIPLIER_MASK, pll_state->mpllb[0]); + tx_clk_div = REG_FIELD_GET(C20_MPLLB_TX_CLK_DIV_MASK, pll_state->mpllb[0]); + ref_clk_mpllb_div = REG_FIELD_GET(C20_REF_CLK_MPLLB_DIV_MASK, pll_state->mpllb[6]); + fb_clk_div4_en = 0; + } else { + tx_rate_mult = 2; + frac_en = REG_FIELD_GET(C20_MPLLA_FRACEN, pll_state->mplla[6]); + frac_quot = pll_state->mplla[8]; + frac_rem = pll_state->mplla[9]; + frac_den = pll_state->mplla[7]; + multiplier = REG_FIELD_GET(C20_MULTIPLIER_MASK, pll_state->mplla[0]); + tx_clk_div = REG_FIELD_GET(C20_MPLLA_TX_CLK_DIV_MASK, pll_state->mplla[1]); + ref_clk_mpllb_div = REG_FIELD_GET(C20_REF_CLK_MPLLB_DIV_MASK, pll_state->mplla[6]); + fb_clk_div4_en = REG_FIELD_GET(C20_FB_CLK_DIV4_EN, pll_state->mplla[0]); + } + + if (frac_en) + frac = frac_quot + DIV_ROUND_CLOSEST(frac_rem, frac_den); + else + frac = 0; + + ref = DIV_ROUND_CLOSEST(refclk * (1 << (1 + fb_clk_div4_en)), 1 << ref_clk_mpllb_div); + vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(ref, (multiplier << (17 - 2)) + frac) >> 17, 10); + + return vco << tx_rate_mult >> tx_clk_div >> tx_rate; +} + /* * TODO: Convert the following to align with intel_c20pll_find_table() and * intel_c20pll_calc_state_from_table(). @@ -2705,56 +2755,6 @@ int intel_cx0pll_calc_state(const struct intel_crtc_state *crtc_state, return intel_c20pll_calc_state(crtc_state, encoder, hw_state); } -static bool intel_c20phy_use_mpllb(const struct intel_c20pll_state *state) -{ - return state->tx[0] & C20_PHY_USE_MPLLB; -} - -static int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c20pll_state *pll_state) -{ - unsigned int frac, frac_en, frac_quot, frac_rem, frac_den; - unsigned int multiplier, refclk = 38400; - unsigned int tx_clk_div; - unsigned int ref_clk_mpllb_div; - unsigned int fb_clk_div4_en; - unsigned int ref, vco; - unsigned int tx_rate_mult; - unsigned int tx_rate = REG_FIELD_GET(C20_PHY_TX_RATE, pll_state->tx[0]); - - if (intel_c20phy_use_mpllb(pll_state)) { - tx_rate_mult = 1; - frac_en = REG_FIELD_GET(C20_MPLLB_FRACEN, pll_state->mpllb[6]); - frac_quot = pll_state->mpllb[8]; - frac_rem = pll_state->mpllb[9]; - frac_den = pll_state->mpllb[7]; - multiplier = REG_FIELD_GET(C20_MULTIPLIER_MASK, pll_state->mpllb[0]); - tx_clk_div = REG_FIELD_GET(C20_MPLLB_TX_CLK_DIV_MASK, pll_state->mpllb[0]); - ref_clk_mpllb_div = REG_FIELD_GET(C20_REF_CLK_MPLLB_DIV_MASK, pll_state->mpllb[6]); - fb_clk_div4_en = 0; - } else { - tx_rate_mult = 2; - frac_en = REG_FIELD_GET(C20_MPLLA_FRACEN, pll_state->mplla[6]); - frac_quot = pll_state->mplla[8]; - frac_rem = pll_state->mplla[9]; - frac_den = pll_state->mplla[7]; - multiplier = REG_FIELD_GET(C20_MULTIPLIER_MASK, pll_state->mplla[0]); - tx_clk_div = REG_FIELD_GET(C20_MPLLA_TX_CLK_DIV_MASK, pll_state->mplla[1]); - ref_clk_mpllb_div = REG_FIELD_GET(C20_REF_CLK_MPLLB_DIV_MASK, pll_state->mplla[6]); - fb_clk_div4_en = REG_FIELD_GET(C20_FB_CLK_DIV4_EN, pll_state->mplla[0]); - } - - if (frac_en) - frac = frac_quot + DIV_ROUND_CLOSEST(frac_rem, frac_den); - else - frac = 0; - - ref = DIV_ROUND_CLOSEST(refclk * (1 << (1 + fb_clk_div4_en)), 1 << ref_clk_mpllb_div); - vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(ref, (multiplier << (17 - 2)) + frac) >> 17, 10); - - return vco << tx_rate_mult >> tx_clk_div >> tx_rate; -} - static void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, struct intel_cx0pll_state *cx0pll_state) { From 2db8d9c26760f1c64137c4ebca76b61fc0e14548 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:44 +0000 Subject: [PATCH 0037/1735] drm/i915/cx0: Drop Cx0 crtc_state from HDMI TMDS pll divider calculation Drop crtc_state from HDMI TMDS calculation and replace with the parameters that are only required. Follow-up changes will call these functions without a crtc_state available. v2: Keep required crtc_state param for intel_c20_pll_tables_get() and other functions calling this one. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-4-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index ac5e304ba306..6447d7c80ffc 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2414,9 +2414,8 @@ static bool is_arrowlake_s_by_host_bridge(void) return pdev && IS_ARROWLAKE_S_BY_HOST_BRIDGE_ID(host_bridge_pci_dev_id); } -static u16 intel_c20_hdmi_tmds_tx_cgf_1(const struct intel_crtc_state *crtc_state) +static u16 intel_c20_hdmi_tmds_tx_cgf_1(struct intel_display *display) { - struct intel_display *display = to_intel_display(crtc_state); u16 tx_misc; u16 tx_dcc_cal_dac_ctrl_range = 8; u16 tx_term_ctrl = 2; @@ -2438,7 +2437,8 @@ static u16 intel_c20_hdmi_tmds_tx_cgf_1(const struct intel_crtc_state *crtc_stat C20_PHY_TX_DCC_BYPASS | C20_PHY_TX_TERM_CTL(tx_term_ctrl)); } -static int intel_c20_compute_hdmi_tmds_pll(const struct intel_crtc_state *crtc_state, +static int intel_c20_compute_hdmi_tmds_pll(struct intel_display *display, + int port_clock, struct intel_c20pll_state *pll_state) { u64 datarate; @@ -2452,10 +2452,10 @@ static int intel_c20_compute_hdmi_tmds_pll(const struct intel_crtc_state *crtc_s u8 mpllb_ana_freq_vco; u8 mpll_div_multiplier; - if (crtc_state->port_clock < 25175 || crtc_state->port_clock > 600000) + if (port_clock < 25175 || port_clock > 600000) return -EINVAL; - datarate = ((u64)crtc_state->port_clock * 1000) * 10; + datarate = ((u64)port_clock * 1000) * 10; mpll_tx_clk_div = ilog2(div64_u64((u64)CLOCK_9999MHZ, (u64)datarate)); vco_freq_shift = ilog2(div64_u64((u64)CLOCK_4999MHZ * (u64)256, (u64)datarate)); vco_freq = (datarate << vco_freq_shift) >> 8; @@ -2477,9 +2477,9 @@ static int intel_c20_compute_hdmi_tmds_pll(const struct intel_crtc_state *crtc_s else mpllb_ana_freq_vco = MPLLB_ANA_FREQ_VCO_0; - pll_state->clock = crtc_state->port_clock; + pll_state->clock = port_clock; pll_state->tx[0] = 0xbe88; - pll_state->tx[1] = intel_c20_hdmi_tmds_tx_cgf_1(crtc_state); + pll_state->tx[1] = intel_c20_hdmi_tmds_tx_cgf_1(display); pll_state->tx[2] = 0x0000; pll_state->cmn[0] = 0x0500; pll_state->cmn[1] = 0x0005; @@ -2731,7 +2731,8 @@ static int intel_c20pll_calc_state(const struct intel_crtc_state *crtc_state, /* TODO: Update SSC state for HDMI as well */ if (!is_dp && err) - err = intel_c20_compute_hdmi_tmds_pll(crtc_state, &hw_state->cx0pll.c20); + err = intel_c20_compute_hdmi_tmds_pll(display, crtc_state->port_clock, + &hw_state->cx0pll.c20); if (err) return err; From a35ab9c32f19a9435d3526fb6b025343c1dc7400 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:45 +0000 Subject: [PATCH 0038/1735] drm/i915/lt_phy: Drop LT PHY crtc_state for port calculation Drop crtc_state from intel_lt_phy_calc_port_clock() function call and replace it with pll state instead. Follow-up changes will call these functions without a crtc_state available. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-5-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 4 +++- drivers/gpu/drm/i915/display/intel_dpll.c | 3 ++- drivers/gpu/drm/i915/display/intel_lt_phy.c | 18 +++++++----------- drivers/gpu/drm/i915/display/intel_lt_phy.h | 4 ++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index cb91d07cdaa6..d8739e2bb004 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4247,13 +4247,15 @@ void intel_ddi_get_clock(struct intel_encoder *encoder, 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, &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(encoder, crtc_state); + intel_lt_phy_calc_port_clock(display, &crtc_state->dpll_hw_state.ltpll); intel_ddi_get_config(encoder, crtc_state); } diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index a4f372c9e6fc..1b5b18fa0a36 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1219,6 +1219,7 @@ static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *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); @@ -1227,7 +1228,7 @@ static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *state, /* TODO: Do the readback via intel_compute_shared_dplls() */ crtc_state->port_clock = - intel_lt_phy_calc_port_clock(encoder, crtc_state); + 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); diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 6cdae03ee172..48f644f417d2 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1680,7 +1680,8 @@ intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state, } static int -intel_lt_phy_calc_hdmi_port_clock(const struct intel_crtc_state *crtc_state) +intel_lt_phy_calc_hdmi_port_clock(struct intel_display *display, + const struct intel_lt_phy_pll_state *lt_state) { #define REGVAL(i) ( \ (lt_state->data[i][3]) | \ @@ -1689,9 +1690,6 @@ intel_lt_phy_calc_hdmi_port_clock(const struct intel_crtc_state *crtc_state) (lt_state->data[i][0] << 24) \ ) - struct intel_display *display = to_intel_display(crtc_state); - const struct intel_lt_phy_pll_state *lt_state = - &crtc_state->dpll_hw_state.ltpll; int clk = 0; u32 d8, pll_reg_5, pll_reg_3, pll_reg_57, m2div_frac, m2div_int; u64 temp0, temp1; @@ -1749,13 +1747,10 @@ intel_lt_phy_calc_hdmi_port_clock(const struct intel_crtc_state *crtc_state) } int -intel_lt_phy_calc_port_clock(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +intel_lt_phy_calc_port_clock(struct intel_display *display, + const struct intel_lt_phy_pll_state *lt_state) { - struct intel_display *display = to_intel_display(encoder); int clk; - const struct intel_lt_phy_pll_state *lt_state = - &crtc_state->dpll_hw_state.ltpll; u8 mode, rate; mode = REG_FIELD_GET8(LT_PHY_VDR_MODE_ENCODING_MASK, @@ -1771,7 +1766,7 @@ intel_lt_phy_calc_port_clock(struct intel_encoder *encoder, lt_state->config[0]); clk = intel_lt_phy_get_dp_clock(rate); } else if (mode == MODE_HDMI_20) { - clk = intel_lt_phy_calc_hdmi_port_clock(crtc_state); + clk = intel_lt_phy_calc_hdmi_port_clock(display, lt_state); } else { drm_WARN_ON(display->drm, "Unsupported LT PHY Mode!\n"); clk = xe3plpd_lt_hdmi_252.clock; @@ -2230,6 +2225,7 @@ 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) { + struct intel_display *display = to_intel_display(encoder); u8 owned_lane_mask; u8 lane; struct ref_tracker *wakeref; @@ -2255,7 +2251,7 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, } pll_state->clock = - intel_lt_phy_calc_port_clock(encoder, crtc_state); + intel_lt_phy_calc_port_clock(display, &crtc_state->dpll_hw_state.ltpll); intel_lt_phy_transaction_end(encoder, wakeref); } diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index bf41858f1bc3..22b12d2d5bb1 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -21,8 +21,8 @@ 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); -int intel_lt_phy_calc_port_clock(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_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, const struct intel_crtc_state *crtc_state); void intel_lt_phy_dump_hw_state(struct intel_display *display, From 81152791701fdfa1bb9adff846a6eef888021227 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:46 +0000 Subject: [PATCH 0039/1735] drm/i915/cx0: Drop encoder from port clock calculation For C10 and C20 we have unused encoder parameter passed to port clock calculation function. Remove the encoder from being passed to the port clock calculation function. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-6-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 6447d7c80ffc..77378e057908 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2103,8 +2103,7 @@ static bool cx0pll_state_is_dp(const struct intel_cx0pll_state *pll_state) return c20pll_state_is_dp(&pll_state->c20); } -static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c10pll_state *pll_state) +static int intel_c10pll_calc_port_clock(const struct intel_c10pll_state *pll_state) { unsigned int frac_quot = 0, frac_rem = 0, frac_den = 1; unsigned int multiplier, tx_clk_div, hdmi_div, refclk = 38400; @@ -2135,8 +2134,7 @@ static bool intel_c20phy_use_mpllb(const struct intel_c20pll_state *state) return state->tx[0] & C20_PHY_USE_MPLLB; } -static int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c20pll_state *pll_state) +static int intel_c20pll_calc_port_clock(const struct intel_c20pll_state *pll_state) { unsigned int frac, frac_en, frac_quot, frac_rem, frac_den; unsigned int multiplier, refclk = 38400; @@ -2325,7 +2323,7 @@ static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, intel_cx0_phy_transaction_end(encoder, wakeref); - pll_state->clock = intel_c10pll_calc_port_clock(encoder, pll_state); + pll_state->clock = intel_c10pll_calc_port_clock(pll_state); cx0pll_state->ssc_enabled = readout_ssc_state(encoder, true); @@ -2824,7 +2822,7 @@ static void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, } } - pll_state->clock = intel_c20pll_calc_port_clock(encoder, pll_state); + pll_state->clock = intel_c20pll_calc_port_clock(pll_state); intel_cx0_phy_transaction_end(encoder, wakeref); @@ -3731,9 +3729,9 @@ int intel_cx0pll_calc_port_clock(struct intel_encoder *encoder, const struct intel_cx0pll_state *pll_state) { if (intel_encoder_is_c10phy(encoder)) - return intel_c10pll_calc_port_clock(encoder, &pll_state->c10); + return intel_c10pll_calc_port_clock(&pll_state->c10); - return intel_c20pll_calc_port_clock(encoder, &pll_state->c20); + return intel_c20pll_calc_port_clock(&pll_state->c20); } /* From 52801b2eb532e8516e2366c4f67ef7cd022ef4ba Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:47 +0000 Subject: [PATCH 0040/1735] drm/i915/cx0: Create macro around PLL tables Create macro for storing PLL dividers with table name and clock rate. v2: Preserve the terminating {} in each PLL table. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-7-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 297 ++++++++++--------- 1 file changed, 165 insertions(+), 132 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 77378e057908..8e780480f6c0 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -780,25 +780,58 @@ static const struct intel_c10pll_state mtl_c10_dp_hbr3 = { .pll[19] = 0x23, }; -static const struct intel_c10pll_state * const mtl_c10_dp_tables[] = { - &mtl_c10_dp_rbr, - &mtl_c10_dp_hbr1, - &mtl_c10_dp_hbr2, - &mtl_c10_dp_hbr3, - NULL, +struct intel_cx0pll_params { + const char *name; + bool is_c10; + bool is_hdmi; + int clock_rate; + union { + const struct intel_c10pll_state *c10; + const struct intel_c20pll_state *c20; + }; }; -static const struct intel_c10pll_state * const mtl_c10_edp_tables[] = { - &mtl_c10_dp_rbr, - &mtl_c10_edp_r216, - &mtl_c10_edp_r243, - &mtl_c10_dp_hbr1, - &mtl_c10_edp_r324, - &mtl_c10_edp_r432, - &mtl_c10_dp_hbr2, - &mtl_c10_edp_r675, - &mtl_c10_dp_hbr3, - NULL, +#define __C10PLL_PARAMS(__is_hdmi, __clock_rate, __state) { \ + .name = __stringify(__state), \ + .is_c10 = true, \ + .is_hdmi = __is_hdmi, \ + .clock_rate = __clock_rate, \ + .c10 = &__state, \ +} + +#define __C20PLL_PARAMS(__is_hdmi, __clock_rate, __state) { \ + .name = __stringify(__state), \ + .is_c10 = false, \ + .is_hdmi = __is_hdmi, \ + .clock_rate = __clock_rate, \ + .c20 = &__state, \ +} + +#define C10PLL_HDMI_PARAMS(__clock_rate, __state) __C10PLL_PARAMS(true, __clock_rate, __state) +#define C10PLL_DP_PARAMS(__clock_rate, __state) __C10PLL_PARAMS(false, __clock_rate, __state) + +#define C20PLL_HDMI_PARAMS(__clock_rate, __state) __C20PLL_PARAMS(true, __clock_rate, __state) +#define C20PLL_DP_PARAMS(__clock_rate, __state) __C20PLL_PARAMS(false, __clock_rate, __state) + +static const struct intel_cx0pll_params mtl_c10_dp_tables[] = { + C10PLL_DP_PARAMS(162000, mtl_c10_dp_rbr), + C10PLL_DP_PARAMS(270000, mtl_c10_dp_hbr1), + C10PLL_DP_PARAMS(540000, mtl_c10_dp_hbr2), + C10PLL_DP_PARAMS(810000, mtl_c10_dp_hbr3), + {} +}; + +static const struct intel_cx0pll_params mtl_c10_edp_tables[] = { + C10PLL_DP_PARAMS(162000, mtl_c10_dp_rbr), + C10PLL_DP_PARAMS(216000, mtl_c10_edp_r216), + C10PLL_DP_PARAMS(243000, mtl_c10_edp_r243), + C10PLL_DP_PARAMS(270000, mtl_c10_dp_hbr1), + C10PLL_DP_PARAMS(324000, mtl_c10_edp_r324), + C10PLL_DP_PARAMS(432000, mtl_c10_edp_r432), + C10PLL_DP_PARAMS(540000, mtl_c10_dp_hbr2), + C10PLL_DP_PARAMS(675000, mtl_c10_edp_r675), + C10PLL_DP_PARAMS(810000, mtl_c10_dp_hbr3), + {} }; /* C20 basic DP 1.4 tables */ @@ -976,15 +1009,15 @@ static const struct intel_c20pll_state mtl_c20_dp_uhbr20 = { }, }; -static const struct intel_c20pll_state * const mtl_c20_dp_tables[] = { - &mtl_c20_dp_rbr, - &mtl_c20_dp_hbr1, - &mtl_c20_dp_hbr2, - &mtl_c20_dp_hbr3, - &mtl_c20_dp_uhbr10, - &mtl_c20_dp_uhbr13_5, - &mtl_c20_dp_uhbr20, - NULL, +static const struct intel_cx0pll_params mtl_c20_dp_tables[] = { + C20PLL_DP_PARAMS(162000, mtl_c20_dp_rbr), + C20PLL_DP_PARAMS(270000, mtl_c20_dp_hbr1), + C20PLL_DP_PARAMS(540000, mtl_c20_dp_hbr2), + C20PLL_DP_PARAMS(810000, mtl_c20_dp_hbr3), + C20PLL_DP_PARAMS(1000000, mtl_c20_dp_uhbr10), + C20PLL_DP_PARAMS(1350000, mtl_c20_dp_uhbr13_5), + C20PLL_DP_PARAMS(2000000, mtl_c20_dp_uhbr20), + {} }; /* @@ -1116,17 +1149,17 @@ static const struct intel_c20pll_state xe2hpd_c20_edp_r675 = { }, }; -static const struct intel_c20pll_state * const xe2hpd_c20_edp_tables[] = { - &mtl_c20_dp_rbr, - &xe2hpd_c20_edp_r216, - &xe2hpd_c20_edp_r243, - &mtl_c20_dp_hbr1, - &xe2hpd_c20_edp_r324, - &xe2hpd_c20_edp_r432, - &mtl_c20_dp_hbr2, - &xe2hpd_c20_edp_r675, - &mtl_c20_dp_hbr3, - NULL, +static const struct intel_cx0pll_params xe2hpd_c20_edp_tables[] = { + C20PLL_DP_PARAMS(162000, mtl_c20_dp_rbr), + C20PLL_DP_PARAMS(216000, xe2hpd_c20_edp_r216), + C20PLL_DP_PARAMS(243000, xe2hpd_c20_edp_r243), + C20PLL_DP_PARAMS(270000, mtl_c20_dp_hbr1), + C20PLL_DP_PARAMS(324000, xe2hpd_c20_edp_r324), + C20PLL_DP_PARAMS(432000, xe2hpd_c20_edp_r432), + C20PLL_DP_PARAMS(540000, mtl_c20_dp_hbr2), + C20PLL_DP_PARAMS(675000, xe2hpd_c20_edp_r675), + C20PLL_DP_PARAMS(810000, mtl_c20_dp_hbr3), + {} }; static const struct intel_c20pll_state xe2hpd_c20_dp_uhbr13_5 = { @@ -1154,30 +1187,30 @@ static const struct intel_c20pll_state xe2hpd_c20_dp_uhbr13_5 = { }, }; -static const struct intel_c20pll_state * const xe2hpd_c20_dp_tables[] = { - &mtl_c20_dp_rbr, - &mtl_c20_dp_hbr1, - &mtl_c20_dp_hbr2, - &mtl_c20_dp_hbr3, - &mtl_c20_dp_uhbr10, - &xe2hpd_c20_dp_uhbr13_5, - NULL, +static const struct intel_cx0pll_params xe2hpd_c20_dp_tables[] = { + C20PLL_DP_PARAMS(162000, mtl_c20_dp_rbr), + C20PLL_DP_PARAMS(270000, mtl_c20_dp_hbr1), + C20PLL_DP_PARAMS(540000, mtl_c20_dp_hbr2), + C20PLL_DP_PARAMS(810000, mtl_c20_dp_hbr3), + C20PLL_DP_PARAMS(1000000, mtl_c20_dp_uhbr10), + C20PLL_DP_PARAMS(1350000, xe2hpd_c20_dp_uhbr13_5), + {} }; -static const struct intel_c20pll_state * const xe3lpd_c20_dp_edp_tables[] = { - &mtl_c20_dp_rbr, - &xe2hpd_c20_edp_r216, - &xe2hpd_c20_edp_r243, - &mtl_c20_dp_hbr1, - &xe2hpd_c20_edp_r324, - &xe2hpd_c20_edp_r432, - &mtl_c20_dp_hbr2, - &xe2hpd_c20_edp_r675, - &mtl_c20_dp_hbr3, - &mtl_c20_dp_uhbr10, - &xe2hpd_c20_dp_uhbr13_5, - &mtl_c20_dp_uhbr20, - NULL, +static const struct intel_cx0pll_params xe3lpd_c20_dp_edp_tables[] = { + C20PLL_DP_PARAMS(162000, mtl_c20_dp_rbr), + C20PLL_DP_PARAMS(216000, xe2hpd_c20_edp_r216), + C20PLL_DP_PARAMS(243000, xe2hpd_c20_edp_r243), + C20PLL_DP_PARAMS(270000, mtl_c20_dp_hbr1), + C20PLL_DP_PARAMS(324000, xe2hpd_c20_edp_r324), + C20PLL_DP_PARAMS(432000, xe2hpd_c20_edp_r432), + C20PLL_DP_PARAMS(540000, mtl_c20_dp_hbr2), + C20PLL_DP_PARAMS(675000, xe2hpd_c20_edp_r675), + C20PLL_DP_PARAMS(810000, mtl_c20_dp_hbr3), + C20PLL_DP_PARAMS(1000000, mtl_c20_dp_uhbr10), + C20PLL_DP_PARAMS(1350000, xe2hpd_c20_dp_uhbr13_5), + C20PLL_DP_PARAMS(2000000, mtl_c20_dp_uhbr20), + {} }; /* @@ -1715,53 +1748,53 @@ static const struct intel_c10pll_state mtl_c10_hdmi_593407 = { .pll[15] = 0x08, .pll[16] = 0x08, .pll[17] = 0x8F, .pll[18] = 0x84, .pll[19] = 0x23, }; -static const struct intel_c10pll_state * const mtl_c10_hdmi_tables[] = { - &mtl_c10_hdmi_25_2, /* Consolidated Table */ - &mtl_c10_hdmi_27_0, /* Consolidated Table */ - &mtl_c10_hdmi_27027, - &mtl_c10_hdmi_28320, - &mtl_c10_hdmi_30240, - &mtl_c10_hdmi_31500, - &mtl_c10_hdmi_36000, - &mtl_c10_hdmi_40000, - &mtl_c10_hdmi_49500, - &mtl_c10_hdmi_50000, - &mtl_c10_hdmi_57284, - &mtl_c10_hdmi_58000, - &mtl_c10_hdmi_65000, - &mtl_c10_hdmi_71000, - &mtl_c10_hdmi_74176, - &mtl_c10_hdmi_74_25, /* Consolidated Table */ - &mtl_c10_hdmi_75000, - &mtl_c10_hdmi_78750, - &mtl_c10_hdmi_85500, - &mtl_c10_hdmi_88750, - &mtl_c10_hdmi_106500, - &mtl_c10_hdmi_108000, - &mtl_c10_hdmi_115500, - &mtl_c10_hdmi_119000, - &mtl_c10_hdmi_135000, - &mtl_c10_hdmi_138500, - &mtl_c10_hdmi_147160, - &mtl_c10_hdmi_148352, - &mtl_c10_hdmi_148_5, /* Consolidated Table */ - &mtl_c10_hdmi_154000, - &mtl_c10_hdmi_162000, - &mtl_c10_hdmi_167000, - &mtl_c10_hdmi_197802, - &mtl_c10_hdmi_198000, - &mtl_c10_hdmi_209800, - &mtl_c10_hdmi_241500, - &mtl_c10_hdmi_262750, - &mtl_c10_hdmi_268500, - &mtl_c10_hdmi_296703, - &mtl_c10_hdmi_297000, - &mtl_c10_hdmi_319750, - &mtl_c10_hdmi_497750, - &mtl_c10_hdmi_592000, - &mtl_c10_hdmi_593407, - &mtl_c10_hdmi_594, /* Consolidated Table */ - NULL, +static const struct intel_cx0pll_params mtl_c10_hdmi_tables[] = { + C10PLL_HDMI_PARAMS(25200, mtl_c10_hdmi_25_2), /* Consolidated Table */ + C10PLL_HDMI_PARAMS(27000, mtl_c10_hdmi_27_0), /* Consolidated Table */ + C10PLL_HDMI_PARAMS(27027, mtl_c10_hdmi_27027), + C10PLL_HDMI_PARAMS(28320, mtl_c10_hdmi_28320), + C10PLL_HDMI_PARAMS(30240, mtl_c10_hdmi_30240), + C10PLL_HDMI_PARAMS(31500, mtl_c10_hdmi_31500), + C10PLL_HDMI_PARAMS(36000, mtl_c10_hdmi_36000), + C10PLL_HDMI_PARAMS(40000, mtl_c10_hdmi_40000), + C10PLL_HDMI_PARAMS(49500, mtl_c10_hdmi_49500), + C10PLL_HDMI_PARAMS(50000, mtl_c10_hdmi_50000), + C10PLL_HDMI_PARAMS(57284, mtl_c10_hdmi_57284), + C10PLL_HDMI_PARAMS(58000, mtl_c10_hdmi_58000), + C10PLL_HDMI_PARAMS(65000, mtl_c10_hdmi_65000), + C10PLL_HDMI_PARAMS(71000, mtl_c10_hdmi_71000), + C10PLL_HDMI_PARAMS(74176, mtl_c10_hdmi_74176), + C10PLL_HDMI_PARAMS(74250, mtl_c10_hdmi_74_25), /* Consolidated Table */ + C10PLL_HDMI_PARAMS(75000, mtl_c10_hdmi_75000), + C10PLL_HDMI_PARAMS(78750, mtl_c10_hdmi_78750), + C10PLL_HDMI_PARAMS(85500, mtl_c10_hdmi_85500), + C10PLL_HDMI_PARAMS(88750, mtl_c10_hdmi_88750), + C10PLL_HDMI_PARAMS(106500, mtl_c10_hdmi_106500), + C10PLL_HDMI_PARAMS(108000, mtl_c10_hdmi_108000), + C10PLL_HDMI_PARAMS(115500, mtl_c10_hdmi_115500), + C10PLL_HDMI_PARAMS(119000, mtl_c10_hdmi_119000), + C10PLL_HDMI_PARAMS(135000, mtl_c10_hdmi_135000), + C10PLL_HDMI_PARAMS(138500, mtl_c10_hdmi_138500), + C10PLL_HDMI_PARAMS(147160, mtl_c10_hdmi_147160), + C10PLL_HDMI_PARAMS(148352, mtl_c10_hdmi_148352), + C10PLL_HDMI_PARAMS(148500, mtl_c10_hdmi_148_5), /* Consolidated Table */ + C10PLL_HDMI_PARAMS(154000, mtl_c10_hdmi_154000), + C10PLL_HDMI_PARAMS(162000, mtl_c10_hdmi_162000), + C10PLL_HDMI_PARAMS(167000, mtl_c10_hdmi_167000), + C10PLL_HDMI_PARAMS(197802, mtl_c10_hdmi_197802), + C10PLL_HDMI_PARAMS(198000, mtl_c10_hdmi_198000), + C10PLL_HDMI_PARAMS(209800, mtl_c10_hdmi_209800), + C10PLL_HDMI_PARAMS(241500, mtl_c10_hdmi_241500), + C10PLL_HDMI_PARAMS(262750, mtl_c10_hdmi_262750), + C10PLL_HDMI_PARAMS(268500, mtl_c10_hdmi_268500), + C10PLL_HDMI_PARAMS(296703, mtl_c10_hdmi_296703), + C10PLL_HDMI_PARAMS(297000, mtl_c10_hdmi_297000), + C10PLL_HDMI_PARAMS(319750, mtl_c10_hdmi_319750), + C10PLL_HDMI_PARAMS(497750, mtl_c10_hdmi_497750), + C10PLL_HDMI_PARAMS(592000, mtl_c10_hdmi_592000), + C10PLL_HDMI_PARAMS(593407, mtl_c10_hdmi_593407), + C10PLL_HDMI_PARAMS(594000, mtl_c10_hdmi_594), /* Consolidated Table */ + {} }; static const struct intel_c20pll_state mtl_c20_hdmi_25_175 = { @@ -2014,21 +2047,21 @@ static const struct intel_c20pll_state mtl_c20_hdmi_1200 = { }, }; -static const struct intel_c20pll_state * const mtl_c20_hdmi_tables[] = { - &mtl_c20_hdmi_25_175, - &mtl_c20_hdmi_27_0, - &mtl_c20_hdmi_74_25, - &mtl_c20_hdmi_148_5, - &mtl_c20_hdmi_594, - &mtl_c20_hdmi_300, - &mtl_c20_hdmi_600, - &mtl_c20_hdmi_800, - &mtl_c20_hdmi_1000, - &mtl_c20_hdmi_1200, - NULL, +static const struct intel_cx0pll_params mtl_c20_hdmi_tables[] = { + C20PLL_HDMI_PARAMS(25175, mtl_c20_hdmi_25_175), + C20PLL_HDMI_PARAMS(27000, mtl_c20_hdmi_27_0), + C20PLL_HDMI_PARAMS(74250, mtl_c20_hdmi_74_25), + C20PLL_HDMI_PARAMS(148500, mtl_c20_hdmi_148_5), + C20PLL_HDMI_PARAMS(594000, mtl_c20_hdmi_594), + C20PLL_HDMI_PARAMS(300000, mtl_c20_hdmi_300), + C20PLL_HDMI_PARAMS(600000, mtl_c20_hdmi_600), + C20PLL_HDMI_PARAMS(800000, mtl_c20_hdmi_800), + C20PLL_HDMI_PARAMS(1000000, mtl_c20_hdmi_1000), + C20PLL_HDMI_PARAMS(1200000, mtl_c20_hdmi_1200), + {} }; -static const struct intel_c10pll_state * const * +static const struct intel_cx0pll_params * intel_c10pll_tables_get(const struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { @@ -2183,16 +2216,16 @@ static int intel_c20pll_calc_port_clock(const struct intel_c20pll_state *pll_sta * intel_c20pll_calc_state_from_table(). */ static int intel_c10pll_calc_state_from_table(struct intel_encoder *encoder, - const struct intel_c10pll_state * const *tables, + const struct intel_cx0pll_params *tables, bool is_dp, int port_clock, int lane_count, struct intel_cx0pll_state *pll_state) { struct intel_display *display = to_intel_display(encoder); int i; - for (i = 0; tables[i]; i++) { - if (port_clock == tables[i]->clock) { - pll_state->c10 = *tables[i]; + for (i = 0; tables[i].name; i++) { + if (port_clock == tables[i].clock_rate) { + pll_state->c10 = *tables[i].c10; intel_cx0pll_update_ssc(encoder, pll_state, is_dp); intel_c10pll_update_pll(encoder, pll_state); @@ -2214,7 +2247,7 @@ static int intel_c10pll_calc_state(const struct intel_crtc_state *crtc_state, { struct intel_display *display = to_intel_display(encoder); bool is_dp = intel_crtc_has_dp_encoder(crtc_state); - const struct intel_c10pll_state * const *tables; + const struct intel_cx0pll_params *tables; int err; tables = intel_c10pll_tables_get(crtc_state, encoder); @@ -2505,7 +2538,7 @@ static int intel_c20_compute_hdmi_tmds_pll(struct intel_display *display, return 0; } -static const struct intel_c20pll_state * const * +static const struct intel_cx0pll_params * intel_c20_pll_tables_get(const struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { @@ -2673,20 +2706,20 @@ static void intel_c20_program_vdr_params(struct intel_encoder *encoder, MB_WRITE_COMMITTED); } -static const struct intel_c20pll_state * +static const struct intel_cx0pll_params * intel_c20_pll_find_table(const struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { - const struct intel_c20pll_state * const *tables; + const struct intel_cx0pll_params *tables; int i; tables = intel_c20_pll_tables_get(crtc_state, encoder); if (!tables) return NULL; - for (i = 0; tables[i]; i++) - if (crtc_state->port_clock == tables[i]->clock) - return tables[i]; + for (i = 0; tables[i].name; i++) + if (crtc_state->port_clock == tables[i].clock_rate) + return &tables[i]; return NULL; } @@ -2695,13 +2728,13 @@ static int intel_c20pll_calc_state_from_table(const struct intel_crtc_state *crt struct intel_encoder *encoder, struct intel_cx0pll_state *pll_state) { - const struct intel_c20pll_state *table; + const struct intel_cx0pll_params *table; table = intel_c20_pll_find_table(crtc_state, encoder); if (!table) return -EINVAL; - pll_state->c20 = *table; + pll_state->c20 = *table->c20; intel_cx0pll_update_ssc(encoder, pll_state, intel_crtc_has_dp_encoder(crtc_state)); From cf0635d40af0029ed5f600f366a395df3c58a19b Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:48 +0000 Subject: [PATCH 0041/1735] drm/i915/lt_phy: Create macro for LT PHY PLL state Create a macro for PLL state for LT PHY similar as for cx0 case. v2: - Move addition of LT_PHY_PLL_DP/HDMI_PARAMS() to this patch. - Fix end of table checking while looking up a table. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-8-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 83 +++++++++++++-------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 48f644f417d2..a86ae6139ff0 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -437,15 +437,32 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr20 = { }, }; -static const struct intel_lt_phy_pll_state * const xe3plpd_lt_dp_tables[] = { - &xe3plpd_lt_dp_rbr, - &xe3plpd_lt_dp_hbr1, - &xe3plpd_lt_dp_hbr2, - &xe3plpd_lt_dp_hbr3, - &xe3plpd_lt_dp_uhbr10, - &xe3plpd_lt_dp_uhbr13_5, - &xe3plpd_lt_dp_uhbr20, - NULL, +struct intel_lt_phy_pll_params { + const char *name; + bool is_hdmi; + int clock_rate; + const struct intel_lt_phy_pll_state *state; +}; + +#define __LT_PHY_PLL_PARAMS(__is_hdmi, __clock_rate, __state) { \ + .name = __stringify(__state), \ + .is_hdmi = __is_hdmi, \ + .clock_rate = __clock_rate, \ + .state = &__state, \ +} + +#define LT_PHY_PLL_HDMI_PARAMS(__clock_rate, __state) __LT_PHY_PLL_PARAMS(true, __clock_rate, __state) +#define LT_PHY_PLL_DP_PARAMS(__clock_rate, __state) __LT_PHY_PLL_PARAMS(false, __clock_rate, __state) + +static const struct intel_lt_phy_pll_params xe3plpd_lt_dp_tables[] = { + LT_PHY_PLL_DP_PARAMS(162000, xe3plpd_lt_dp_rbr), + LT_PHY_PLL_DP_PARAMS(270000, xe3plpd_lt_dp_hbr1), + LT_PHY_PLL_DP_PARAMS(540000, xe3plpd_lt_dp_hbr2), + LT_PHY_PLL_DP_PARAMS(810000, xe3plpd_lt_dp_hbr3), + LT_PHY_PLL_DP_PARAMS(1000000, xe3plpd_lt_dp_uhbr10), + LT_PHY_PLL_DP_PARAMS(1350000, xe3plpd_lt_dp_uhbr13_5), + LT_PHY_PLL_DP_PARAMS(2000000, xe3plpd_lt_dp_uhbr20), + {} }; static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_2_16 = { @@ -718,17 +735,17 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_6_75 = { }, }; -static const struct intel_lt_phy_pll_state * const xe3plpd_lt_edp_tables[] = { - &xe3plpd_lt_dp_rbr, - &xe3plpd_lt_edp_2_16, - &xe3plpd_lt_edp_2_43, - &xe3plpd_lt_dp_hbr1, - &xe3plpd_lt_edp_3_24, - &xe3plpd_lt_edp_4_32, - &xe3plpd_lt_dp_hbr2, - &xe3plpd_lt_edp_6_75, - &xe3plpd_lt_dp_hbr3, - NULL, +static const struct intel_lt_phy_pll_params xe3plpd_lt_edp_tables[] = { + LT_PHY_PLL_DP_PARAMS(162000, xe3plpd_lt_dp_rbr), + LT_PHY_PLL_DP_PARAMS(216000, xe3plpd_lt_edp_2_16), + LT_PHY_PLL_DP_PARAMS(243000, xe3plpd_lt_edp_2_43), + LT_PHY_PLL_DP_PARAMS(270000, xe3plpd_lt_dp_hbr1), + LT_PHY_PLL_DP_PARAMS(324000, xe3plpd_lt_edp_3_24), + LT_PHY_PLL_DP_PARAMS(432000, xe3plpd_lt_edp_4_32), + LT_PHY_PLL_DP_PARAMS(540000, xe3plpd_lt_dp_hbr2), + LT_PHY_PLL_DP_PARAMS(675000, xe3plpd_lt_edp_6_75), + LT_PHY_PLL_DP_PARAMS(810000, xe3plpd_lt_dp_hbr3), + {} }; static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_252 = { @@ -1001,13 +1018,13 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_5p94 = { }, }; -static const struct intel_lt_phy_pll_state * const xe3plpd_lt_hdmi_tables[] = { - &xe3plpd_lt_hdmi_252, - &xe3plpd_lt_hdmi_272, - &xe3plpd_lt_hdmi_742p5, - &xe3plpd_lt_hdmi_1p485, - &xe3plpd_lt_hdmi_5p94, - NULL, +static const struct intel_lt_phy_pll_params xe3plpd_lt_hdmi_tables[] = { + LT_PHY_PLL_HDMI_PARAMS(25200, xe3plpd_lt_hdmi_252), + LT_PHY_PLL_HDMI_PARAMS(27200, xe3plpd_lt_hdmi_272), + LT_PHY_PLL_HDMI_PARAMS(74250, xe3plpd_lt_hdmi_742p5), + LT_PHY_PLL_HDMI_PARAMS(148500, xe3plpd_lt_hdmi_1p485), + LT_PHY_PLL_HDMI_PARAMS(594000, xe3plpd_lt_hdmi_5p94), + {} }; static u8 intel_lt_phy_get_owned_lane_mask(struct intel_encoder *encoder) @@ -1346,7 +1363,7 @@ static void intel_lt_phy_transaction_end(struct intel_encoder *encoder, struct r intel_display_power_put(display, POWER_DOMAIN_DC_OFF, wakeref); } -static const struct intel_lt_phy_pll_state * const * +static const struct intel_lt_phy_pll_params * intel_lt_phy_pll_tables_get(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { @@ -1735,7 +1752,7 @@ intel_lt_phy_calc_hdmi_port_clock(struct intel_display *display, if (d8 == 0) { drm_WARN_ON(display->drm, "Invalid port clock using lowest HDMI portclock\n"); - return xe3plpd_lt_hdmi_252.clock; + return xe3plpd_lt_hdmi_tables[0].clock_rate; } m2div_int = (pll_reg_3 & REG_GENMASK(14, 5)) >> 5; temp0 = ((u64)m2div_frac * REF_CLK_KHZ) >> 32; @@ -1779,16 +1796,16 @@ int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { - const struct intel_lt_phy_pll_state * const *tables; + const struct intel_lt_phy_pll_params *tables; int i; tables = intel_lt_phy_pll_tables_get(crtc_state, encoder); if (!tables) return -EINVAL; - for (i = 0; tables[i]; i++) { - if (crtc_state->port_clock == tables[i]->clock) { - crtc_state->dpll_hw_state.ltpll = *tables[i]; + for (i = 0; tables[i].name; i++) { + if (crtc_state->port_clock == tables[i].clock_rate) { + crtc_state->dpll_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; From 920fa5d920c362a2e57ffec7bb39b5c25bc59610 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:49 +0000 Subject: [PATCH 0042/1735] drm/i915/display: Add helper function for fuzzy clock check The hard coded clock rate stored in the PLL state will be removed by a follow-up change. The clock is calculated instead of using clock from the PLL divider values. Since this calculated clock may vary due to fixed point rounding issues, a +-1 kHz variation is allowed with the request clock rate against the calculated clock rate. v2: - Use the stricter +-1 kHz allowed difference. - Derive the clock from PLL dividers in intel_cx0pll_enable(). - Move corresponding fuzzy check for LT PHY PLLs to this patch. v3: Reword commit message (Suraj) Move clock check to intel_dpll and rename it (Suraj) Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-9-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 22 ++++++++++++++++---- drivers/gpu/drm/i915/display/intel_dpll.c | 5 +++++ drivers/gpu/drm/i915/display/intel_dpll.h | 1 + drivers/gpu/drm/i915/display/intel_lt_phy.c | 7 ++++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 8e780480f6c0..26d3d41d41a7 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -18,6 +18,7 @@ #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dp.h" +#include "intel_dpll.h" #include "intel_hdmi.h" #include "intel_lt_phy.h" #include "intel_panel.h" @@ -2224,7 +2225,10 @@ static int intel_c10pll_calc_state_from_table(struct intel_encoder *encoder, int i; for (i = 0; tables[i].name; i++) { - if (port_clock == tables[i].clock_rate) { + int clock = intel_c10pll_calc_port_clock(tables[i].c10); + + drm_WARN_ON(display->drm, !intel_dpll_clock_matches(clock, tables[i].clock_rate)); + if (intel_dpll_clock_matches(port_clock, clock)) { pll_state->c10 = *tables[i].c10; intel_cx0pll_update_ssc(encoder, pll_state, is_dp); intel_c10pll_update_pll(encoder, pll_state); @@ -2710,6 +2714,7 @@ static const struct intel_cx0pll_params * intel_c20_pll_find_table(const struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { + struct intel_display *display = to_intel_display(crtc_state); const struct intel_cx0pll_params *tables; int i; @@ -2717,9 +2722,13 @@ intel_c20_pll_find_table(const struct intel_crtc_state *crtc_state, if (!tables) return NULL; - for (i = 0; tables[i].name; i++) - if (crtc_state->port_clock == tables[i].clock_rate) + for (i = 0; tables[i].name; i++) { + int clock = intel_c20pll_calc_port_clock(tables[i].c20); + + drm_WARN_ON(display->drm, !intel_dpll_clock_matches(clock, tables[i].clock_rate)); + if (intel_dpll_clock_matches(crtc_state->port_clock, clock)) return &tables[i]; + } return NULL; } @@ -3255,7 +3264,6 @@ static u32 intel_cx0_get_pclk_pll_ack(u8 lane_mask) static void intel_cx0pll_enable(struct intel_encoder *encoder, const struct intel_cx0pll_state *pll_state) { - int port_clock = pll_state->use_c10 ? pll_state->c10.clock : pll_state->c20.clock; struct intel_display *display = to_intel_display(encoder); enum phy phy = intel_encoder_to_phy(encoder); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); @@ -3263,6 +3271,12 @@ static void intel_cx0pll_enable(struct intel_encoder *encoder, u8 maxpclk_lane = lane_reversal ? INTEL_CX0_LANE1 : INTEL_CX0_LANE0; struct ref_tracker *wakeref = intel_cx0_phy_transaction_begin(encoder); + int port_clock; + + if (pll_state->use_c10) + port_clock = intel_c10pll_calc_port_clock(&pll_state->c10); + else + port_clock = intel_c20pll_calc_port_clock(&pll_state->c20); /* * Lane reversal is never used in DP-alt mode, in that case the diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 1b5b18fa0a36..8433e3ff0319 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -2334,3 +2334,8 @@ void assert_pll_disabled(struct intel_display *display, enum pipe pipe) { assert_pll(display, pipe, false); } + +bool intel_dpll_clock_matches(int clock1, int clock2) +{ + return abs(clock1 - clock2) <= 1; +} diff --git a/drivers/gpu/drm/i915/display/intel_dpll.h b/drivers/gpu/drm/i915/display/intel_dpll.h index 3444a2dd3166..8cd0d17e974e 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.h +++ b/drivers/gpu/drm/i915/display/intel_dpll.h @@ -48,5 +48,6 @@ void chv_crtc_clock_get(struct intel_crtc_state *crtc_state); void assert_pll_enabled(struct intel_display *display, enum pipe pipe); void assert_pll_disabled(struct intel_display *display, enum pipe pipe); +bool intel_dpll_clock_matches(int clock1, int clock2); #endif diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index a86ae6139ff0..2790caba5457 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -14,6 +14,7 @@ #include "intel_display.h" #include "intel_display_types.h" #include "intel_display_utils.h" +#include "intel_dpll.h" #include "intel_dpll_mgr.h" #include "intel_hdmi.h" #include "intel_lt_phy.h" @@ -1796,6 +1797,7 @@ int intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { + struct intel_display *display = to_intel_display(crtc_state); const struct intel_lt_phy_pll_params *tables; int i; @@ -1804,7 +1806,10 @@ intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state, return -EINVAL; for (i = 0; tables[i].name; i++) { - if (crtc_state->port_clock == tables[i].clock_rate) { + int clock = intel_lt_phy_calc_port_clock(display, tables[i].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; if (intel_crtc_has_dp_encoder(crtc_state)) { if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) From 6af62d12317f369f96b0f272e9e074372d09e89d Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:50 +0000 Subject: [PATCH 0043/1735] drm/i915/cx0: Fix HDMI FRL clock rates HDMI FRL clock rates are incorrectly defined. Fix these rates. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-10-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 26d3d41d41a7..eda0e176b8be 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -1924,7 +1924,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_594 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_300 = { - .clock = 3000000, + .clock = 300000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1949,7 +1949,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_300 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_600 = { - .clock = 6000000, + .clock = 600000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1974,7 +1974,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_600 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_800 = { - .clock = 8000000, + .clock = 800000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1999,7 +1999,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_800 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { - .clock = 10000000, + .clock = 1000000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -2024,7 +2024,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_1200 = { - .clock = 12000000, + .clock = 1200000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ From 50ad932880feaf6a526e0cfc314a4caf83310cd3 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:51 +0000 Subject: [PATCH 0044/1735] drm/i915/cx0: Add a fuzzy check for DP/HDMI clock rates during programming Since the clock rate is derived from the PLL divider values it can have a +-1kHz difference wrt. the reference rates in the comparison Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-11-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 8 +++++++- drivers/gpu/drm/i915/display/intel_hdmi.c | 19 +++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index eda0e176b8be..3b56d25c8db8 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -3012,6 +3012,12 @@ static void intel_c20_pll_program(struct intel_display *display, MB_WRITE_COMMITTED); } +static bool is_mplla_clock_rate(int clock) +{ + return intel_dpll_clock_matches(clock, 1000000) || + intel_dpll_clock_matches(clock, 2000000); +} + static void intel_program_port_clock_ctl(struct intel_encoder *encoder, const struct intel_cx0pll_state *pll_state, int port_clock, @@ -3037,7 +3043,7 @@ static void intel_program_port_clock_ctl(struct intel_encoder *encoder, /* TODO: HDMI FRL */ /* DP2.0 10G and 20G rates enable MPLLA*/ - if (port_clock == 1000000 || port_clock == 2000000) + if (is_mplla_clock_rate(port_clock)) val |= pll_state->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0; else val |= pll_state->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 055e68810d0d..05e898d10a2b 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -56,6 +56,7 @@ #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dp.h" +#include "intel_dpll.h" #include "intel_gmbus.h" #include "intel_hdcp.h" #include "intel_hdcp_regs.h" @@ -70,16 +71,14 @@ bool intel_hdmi_is_frl(u32 clock) { - switch (clock) { - case 300000: /* 3 Gbps */ - case 600000: /* 6 Gbps */ - case 800000: /* 8 Gbps */ - case 1000000: /* 10 Gbps */ - case 1200000: /* 12 Gbps */ - return true; - default: - return false; - } + u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 }; + int i; + + for (i = 0; i < ARRAY_SIZE(rates); i++) + if (intel_dpll_clock_matches(clock, rates[i])) + return true; + + return false; } static void From 58213c1d781cb4f5a4f6cadedf296dc6fc43b3a6 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:52 +0000 Subject: [PATCH 0045/1735] drm/i915/cx0: Verify C10/C20 pll dividers Add verification for pll table dividers. The port clock is computed based on pll tables and, for hdmi case, the algorithmic model is applied to calculate pll dividers. If port clock differs more than +-1 kHz from expected value an drm_warn() is thrown and pll divider differences are printed out for debugging purposes. v2: - Move clock derivation from dividers in intel_cx0pll_enable() earlier in the patchset. - Keep intel_cx0_pll_power_save_wa() in intel_dpll_sanitize_state() - Use tables[i].name != NULL as a terminating condition. - Drop duplicate intel_cx0pll_clock_matches() declaration in header. - Use state vs. params term consistently in intel_c10pll_verify_clock() and intel_c20pll_verify_clock(). Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-12-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 121 ++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cx0_phy.h | 1 + drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 9 +- 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 3b56d25c8db8..ce4b7582b737 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -3838,3 +3838,124 @@ void intel_cx0_pll_power_save_wa(struct intel_display *display) intel_cx0pll_disable(encoder); } } + +static void intel_c10pll_verify_clock(struct intel_display *display, + int precomputed_clock, + const char *pll_state_name, + const struct intel_c10pll_state *pll_state, + bool is_precomputed_state) +{ + struct drm_printer p; + int clock; + + clock = intel_c10pll_calc_port_clock(pll_state); + + if (intel_dpll_clock_matches(clock, precomputed_clock)) + return; + + drm_warn(display->drm, + "PLL state %s (%s): clock difference too high: computed %d, pre-computed %d\n", + pll_state_name, + is_precomputed_state ? "precomputed" : "computed", + clock, precomputed_clock); + + if (!drm_debug_enabled(DRM_UT_KMS)) + return; + + p = drm_dbg_printer(display->drm, DRM_UT_KMS, NULL); + + drm_printf(&p, "PLL state %s (%s):\n", + pll_state_name, + is_precomputed_state ? "precomputed" : "computed"); + intel_c10pll_dump_hw_state(&p, pll_state); +} + +static void intel_c10pll_verify_params(struct intel_display *display, + const struct intel_cx0pll_params *pll_params) +{ + struct intel_c10pll_state pll_state; + + intel_c10pll_verify_clock(display, pll_params->clock_rate, pll_params->name, pll_params->c10, true); + + if (!pll_params->is_hdmi) + return; + + intel_snps_hdmi_pll_compute_c10pll(&pll_state, pll_params->clock_rate); + + intel_c10pll_verify_clock(display, pll_params->clock_rate, pll_params->name, &pll_state, false); +} + +static void intel_c20pll_verify_clock(struct intel_display *display, + int precomputed_clock, + const char *pll_state_name, + const struct intel_c20pll_state *pll_state, + bool is_precomputed_state) +{ + struct drm_printer p; + int clock; + + clock = intel_c20pll_calc_port_clock(pll_state); + + if (intel_dpll_clock_matches(clock, precomputed_clock)) + return; + + drm_warn(display->drm, + "PLL state %s (%s): clock difference too high: computed %d, pre-computed %d\n", + pll_state_name, + is_precomputed_state ? "precomputed" : "computed", + clock, precomputed_clock); + + if (!drm_debug_enabled(DRM_UT_KMS)) + return; + + p = drm_dbg_printer(display->drm, DRM_UT_KMS, NULL); + + drm_printf(&p, "PLL state %s (%s):\n", + pll_state_name, + is_precomputed_state ? "precomputed" : "computed"); + intel_c20pll_dump_hw_state(&p, pll_state); +} + +static void intel_c20pll_verify_params(struct intel_display *display, + const struct intel_cx0pll_params *pll_params) +{ + struct intel_c20pll_state pll_state; + + intel_c20pll_verify_clock(display, pll_params->clock_rate, pll_params->name, pll_params->c20, true); + + if (!pll_params->is_hdmi) + return; + + if (intel_c20_compute_hdmi_tmds_pll(display, pll_params->clock_rate, &pll_state) != 0) + return; + + intel_c20pll_verify_clock(display, pll_params->clock_rate, pll_params->name, &pll_state, false); +} + +static void intel_cx0pll_verify_tables(struct intel_display *display, + const struct intel_cx0pll_params *tables) +{ + int i; + + for (i = 0; tables[i].name; i++) { + if (tables[i].is_c10) + intel_c10pll_verify_params(display, &tables[i]); + else + intel_c20pll_verify_params(display, &tables[i]); + } +} + +void intel_cx0pll_verify_plls(struct intel_display *display) +{ + /* C10 */ + intel_cx0pll_verify_tables(display, mtl_c10_edp_tables); + intel_cx0pll_verify_tables(display, mtl_c10_dp_tables); + intel_cx0pll_verify_tables(display, mtl_c10_hdmi_tables); + + /* C20 */ + intel_cx0pll_verify_tables(display, xe2hpd_c20_edp_tables); + intel_cx0pll_verify_tables(display, mtl_c20_dp_tables); + intel_cx0pll_verify_tables(display, xe2hpd_c20_dp_tables); + intel_cx0pll_verify_tables(display, xe3lpd_c20_dp_edp_tables); + intel_cx0pll_verify_tables(display, mtl_c20_hdmi_tables); +} diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index ae98ac23ea22..347fdbc0af73 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -77,6 +77,7 @@ bool intel_mtl_tbt_pll_readout_hw_state(struct intel_display *display, struct intel_dpll_hw_state *hw_state); int intel_mtl_tbt_calc_port_clock(struct intel_encoder *encoder); +void intel_cx0pll_verify_plls(struct intel_display *display); void intel_cx0_pll_power_save_wa(struct intel_display *display); void intel_lnl_mac_transmit_lfps(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 9aa84a430f09..7127bc2a0898 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4613,7 +4613,7 @@ void intel_dpll_init(struct intel_display *display) dpll_mgr = &pch_pll_mgr; if (!dpll_mgr) - return; + goto out_verify; dpll_info = dpll_mgr->dpll_info; @@ -4632,6 +4632,13 @@ void intel_dpll_init(struct intel_display *display) display->dpll.mgr = dpll_mgr; display->dpll.num_dpll = i; + +out_verify: + /* + * TODO: Convert these to a KUnit test or dependent on a kconfig + * debug option. + */ + intel_cx0pll_verify_plls(display); } /** From 10d187b3560a45e6cf829a9c52ee54c6dfb42f3a Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:53 +0000 Subject: [PATCH 0046/1735] drm/i915/lt_phy: Add verification for lt phy pll dividers Add verification for lt phy pll dividers during boot. The port clock is calculated from pll dividers and compared against the requested port clock value. If there are a difference exceeding +-1 kHz an drm_warn() is thrown out to indicate possible pll divider mismatch. v2: - Move the LT_PHY_PLL_PARAMS -> LT_PHY_PLL_DP/HDMI_PARAMS change earlier. - Use tables[i].name != NULL as a terminating condition. - Use state vs. params term consistently in intel_c10pll_verify_clock() and intel_c20pll_verify_clock(). Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-13-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 2 + drivers/gpu/drm/i915/display/intel_lt_phy.c | 63 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_lt_phy.h | 1 + 3 files changed, 66 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 7127bc2a0898..f35a9252f4e1 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -38,6 +38,7 @@ #include "intel_dpll.h" #include "intel_dpll_mgr.h" #include "intel_hti.h" +#include "intel_lt_phy.h" #include "intel_mg_phy_regs.h" #include "intel_pch_refclk.h" #include "intel_step.h" @@ -4639,6 +4640,7 @@ void intel_dpll_init(struct intel_display *display) * debug option. */ intel_cx0pll_verify_plls(display); + intel_lt_phy_verify_plls(display); } /** diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 2790caba5457..dbe2b2dc9887 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -2337,3 +2337,66 @@ void intel_xe3plpd_pll_disable(struct intel_encoder *encoder) intel_lt_phy_pll_disable(encoder); } + +static void intel_lt_phy_pll_verify_clock(struct intel_display *display, + int precomputed_clock, + const char *pll_state_name, + const struct intel_lt_phy_pll_state *pll_state, + bool is_precomputed_state) +{ + struct drm_printer p; + int clock; + + clock = intel_lt_phy_calc_port_clock(display, pll_state); + + if (intel_dpll_clock_matches(clock, precomputed_clock)) + return; + + drm_warn(display->drm, + "PLL state %s (%s): clock difference too high: computed %d, pre-computed %d\n", + pll_state_name, + is_precomputed_state ? "precomputed" : "computed", + clock, precomputed_clock); + + if (!drm_debug_enabled(DRM_UT_KMS)) + return; + + p = drm_dbg_printer(display->drm, DRM_UT_KMS, NULL); + + drm_printf(&p, "PLL state %s (%s):\n", + pll_state_name, + is_precomputed_state ? "precomputed" : "computed"); + intel_lt_phy_dump_hw_state(display, pll_state); +} + +static void intel_lt_phy_pll_verify_params(struct intel_display *display, + const struct intel_lt_phy_pll_params *pll_params) +{ + struct intel_lt_phy_pll_state pll_state; + + intel_lt_phy_pll_verify_clock(display, pll_params->clock_rate, pll_params->name, pll_params->state, true); + + if (!pll_params->is_hdmi) + return; + + if (intel_lt_phy_calculate_hdmi_state(&pll_state, pll_params->clock_rate) != 0) + return; + + intel_lt_phy_pll_verify_clock(display, pll_params->clock_rate, pll_params->name, &pll_state, false); +} + +static void intel_lt_phy_pll_verify_tables(struct intel_display *display, + const struct intel_lt_phy_pll_params *tables) +{ + int i; + + for (i = 0; tables[i].name; i++) + intel_lt_phy_pll_verify_params(display, &tables[i]); +} + +void intel_lt_phy_verify_plls(struct intel_display *display) +{ + intel_lt_phy_pll_verify_tables(display, xe3plpd_lt_dp_tables); + intel_lt_phy_pll_verify_tables(display, xe3plpd_lt_edp_tables); + intel_lt_phy_pll_verify_tables(display, xe3plpd_lt_hdmi_tables); +} diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h index 22b12d2d5bb1..db905668f86d 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.h +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h @@ -41,5 +41,6 @@ intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state, void intel_xe3plpd_pll_enable(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_xe3plpd_pll_disable(struct intel_encoder *encoder); +void intel_lt_phy_verify_plls(struct intel_display *display); #endif /* __INTEL_LT_PHY_H__ */ From 4fa244583e77fba2388f05a44f400f44f79da396 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:54 +0000 Subject: [PATCH 0047/1735] drm/i915/cx0: Drop C20 25.175 MHz rate Drop C20 25.175 MHz PLL table as with these PLL dividers the port clock will be incorrectly calculated to 25.2 MHz. For 25.175 MHz rate the PLl dividers are calculated algorithmically making PLL table for this rate redundant. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-14-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 26 -------------------- 1 file changed, 26 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index ce4b7582b737..a0af7d3e87b6 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -1798,31 +1798,6 @@ static const struct intel_cx0pll_params mtl_c10_hdmi_tables[] = { {} }; -static const struct intel_c20pll_state mtl_c20_hdmi_25_175 = { - .clock = 25175, - .tx = { 0xbe88, /* tx cfg0 */ - 0x9800, /* tx cfg1 */ - 0x0000, /* tx cfg2 */ - }, - .cmn = { 0x0500, /* cmn cfg0*/ - 0x0005, /* cmn cfg1 */ - 0x0000, /* cmn cfg2 */ - 0x0000, /* cmn cfg3 */ - }, - .mpllb = { 0xa0d2, /* mpllb cfg0 */ - 0x7d80, /* mpllb cfg1 */ - 0x0906, /* mpllb cfg2 */ - 0xbe40, /* mpllb cfg3 */ - 0x0000, /* mpllb cfg4 */ - 0x0000, /* mpllb cfg5 */ - 0x0200, /* mpllb cfg6 */ - 0x0001, /* mpllb cfg7 */ - 0x0000, /* mpllb cfg8 */ - 0x0000, /* mpllb cfg9 */ - 0x0001, /* mpllb cfg10 */ - }, -}; - static const struct intel_c20pll_state mtl_c20_hdmi_27_0 = { .clock = 27000, .tx = { 0xbe88, /* tx cfg0 */ @@ -2049,7 +2024,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_1200 = { }; static const struct intel_cx0pll_params mtl_c20_hdmi_tables[] = { - C20PLL_HDMI_PARAMS(25175, mtl_c20_hdmi_25_175), C20PLL_HDMI_PARAMS(27000, mtl_c20_hdmi_27_0), C20PLL_HDMI_PARAMS(74250, mtl_c20_hdmi_74_25), C20PLL_HDMI_PARAMS(148500, mtl_c20_hdmi_148_5), From 1b85f96de24fd91274e46614c9d9d2a274dafe46 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:55 +0000 Subject: [PATCH 0048/1735] drm/i915/lt_phy: Drop 27.2 MHz rate Drop 27.2 MHz PLL table as with these PLL dividers the port clock will be incorrectly calculated to 27.0 MHz. For 27.2 MHz rate the PLl dividers are calculated algorithmically making PLL table for this rate redundant. Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-15-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_lt_phy.c | 55 --------------------- 1 file changed, 55 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index dbe2b2dc9887..a3326057449a 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -803,60 +803,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_252 = { }, }; -static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_272 = { - .clock = 27200, - .config = { - 0x84, - 0x2d, - 0x0, - }, - .addr_msb = { - 0x87, - 0x87, - 0x87, - 0x87, - 0x88, - 0x88, - 0x88, - 0x88, - 0x88, - 0x88, - 0x88, - 0x88, - 0x88, - }, - .addr_lsb = { - 0x10, - 0x0c, - 0x14, - 0xe4, - 0x0c, - 0x10, - 0x14, - 0x18, - 0x48, - 0x40, - 0x4c, - 0x24, - 0x44, - }, - .data = { - { 0x0, 0x4c, 0x2, 0x0 }, - { 0x0b, 0x15, 0x26, 0xa0 }, - { 0x60, 0x0, 0x0, 0x0 }, - { 0x8, 0x4, 0x96, 0x28 }, - { 0xfa, 0x0c, 0x84, 0x11 }, - { 0x80, 0x0f, 0xd9, 0x53 }, - { 0x86, 0x0, 0x0, 0x0 }, - { 0x1, 0xa0, 0x1, 0x0 }, - { 0x4b, 0x0, 0x0, 0x0 }, - { 0x28, 0x0, 0x0, 0x0 }, - { 0x0, 0x14, 0x2a, 0x14 }, - { 0x0, 0x0, 0x0, 0x0 }, - { 0x0, 0x0, 0x0, 0x0 }, - }, -}; - static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_742p5 = { .clock = 74250, .config = { @@ -1021,7 +967,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_5p94 = { static const struct intel_lt_phy_pll_params xe3plpd_lt_hdmi_tables[] = { LT_PHY_PLL_HDMI_PARAMS(25200, xe3plpd_lt_hdmi_252), - LT_PHY_PLL_HDMI_PARAMS(27200, xe3plpd_lt_hdmi_272), LT_PHY_PLL_HDMI_PARAMS(74250, xe3plpd_lt_hdmi_742p5), LT_PHY_PLL_HDMI_PARAMS(148500, xe3plpd_lt_hdmi_1p485), LT_PHY_PLL_HDMI_PARAMS(594000, xe3plpd_lt_hdmi_5p94), From 301929e3628bd8afc8919123e63763cefd14ce89 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Mon, 19 Jan 2026 09:37:56 +0000 Subject: [PATCH 0049/1735] drm/i915/display: Remove .clock member from eDP/DP/HDMI pll tables PLL state structure has a member .clock. This is not needed as the port clock is possible to calculate from the pll dividers. Remove the encoder from being passed to the port clock calculation function. v2: Keep the pll_state->clock assignment in intel_snps_hdmi_pll_compute_mpllb(). Signed-off-by: Mika Kahola Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260119093757.2850233-16-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 86 +------------------ drivers/gpu/drm/i915/display/intel_dpll_mgr.h | 3 - drivers/gpu/drm/i915/display/intel_lt_phy.c | 21 +---- .../drm/i915/display/intel_snps_hdmi_pll.c | 2 - 4 files changed, 3 insertions(+), 109 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index a0af7d3e87b6..4f56a370102d 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -548,7 +548,6 @@ void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, */ static const struct intel_c10pll_state mtl_c10_dp_rbr = { - .clock = 162000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0xB4, @@ -574,7 +573,6 @@ static const struct intel_c10pll_state mtl_c10_dp_rbr = { }; static const struct intel_c10pll_state mtl_c10_edp_r216 = { - .clock = 216000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0x4, @@ -600,7 +598,6 @@ static const struct intel_c10pll_state mtl_c10_edp_r216 = { }; static const struct intel_c10pll_state mtl_c10_edp_r243 = { - .clock = 243000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0x34, @@ -626,7 +623,6 @@ static const struct intel_c10pll_state mtl_c10_edp_r243 = { }; static const struct intel_c10pll_state mtl_c10_dp_hbr1 = { - .clock = 270000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0xF4, @@ -652,7 +648,6 @@ static const struct intel_c10pll_state mtl_c10_dp_hbr1 = { }; static const struct intel_c10pll_state mtl_c10_edp_r324 = { - .clock = 324000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0xB4, @@ -678,7 +673,6 @@ static const struct intel_c10pll_state mtl_c10_edp_r324 = { }; static const struct intel_c10pll_state mtl_c10_edp_r432 = { - .clock = 432000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0x4, @@ -704,7 +698,6 @@ static const struct intel_c10pll_state mtl_c10_edp_r432 = { }; static const struct intel_c10pll_state mtl_c10_dp_hbr2 = { - .clock = 540000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0xF4, @@ -730,7 +723,6 @@ static const struct intel_c10pll_state mtl_c10_dp_hbr2 = { }; static const struct intel_c10pll_state mtl_c10_edp_r675 = { - .clock = 675000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0xB4, @@ -756,7 +748,6 @@ static const struct intel_c10pll_state mtl_c10_edp_r675 = { }; static const struct intel_c10pll_state mtl_c10_dp_hbr3 = { - .clock = 810000, .tx = 0x10, .cmn = 0x21, .pll[0] = 0x34, @@ -837,7 +828,6 @@ static const struct intel_cx0pll_params mtl_c10_edp_tables[] = { /* C20 basic DP 1.4 tables */ static const struct intel_c20pll_state mtl_c20_dp_rbr = { - .clock = 162000, .tx = { 0xbe88, /* tx cfg0 */ 0x5800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -862,7 +852,6 @@ static const struct intel_c20pll_state mtl_c20_dp_rbr = { }; static const struct intel_c20pll_state mtl_c20_dp_hbr1 = { - .clock = 270000, .tx = { 0xbe88, /* tx cfg0 */ 0x4800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -887,7 +876,6 @@ static const struct intel_c20pll_state mtl_c20_dp_hbr1 = { }; static const struct intel_c20pll_state mtl_c20_dp_hbr2 = { - .clock = 540000, .tx = { 0xbe88, /* tx cfg0 */ 0x4800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -912,7 +900,6 @@ static const struct intel_c20pll_state mtl_c20_dp_hbr2 = { }; static const struct intel_c20pll_state mtl_c20_dp_hbr3 = { - .clock = 810000, .tx = { 0xbe88, /* tx cfg0 */ 0x4800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -938,7 +925,6 @@ static const struct intel_c20pll_state mtl_c20_dp_hbr3 = { /* C20 basic DP 2.0 tables */ static const struct intel_c20pll_state mtl_c20_dp_uhbr10 = { - .clock = 1000000, /* 10 Gbps */ .tx = { 0xbe21, /* tx cfg0 */ 0xe800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -962,7 +948,6 @@ static const struct intel_c20pll_state mtl_c20_dp_uhbr10 = { }; static const struct intel_c20pll_state mtl_c20_dp_uhbr13_5 = { - .clock = 1350000, /* 13.5 Gbps */ .tx = { 0xbea0, /* tx cfg0 */ 0x4800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -987,7 +972,6 @@ static const struct intel_c20pll_state mtl_c20_dp_uhbr13_5 = { }; static const struct intel_c20pll_state mtl_c20_dp_uhbr20 = { - .clock = 2000000, /* 20 Gbps */ .tx = { 0xbe20, /* tx cfg0 */ 0x4800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1026,7 +1010,6 @@ static const struct intel_cx0pll_params mtl_c20_dp_tables[] = { */ static const struct intel_c20pll_state xe2hpd_c20_edp_r216 = { - .clock = 216000, .tx = { 0xbe88, 0x4800, 0x0000, @@ -1051,7 +1034,6 @@ static const struct intel_c20pll_state xe2hpd_c20_edp_r216 = { }; static const struct intel_c20pll_state xe2hpd_c20_edp_r243 = { - .clock = 243000, .tx = { 0xbe88, 0x4800, 0x0000, @@ -1076,7 +1058,6 @@ static const struct intel_c20pll_state xe2hpd_c20_edp_r243 = { }; static const struct intel_c20pll_state xe2hpd_c20_edp_r324 = { - .clock = 324000, .tx = { 0xbe88, 0x4800, 0x0000, @@ -1101,7 +1082,6 @@ static const struct intel_c20pll_state xe2hpd_c20_edp_r324 = { }; static const struct intel_c20pll_state xe2hpd_c20_edp_r432 = { - .clock = 432000, .tx = { 0xbe88, 0x4800, 0x0000, @@ -1126,7 +1106,6 @@ static const struct intel_c20pll_state xe2hpd_c20_edp_r432 = { }; static const struct intel_c20pll_state xe2hpd_c20_edp_r675 = { - .clock = 675000, .tx = { 0xbe88, 0x4800, 0x0000, @@ -1164,7 +1143,6 @@ static const struct intel_cx0pll_params xe2hpd_c20_edp_tables[] = { }; static const struct intel_c20pll_state xe2hpd_c20_dp_uhbr13_5 = { - .clock = 1350000, /* 13.5 Gbps */ .tx = { 0xbea0, /* tx cfg0 */ 0x4800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1219,7 +1197,6 @@ static const struct intel_cx0pll_params xe3lpd_c20_dp_edp_tables[] = { */ static const struct intel_c10pll_state mtl_c10_hdmi_25_2 = { - .clock = 25200, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x4, @@ -1245,7 +1222,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_25_2 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_27_0 = { - .clock = 27000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, @@ -1271,7 +1247,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_27_0 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_74_25 = { - .clock = 74250, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, @@ -1297,7 +1272,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_74_25 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_148_5 = { - .clock = 148500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, @@ -1323,7 +1297,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_148_5 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_594 = { - .clock = 594000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, @@ -1350,7 +1323,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_594 = { /* Precomputed C10 HDMI PLL tables */ static const struct intel_c10pll_state mtl_c10_hdmi_27027 = { - .clock = 27027, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xC0, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1360,7 +1332,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_27027 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_28320 = { - .clock = 28320, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x04, .pll[1] = 0x00, .pll[2] = 0xCC, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1370,7 +1341,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_28320 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_30240 = { - .clock = 30240, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x04, .pll[1] = 0x00, .pll[2] = 0xDC, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1380,7 +1350,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_30240 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_31500 = { - .clock = 31500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x62, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1390,7 +1359,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_31500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_36000 = { - .clock = 36000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xC4, .pll[1] = 0x00, .pll[2] = 0x76, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1400,7 +1368,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_36000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_40000 = { - .clock = 40000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xB4, .pll[1] = 0x00, .pll[2] = 0x86, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1410,7 +1377,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_40000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_49500 = { - .clock = 49500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x74, .pll[1] = 0x00, .pll[2] = 0xAE, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1420,7 +1386,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_49500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_50000 = { - .clock = 50000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x74, .pll[1] = 0x00, .pll[2] = 0xB0, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1430,7 +1395,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_50000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_57284 = { - .clock = 57284, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xCE, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1440,7 +1404,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_57284 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_58000 = { - .clock = 58000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xD0, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1450,7 +1413,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_58000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_65000 = { - .clock = 65000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x66, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1460,7 +1422,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_65000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_71000 = { - .clock = 71000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x72, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1470,7 +1431,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_71000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_74176 = { - .clock = 74176, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x7A, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1480,7 +1440,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_74176 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_75000 = { - .clock = 75000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x7C, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1490,7 +1449,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_75000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_78750 = { - .clock = 78750, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xB4, .pll[1] = 0x00, .pll[2] = 0x84, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1500,7 +1458,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_78750 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_85500 = { - .clock = 85500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xB4, .pll[1] = 0x00, .pll[2] = 0x92, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1510,7 +1467,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_85500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_88750 = { - .clock = 88750, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x74, .pll[1] = 0x00, .pll[2] = 0x98, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1520,7 +1476,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_88750 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_106500 = { - .clock = 106500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xBC, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1530,7 +1485,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_106500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_108000 = { - .clock = 108000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xC0, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1540,7 +1494,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_108000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_115500 = { - .clock = 115500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xD0, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1550,7 +1503,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_115500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_119000 = { - .clock = 119000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xD6, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1560,7 +1512,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_119000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_135000 = { - .clock = 135000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x6C, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1570,7 +1521,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_135000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_138500 = { - .clock = 138500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x70, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1580,7 +1530,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_138500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_147160 = { - .clock = 147160, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x78, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1590,7 +1539,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_147160 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_148352 = { - .clock = 148352, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x7A, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1600,7 +1548,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_148352 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_154000 = { - .clock = 154000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xB4, .pll[1] = 0x00, .pll[2] = 0x80, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1610,7 +1557,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_154000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_162000 = { - .clock = 162000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xB4, .pll[1] = 0x00, .pll[2] = 0x88, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1620,7 +1566,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_162000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_167000 = { - .clock = 167000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xB4, .pll[1] = 0x00, .pll[2] = 0x8C, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1630,7 +1575,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_167000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_197802 = { - .clock = 197802, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x74, .pll[1] = 0x00, .pll[2] = 0xAE, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1640,7 +1584,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_197802 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_198000 = { - .clock = 198000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x74, .pll[1] = 0x00, .pll[2] = 0xAE, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1650,7 +1593,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_198000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_209800 = { - .clock = 209800, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xBA, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1660,7 +1602,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_209800 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_241500 = { - .clock = 241500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xDA, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1670,7 +1611,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_241500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_262750 = { - .clock = 262750, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x68, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1680,7 +1620,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_262750 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_268500 = { - .clock = 268500, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x6A, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1690,7 +1629,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_268500 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_296703 = { - .clock = 296703, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x7A, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1700,7 +1638,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_296703 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_297000 = { - .clock = 297000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x7A, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1710,7 +1647,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_297000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_319750 = { - .clock = 319750, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xB4, .pll[1] = 0x00, .pll[2] = 0x86, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1720,7 +1656,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_319750 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_497750 = { - .clock = 497750, .tx = 0x10, .cmn = 0x1, .pll[0] = 0x34, .pll[1] = 0x00, .pll[2] = 0xE2, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1730,7 +1665,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_497750 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_592000 = { - .clock = 592000, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x7A, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1740,7 +1674,6 @@ static const struct intel_c10pll_state mtl_c10_hdmi_592000 = { }; static const struct intel_c10pll_state mtl_c10_hdmi_593407 = { - .clock = 593407, .tx = 0x10, .cmn = 0x1, .pll[0] = 0xF4, .pll[1] = 0x00, .pll[2] = 0x7A, .pll[3] = 0x00, .pll[4] = 0x00, @@ -1799,7 +1732,6 @@ static const struct intel_cx0pll_params mtl_c10_hdmi_tables[] = { }; static const struct intel_c20pll_state mtl_c20_hdmi_27_0 = { - .clock = 27000, .tx = { 0xbe88, /* tx cfg0 */ 0x9800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1824,7 +1756,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_27_0 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_74_25 = { - .clock = 74250, .tx = { 0xbe88, /* tx cfg0 */ 0x9800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1849,7 +1780,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_74_25 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_148_5 = { - .clock = 148500, .tx = { 0xbe88, /* tx cfg0 */ 0x9800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1874,7 +1804,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_148_5 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_594 = { - .clock = 594000, .tx = { 0xbe88, /* tx cfg0 */ 0x9800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1899,7 +1828,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_594 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_300 = { - .clock = 300000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1924,7 +1852,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_300 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_600 = { - .clock = 600000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1949,7 +1876,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_600 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_800 = { - .clock = 800000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1974,7 +1900,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_800 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { - .clock = 1000000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -1999,7 +1924,6 @@ static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { }; static const struct intel_c20pll_state mtl_c20_hdmi_1200 = { - .clock = 1200000, .tx = { 0xbe98, /* tx cfg0 */ 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ @@ -2334,8 +2258,6 @@ static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, intel_cx0_phy_transaction_end(encoder, wakeref); - pll_state->clock = intel_c10pll_calc_port_clock(pll_state); - cx0pll_state->ssc_enabled = readout_ssc_state(encoder, true); if (cx0pll_state->ssc_enabled != intel_c10pll_ssc_enabled(pll_state)) @@ -2380,8 +2302,7 @@ static void intel_c10pll_dump_hw_state(struct drm_printer *p, unsigned int multiplier, tx_clk_div; fracen = hw_state->pll[0] & C10_PLL0_FRACEN; - drm_printf(p, "c10pll_hw_state: clock: %d, fracen: %s, ", - hw_state->clock, str_yes_no(fracen)); + drm_printf(p, "c10pll_hw_state: fracen: %s, ", str_yes_no(fracen)); if (fracen) { frac_quot = hw_state->pll[12] << 8 | hw_state->pll[11]; @@ -2486,7 +2407,6 @@ static int intel_c20_compute_hdmi_tmds_pll(struct intel_display *display, else mpllb_ana_freq_vco = MPLLB_ANA_FREQ_VCO_0; - pll_state->clock = port_clock; pll_state->tx[0] = 0xbe88; pll_state->tx[1] = intel_c20_hdmi_tmds_tx_cgf_1(display); pll_state->tx[2] = 0x0000; @@ -2838,8 +2758,6 @@ static void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, } } - pll_state->clock = intel_c20pll_calc_port_clock(pll_state); - intel_cx0_phy_transaction_end(encoder, wakeref); cx0pll_state->ssc_enabled = readout_ssc_state(encoder, intel_c20phy_use_mpllb(pll_state)); @@ -2850,7 +2768,7 @@ static void intel_c20pll_dump_hw_state(struct drm_printer *p, { int i; - drm_printf(p, "c20pll_hw_state: clock: %d\n", hw_state->clock); + drm_printf(p, "c20pll_hw_state:\n"); drm_printf(p, "tx[0] = 0x%.4x, tx[1] = 0x%.4x, tx[2] = 0x%.4x\n", hw_state->tx[0], hw_state->tx[1], hw_state->tx[2]); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index 5b71c860515f..4cc14ce5eebe 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -241,14 +241,12 @@ struct intel_mpllb_state { }; struct intel_c10pll_state { - u32 clock; /* in KHz */ u8 tx; u8 cmn; u8 pll[20]; }; struct intel_c20pll_state { - u32 clock; /* in kHz */ u16 tx[3]; u16 cmn[4]; union { @@ -274,7 +272,6 @@ struct intel_cx0pll_state { }; struct intel_lt_phy_pll_state { - u32 clock; /* in kHz */ u8 addr_msb[13]; u8 addr_lsb[13]; u8 data[13][4]; diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index a3326057449a..b4b281ef258b 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -61,7 +61,6 @@ struct lt_phy_params { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_rbr = { - .clock = 162000, .config = { 0x83, 0x2d, @@ -115,7 +114,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_rbr = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr1 = { - .clock = 270000, .config = { 0x8b, 0x2d, @@ -169,7 +167,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr1 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr2 = { - .clock = 540000, .config = { 0x93, 0x2d, @@ -223,7 +220,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr2 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr3 = { - .clock = 810000, .config = { 0x9b, 0x2d, @@ -277,7 +273,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr3 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr10 = { - .clock = 1000000, .config = { 0x43, 0x2d, @@ -331,7 +326,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr10 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr13_5 = { - .clock = 1350000, .config = { 0xcb, 0x2d, @@ -385,7 +379,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr13_5 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr20 = { - .clock = 2000000, .config = { 0x53, 0x2d, @@ -467,7 +460,6 @@ static const struct intel_lt_phy_pll_params xe3plpd_lt_dp_tables[] = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_2_16 = { - .clock = 216000, .config = { 0xa3, 0x2d, @@ -521,7 +513,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_2_16 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_2_43 = { - .clock = 243000, .config = { 0xab, 0x2d, @@ -575,7 +566,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_2_43 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_3_24 = { - .clock = 324000, .config = { 0xb3, 0x2d, @@ -629,7 +619,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_3_24 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_4_32 = { - .clock = 432000, .config = { 0xbb, 0x2d, @@ -683,7 +672,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_4_32 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_6_75 = { - .clock = 675000, .config = { 0xdb, 0x2d, @@ -750,7 +738,6 @@ static const struct intel_lt_phy_pll_params xe3plpd_lt_edp_tables[] = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_252 = { - .clock = 25200, .config = { 0x84, 0x2d, @@ -804,7 +791,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_252 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_742p5 = { - .clock = 74250, .config = { 0x84, 0x2d, @@ -858,7 +844,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_742p5 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_1p485 = { - .clock = 148500, .config = { 0x84, 0x2d, @@ -912,7 +897,6 @@ static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_1p485 = { }; static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_5p94 = { - .clock = 594000, .config = { 0x84, 0x2d, @@ -1732,7 +1716,7 @@ intel_lt_phy_calc_port_clock(struct intel_display *display, clk = intel_lt_phy_calc_hdmi_port_clock(display, lt_state); } else { drm_WARN_ON(display->drm, "Unsupported LT PHY Mode!\n"); - clk = xe3plpd_lt_hdmi_252.clock; + clk = 25200; } return clk; @@ -2192,7 +2176,6 @@ 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) { - struct intel_display *display = to_intel_display(encoder); u8 owned_lane_mask; u8 lane; struct ref_tracker *wakeref; @@ -2217,8 +2200,6 @@ void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, LT_PHY_VDR_X_DATAY(i, j)); } - pll_state->clock = - intel_lt_phy_calc_port_clock(display, &crtc_state->dpll_hw_state.ltpll); intel_lt_phy_transaction_end(encoder, wakeref); } diff --git a/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c b/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c index a201edceee10..7fe6b4a18213 100644 --- a/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c +++ b/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c @@ -332,8 +332,6 @@ void intel_snps_hdmi_pll_compute_c10pll(struct intel_c10pll_state *pll_state, u6 c10_curve_1, c10_curve_2, prescaler_divider, &pll_params); - pll_state->clock = pixel_clock; - pll_state->tx = 0x10; pll_state->cmn = 0x1; pll_state->pll[0] = REG_FIELD_PREP(C10_PLL0_DIV5CLK_EN, pll_params.mpll_div5_en) | From 9dd08fdecc0c98d6516c2d2d1fa189c1332f8dab Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Tue, 20 Jan 2026 11:06:10 +0000 Subject: [PATCH 0050/1735] drm/xe/uapi: disallow bind queue sharing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently this is very broken if someone attempts to create a bind queue and share it across multiple VMs. For example currently we assume it is safe to acquire the user VM lock to protect some of the bind queue state, but if allow sharing the bind queue with multiple VMs then this quickly breaks down. To fix this reject using a bind queue with any VM that is not the same VM that was originally passed when creating the bind queue. This a uAPI change, however this was more of an oversight on kernel side that we didn't reject this, and expectation is that userspace shouldn't be using bind queues in this way, so in theory this change should go unnoticed. Based on a patch from Matt Brost. v2 (Matt B): - Hold the vm lock over queue create, to ensure it can't be closed as we attach the user_vm to the queue. - Make sure we actually check for NULL user_vm in destruction path. v3: - Fix error path handling. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Reported-by: Thomas Hellström Signed-off-by: Matthew Auld Cc: José Roberto de Souza Cc: Matthew Brost Cc: Michal Mrozek Cc: Carl Zhang Cc: # v6.8+ Acked-by: José Roberto de Souza Reviewed-by: Matthew Brost Reviewed-by: Arvind Yadav Acked-by: Michal Mrozek Link: https://patch.msgid.link/20260120110609.77958-3-matthew.auld@intel.com --- drivers/gpu/drm/xe/xe_exec_queue.c | 32 +++++++++++++++++++++++- drivers/gpu/drm/xe/xe_exec_queue.h | 1 + drivers/gpu/drm/xe/xe_exec_queue_types.h | 6 +++++ drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 2 +- drivers/gpu/drm/xe/xe_vm.c | 7 +++++- 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index a58968a0a781..7e7e663189e4 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -412,6 +412,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe * @xe: Xe device. * @tile: tile which bind exec queue belongs to. * @flags: exec queue creation flags + * @user_vm: The user VM which this exec queue belongs to * @extensions: exec queue creation extensions * * Normalize bind exec queue creation. Bind exec queue is tied to migration VM @@ -425,6 +426,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe */ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe, struct xe_tile *tile, + struct xe_vm *user_vm, u32 flags, u64 extensions) { struct xe_gt *gt = tile->primary_gt; @@ -461,6 +463,9 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe, xe_exec_queue_put(q); return ERR_PTR(err); } + + if (user_vm) + q->user_vm = xe_vm_get(user_vm); } return q; @@ -491,6 +496,11 @@ void xe_exec_queue_destroy(struct kref *ref) xe_exec_queue_put(eq); } + if (q->user_vm) { + xe_vm_put(q->user_vm); + q->user_vm = NULL; + } + q->ops->destroy(q); } @@ -1121,6 +1131,22 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, XE_IOCTL_DBG(xe, eci[0].engine_instance != 0)) return -EINVAL; + vm = xe_vm_lookup(xef, args->vm_id); + if (XE_IOCTL_DBG(xe, !vm)) + return -ENOENT; + + err = down_read_interruptible(&vm->lock); + if (err) { + xe_vm_put(vm); + return err; + } + + if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) { + up_read(&vm->lock); + xe_vm_put(vm); + return -ENOENT; + } + for_each_tile(tile, xe, id) { struct xe_exec_queue *new; @@ -1128,9 +1154,11 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, if (id) flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD; - new = xe_exec_queue_create_bind(xe, tile, flags, + new = xe_exec_queue_create_bind(xe, tile, vm, flags, args->extensions); if (IS_ERR(new)) { + up_read(&vm->lock); + xe_vm_put(vm); err = PTR_ERR(new); if (q) goto put_exec_queue; @@ -1142,6 +1170,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, list_add_tail(&new->multi_gt_list, &q->multi_gt_link); } + up_read(&vm->lock); + xe_vm_put(vm); } else { logical_mask = calc_validate_logical_mask(xe, eci, args->width, diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h index b1e51789128f..c9e3a7c2d249 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.h +++ b/drivers/gpu/drm/xe/xe_exec_queue.h @@ -28,6 +28,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe u32 flags, u64 extensions); struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe, struct xe_tile *tile, + struct xe_vm *user_vm, u32 flags, u64 extensions); void xe_exec_queue_fini(struct xe_exec_queue *q); diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index 601e742c79ff..e987d431ce27 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -92,6 +92,12 @@ struct xe_exec_queue { struct kref refcount; /** @vm: VM (address space) for this exec queue */ struct xe_vm *vm; + /** + * @user_vm: User VM (address space) for this exec queue (bind queues + * only) + */ + struct xe_vm *user_vm; + /** @class: class of this exec queue */ enum xe_engine_class class; /** diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 052a5071e69f..db023fb66a27 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -350,7 +350,7 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe) flags = EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_PERMANENT | EXEC_QUEUE_FLAG_MIGRATE; - q = xe_exec_queue_create_bind(xe, tile, flags, 0); + q = xe_exec_queue_create_bind(xe, tile, NULL, flags, 0); if (IS_ERR(q)) { err = PTR_ERR(q); goto err_ret; diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index e330c794b626..f7bb21ac1987 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1651,7 +1651,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef) if (!vm->pt_root[id]) continue; - q = xe_exec_queue_create_bind(xe, tile, create_flags, 0); + q = xe_exec_queue_create_bind(xe, tile, vm, create_flags, 0); if (IS_ERR(q)) { err = PTR_ERR(q); goto err_close; @@ -3647,6 +3647,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) } } + if (XE_IOCTL_DBG(xe, q && vm != q->user_vm)) { + err = -EINVAL; + goto put_exec_queue; + } + /* Ensure all UNMAPs visible */ xe_svm_flush(vm); From 9dd1048bca4fe2aa67c7a286bafb3947537adedb Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Tue, 20 Jan 2026 11:06:11 +0000 Subject: [PATCH 0051/1735] drm/xe/migrate: fix job lock assert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are meant to be checking the user vm for the bind queue, but actually we are checking the migrate vm. For various reasons this is not currently firing but this will likely change in the future. Now that we have the user_vm attached to the bind queue, we can fix this by directly checking that here. Fixes: dba89840a920 ("drm/xe: Add GT TLB invalidation jobs") Signed-off-by: Matthew Auld Cc: Thomas Hellström Cc: Matthew Brost Reviewed-by: Matthew Brost Reviewed-by: Arvind Yadav Link: https://patch.msgid.link/20260120110609.77958-4-matthew.auld@intel.com --- drivers/gpu/drm/xe/xe_migrate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 00eef41a9e36..6e202428aac2 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -2499,7 +2499,7 @@ void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q) if (is_migrate) mutex_lock(&m->job_mutex); else - xe_vm_assert_held(q->vm); /* User queues VM's should be locked */ + xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */ } /** @@ -2517,7 +2517,7 @@ void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q) if (is_migrate) mutex_unlock(&m->job_mutex); else - xe_vm_assert_held(q->vm); /* User queues VM's should be locked */ + xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */ } #if IS_ENABLED(CONFIG_PROVE_LOCKING) From 6695dc279820a50cb20ecd8b5250e05234dac780 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Wed, 7 Jan 2026 16:29:36 +0000 Subject: [PATCH 0052/1735] drm/i915/display: Prevent u64 underflow in intel_fbc_stolen_end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Static analysis reveals a potential integer underflow in intel_fbc_stolen_end. This can apparently occur if intel_parent_stolen_area_size returns zero (or, theoretically, any value less than 2^23), as 2^23 is subtracted from the return value and stored in a u64. While this doesn't appear to cause any issues due to the use of the min() function to clamp the return values from the intel_fbc_stolen_end function, it would be best practice to avoid undeflowing values like this on principle. So, rework the function to prevent the underflow from occurring. Note that the underflow at present would result in the value of intel_fbc_cfb_base_max being returned at the end of intel_fbc_stolen_end, so just return that if the value of intel_parent_stolen_area_size is too small. While we're here, fix the other comments here and modify the execution path for readability. v2: (Jani) - Fix the comments in intel_fbc_stolen_end - Use check_sub_overflow - Remove macro that mirrors SZ_8M, as it is now only referenced once - Misc. formatting fixes Fixes: a9da512b3ed7 ("drm/i915: avoid the last 8mb of stolen on BDW/SKL") Signed-off-by: Jonathan Cavitt Cc: Paulo Zanoni Cc: Ville Syrjälä Cc: Daniel Vetter Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260107162935.8123-2-jonathan.cavitt@intel.com Signed-off-by: Matt Roper --- drivers/gpu/drm/i915/display/intel_fbc.c | 29 +++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index fef2f35ff1e9..1f3f5237a1c2 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -809,19 +809,32 @@ static u64 intel_fbc_cfb_base_max(struct intel_display *display) static u64 intel_fbc_stolen_end(struct intel_display *display) { - u64 end; + u64 end = intel_fbc_cfb_base_max(display); - /* The FBC hardware for BDW/SKL doesn't have access to the stolen + /* + * The FBC hardware for BDW/SKL doesn't have access to the stolen * reserved range size, so it always assumes the maximum (8mb) is used. * If we enable FBC using a CFB on that memory range we'll get FIFO - * underruns, even if that range is not reserved by the BIOS. */ + * underruns, even if that range is not reserved by the BIOS. + */ if (display->platform.broadwell || - (DISPLAY_VER(display) == 9 && !display->platform.broxton)) - end = intel_parent_stolen_area_size(display) - 8 * 1024 * 1024; - else - end = U64_MAX; + (DISPLAY_VER(display) == 9 && !display->platform.broxton)) { + u64 stolen_area_size = intel_parent_stolen_area_size(display); - return min(end, intel_fbc_cfb_base_max(display)); + /* + * If stolen_area_size is less than SZ_8M, use + * intel_fbc_cfb_base_max instead. This should not happen, + * so warn if it does. + */ + if (drm_WARN_ON(display->drm, + check_sub_overflow(stolen_area_size, + SZ_8M, &stolen_area_size))) + return end; + + return min(end, stolen_area_size); + } + + return end; } static int intel_fbc_min_limit(const struct intel_plane_state *plane_state) From 37d5f4da49821cd542561d5cfffea35d32f5bef6 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Wed, 14 Jan 2026 08:24:56 +0530 Subject: [PATCH 0053/1735] drm/i915/gvt_mmio_table: Use the gvt versions of the display macros Include gvt/display_helpers.h so that the display register macros in intel_gvt_mmio_table.c expand through the exported display functions. This lets us keep the existing macro calls while avoiding direct access to display internals, helping the display modularization work. Signed-off-by: Ankit Nautiyal Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260114025456.1639171-1-ankit.k.nautiyal@intel.com --- drivers/gpu/drm/i915/intel_gvt_mmio_table.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_gvt_mmio_table.c b/drivers/gpu/drm/i915/intel_gvt_mmio_table.c index 478d00f89a4b..052596ac83a0 100644 --- a/drivers/gpu/drm/i915/intel_gvt_mmio_table.c +++ b/drivers/gpu/drm/i915/intel_gvt_mmio_table.c @@ -11,12 +11,12 @@ #include "display/intel_color_regs.h" #include "display/intel_crt_regs.h" #include "display/intel_cursor_regs.h" -#include "display/intel_display_core.h" #include "display/intel_display_regs.h" #include "display/intel_display_types.h" #include "display/intel_dmc_regs.h" #include "display/intel_dp_aux_regs.h" #include "display/intel_dpio_phy.h" +#include "display/intel_fbc.h" #include "display/intel_fbc_regs.h" #include "display/intel_fdi_regs.h" #include "display/intel_lvds_regs.h" @@ -32,6 +32,7 @@ #include "gt/intel_engine_regs.h" #include "gt/intel_gt_regs.h" +#include "gvt/display_helpers.h" #include "gvt/reg.h" #include "i915_drv.h" From 1e372b246199ca7a35f930177fea91b557dac16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Wed, 21 Jan 2026 10:10:47 +0100 Subject: [PATCH 0054/1735] drm, drm/xe: Fix xe userptr in the absence of CONFIG_DEVICE_PRIVATE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_DEVICE_PRIVATE is not selected by default by some distros, for example Fedora, and that leads to a regression in the xe driver since userptr support gets compiled out. It turns out that DRM_GPUSVM, which is needed for xe userptr support compiles also without CONFIG_DEVICE_PRIVATE, but doesn't compile without CONFIG_ZONE_DEVICE. Exclude the drm_pagemap files from compilation with !CONFIG_ZONE_DEVICE, and remove the CONFIG_DEVICE_PRIVATE dependency from CONFIG_DRM_GPUSVM and the xe driver's selection of it, re-enabling xe userptr for those configs. v2: - Don't compile the drm_pagemap files unless CONFIG_ZONE_DEVICE is set. - Adjust the drm_pagemap.h header accordingly. Fixes: 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") Cc: Matthew Auld Cc: Himal Prasad Ghimiray Cc: Thomas Hellström Cc: Matthew Brost Cc: "Thomas Hellström" Cc: Rodrigo Vivi Cc: dri-devel@lists.freedesktop.org Cc: # v6.18+ Signed-off-by: Thomas Hellström Reviewed-by: Matthew Auld Acked-by: Maarten Lankhorst Link: https://patch.msgid.link/20260121091048.41371-2-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/Kconfig | 2 +- drivers/gpu/drm/Makefile | 4 +++- drivers/gpu/drm/xe/Kconfig | 2 +- include/drm/drm_pagemap.h | 18 ++++++++++++++---- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index a33b90251530..d3d52310c9cc 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -210,7 +210,7 @@ config DRM_GPUVM config DRM_GPUSVM tristate - depends on DRM && DEVICE_PRIVATE + depends on DRM select HMM_MIRROR select MMU_NOTIFIER help diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0deee72ef935..0c21029c446f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -108,9 +108,11 @@ obj-$(CONFIG_DRM_EXEC) += drm_exec.o obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o drm_gpusvm_helper-y := \ - drm_gpusvm.o\ + drm_gpusvm.o +drm_gpusvm_helper-$(CONFIG_ZONE_DEVICE) += \ drm_pagemap.o\ drm_pagemap_util.o + obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm_helper.o obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig index 4b288eb3f5b0..c34be1be155b 100644 --- a/drivers/gpu/drm/xe/Kconfig +++ b/drivers/gpu/drm/xe/Kconfig @@ -39,7 +39,7 @@ config DRM_XE select DRM_TTM select DRM_TTM_HELPER select DRM_EXEC - select DRM_GPUSVM if !UML && DEVICE_PRIVATE + select DRM_GPUSVM if !UML select DRM_GPUVM select DRM_SCHED select MMU_NOTIFIER diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h index 46e9c58f09e0..2baf0861f78f 100644 --- a/include/drm/drm_pagemap.h +++ b/include/drm/drm_pagemap.h @@ -243,6 +243,8 @@ struct drm_pagemap_devmem_ops { struct dma_fence *pre_migrate_fence); }; +#if IS_ENABLED(CONFIG_ZONE_DEVICE) + int drm_pagemap_init(struct drm_pagemap *dpagemap, struct dev_pagemap *pagemap, struct drm_device *drm, @@ -252,17 +254,22 @@ struct drm_pagemap *drm_pagemap_create(struct drm_device *drm, struct dev_pagemap *pagemap, const struct drm_pagemap_ops *ops); -#if IS_ENABLED(CONFIG_DRM_GPUSVM) +struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page); void drm_pagemap_put(struct drm_pagemap *dpagemap); #else +static inline struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page) +{ + return NULL; +} + static inline void drm_pagemap_put(struct drm_pagemap *dpagemap) { } -#endif /* IS_ENABLED(CONFIG_DRM_GPUSVM) */ +#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */ /** * drm_pagemap_get() - Obtain a reference on a struct drm_pagemap @@ -334,6 +341,8 @@ struct drm_pagemap_migrate_details { u32 source_peer_migrates : 1; }; +#if IS_ENABLED(CONFIG_ZONE_DEVICE) + int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, struct mm_struct *mm, unsigned long start, unsigned long end, @@ -343,8 +352,6 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation); const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void); -struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page); - void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation, struct device *dev, struct mm_struct *mm, const struct drm_pagemap_devmem_ops *ops, @@ -359,4 +366,7 @@ int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, void drm_pagemap_destroy(struct drm_pagemap *dpagemap, bool is_atomic_or_reclaim); int drm_pagemap_reinit(struct drm_pagemap *dpagemap); + +#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */ + #endif From 9386f49316074d2d76fd78d6bd359996de42fb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Wed, 21 Jan 2026 10:10:48 +0100 Subject: [PATCH 0055/1735] drm/xe: Select CONFIG_DEVICE_PRIVATE when DRM_XE_GPUSVM is selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_DEVICE_PRIVATE is a prerequisite for DRM_XE_GPUSVM. Explicitly select it so that DRM_XE_GPUSVM is not unintentionally left out from distro configs not explicitly enabling CONFIG_DEVICE_PRIVATE. v2: - Select also CONFIG_ZONE_DEVICE since it's needed by CONFIG_DEVICE_PRIVATE. v3: - Depend on CONFIG_ZONE_DEVICE rather than selecting it. Cc: Matthew Auld Cc: Matthew Brost Cc: Rodrigo Vivi Cc: Signed-off-by: Thomas Hellström Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260121091048.41371-3-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/xe/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig index c34be1be155b..4d7dcaff2b91 100644 --- a/drivers/gpu/drm/xe/Kconfig +++ b/drivers/gpu/drm/xe/Kconfig @@ -80,8 +80,9 @@ config DRM_XE_GPUSVM bool "Enable CPU to GPU address mirroring" depends on DRM_XE depends on !UML - depends on DEVICE_PRIVATE + depends on ZONE_DEVICE default y + select DEVICE_PRIVATE select DRM_GPUSVM help Enable this option if you want support for CPU to GPU address From dc2fc00ba94dee539593228a5e188c8e6a84ba47 Mon Sep 17 00:00:00 2001 From: Sanjay Yadav Date: Wed, 21 Jan 2026 16:44:17 +0530 Subject: [PATCH 0056/1735] drm/xe: Use DRM_BUDDY_CONTIGUOUS_ALLOCATION for contiguous allocations The VRAM/stolen memory managers do not currently set DRM_BUDDY_CONTIGUOUS_ALLOCATION for contiguous allocations. Enabling this flag activates the buddy allocator's try_harder path, which helps handle fragmented memory scenarios. This enables the __alloc_contig_try_harder fallback in the buddy allocator, allowing contiguous allocation requests to succeed even when memory is fragmented by combining allocations from both(RHS and LHS) sides of a large free block. v2: (Matt B) - Remove redundant logic for rounding allocation size and trimming when TTM_PL_FLAG_CONTIGUOUS is set, since drm_buddy now handles this when DRM_BUDDY_CONTIGUOUS_ALLOCATION is enabled Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6713 Suggested-by: Matthew Auld Signed-off-by: Sanjay Yadav Reviewed-by: Matthew Brost Reviewed-by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260121111416.3104399-2-sanjay.kumar.yadav@intel.com --- drivers/gpu/drm/xe/xe_ttm_vram_mgr.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c index 6553a19f7cf2..d6aa61e55f4d 100644 --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c @@ -81,6 +81,9 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man, if (place->flags & TTM_PL_FLAG_TOPDOWN) vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + if (place->flags & TTM_PL_FLAG_CONTIGUOUS) + vres->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION; + if (place->fpfn || lpfn != man->size >> PAGE_SHIFT) vres->flags |= DRM_BUDDY_RANGE_ALLOCATION; @@ -110,25 +113,12 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man, goto error_unlock; } - if (place->fpfn + (size >> PAGE_SHIFT) != lpfn && - place->flags & TTM_PL_FLAG_CONTIGUOUS) { - size = roundup_pow_of_two(size); - min_page_size = size; - - lpfn = max_t(unsigned long, place->fpfn + (size >> PAGE_SHIFT), lpfn); - } - err = drm_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT, (u64)lpfn << PAGE_SHIFT, size, min_page_size, &vres->blocks, vres->flags); if (err) goto error_unlock; - if (place->flags & TTM_PL_FLAG_CONTIGUOUS) { - if (!drm_buddy_block_trim(mm, NULL, vres->base.size, &vres->blocks)) - size = vres->base.size; - } - if (lpfn <= mgr->visible_size >> PAGE_SHIFT) { vres->used_visible_size = size; } else { From 0b2d86670a8438f6e98266a86ab5f407c6e2b0b6 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Fri, 16 Jan 2026 13:57:30 +0100 Subject: [PATCH 0057/1735] drm/panthor: Rework panthor_irq::suspended into panthor_irq::state To deal with the threaded interrupt handler and a suspend action overlapping, the boolean panthor_irq::suspended is not sufficient. Rework it into taking several different values depending on the current state, and check it and set it within the IRQ helper functions. Co-developed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Nicolas Frattaroli Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260116-panthor-tracepoints-v10-1-d925986e3d1b@collabora.com --- drivers/gpu/drm/panthor/panthor_device.h | 35 +++++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index f35e52b9546a..8597b388cc40 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -61,6 +61,17 @@ enum panthor_device_pm_state { PANTHOR_DEVICE_PM_STATE_SUSPENDING, }; +enum panthor_irq_state { + /** @PANTHOR_IRQ_STATE_ACTIVE: IRQ is active and ready to process events. */ + PANTHOR_IRQ_STATE_ACTIVE = 0, + /** @PANTHOR_IRQ_STATE_PROCESSING: IRQ is currently processing events. */ + PANTHOR_IRQ_STATE_PROCESSING, + /** @PANTHOR_IRQ_STATE_SUSPENDED: IRQ is suspended. */ + PANTHOR_IRQ_STATE_SUSPENDED, + /** @PANTHOR_IRQ_STATE_SUSPENDING: IRQ is being suspended. */ + PANTHOR_IRQ_STATE_SUSPENDING, +}; + /** * struct panthor_irq - IRQ data * @@ -76,8 +87,8 @@ struct panthor_irq { /** @mask: Current mask being applied to xxx_INT_MASK. */ u32 mask; - /** @suspended: Set to true when the IRQ is suspended. */ - atomic_t suspended; + /** @state: one of &enum panthor_irq_state reflecting the current state. */ + atomic_t state; }; /** @@ -409,12 +420,17 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) { \ struct panthor_irq *pirq = data; \ struct panthor_device *ptdev = pirq->ptdev; \ + enum panthor_irq_state old_state; \ \ - if (atomic_read(&pirq->suspended)) \ - return IRQ_NONE; \ if (!gpu_read(ptdev, __reg_prefix ## _INT_STAT)) \ return IRQ_NONE; \ \ + old_state = atomic_cmpxchg(&pirq->state, \ + PANTHOR_IRQ_STATE_ACTIVE, \ + PANTHOR_IRQ_STATE_PROCESSING); \ + if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \ + return IRQ_NONE; \ + \ gpu_write(ptdev, __reg_prefix ## _INT_MASK, 0); \ return IRQ_WAKE_THREAD; \ } \ @@ -423,6 +439,7 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da { \ struct panthor_irq *pirq = data; \ struct panthor_device *ptdev = pirq->ptdev; \ + enum panthor_irq_state old_state; \ irqreturn_t ret = IRQ_NONE; \ \ while (true) { \ @@ -435,7 +452,10 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da ret = IRQ_HANDLED; \ } \ \ - if (!atomic_read(&pirq->suspended)) \ + old_state = atomic_cmpxchg(&pirq->state, \ + PANTHOR_IRQ_STATE_PROCESSING, \ + PANTHOR_IRQ_STATE_ACTIVE); \ + if (old_state == PANTHOR_IRQ_STATE_PROCESSING) \ gpu_write(ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ \ return ret; \ @@ -445,14 +465,15 @@ static inline void panthor_ ## __name ## _irq_suspend(struct panthor_irq *pirq) { \ pirq->mask = 0; \ gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, 0); \ + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); \ synchronize_irq(pirq->irq); \ - atomic_set(&pirq->suspended, true); \ + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ } \ \ static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq, u32 mask) \ { \ - atomic_set(&pirq->suspended, false); \ pirq->mask = mask; \ + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE); \ gpu_write(pirq->ptdev, __reg_prefix ## _INT_CLEAR, mask); \ gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, mask); \ } \ From c5bf1d4e4473f0f18dfba0266a8fd48cb3700e73 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Fri, 16 Jan 2026 13:57:31 +0100 Subject: [PATCH 0058/1735] drm/panthor: Extend IRQ helpers for mask modification/restoration The current IRQ helpers do not guarantee mutual exclusion that covers the entire transaction from accessing the mask member and modifying the mask register. This makes it hard, if not impossible, to implement mask modification helpers that may change one of these outside the normal suspend/resume/isr code paths. Add a spinlock to struct panthor_irq that protects both the mask member and register. Acquire it in all code paths that access these, but drop it before processing the threaded handler function. Then, add the aforementioned new helpers: enable_events, and disable_events. They work by ORing and NANDing the mask bits. resume is changed to no longer have a mask passed, as pirq->mask is supposed to be the user-requested mask now, rather than a mirror of the INT_MASK register contents. Users of the resume helper are adjusted accordingly, including a rather painful refactor in panthor_mmu.c. In panthor_mmu.c, the bespoke mask modification is excised, and replaced with enable_events/disable_events in as_enable/as_disable. Co-developed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Nicolas Frattaroli Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260116-panthor-tracepoints-v10-2-d925986e3d1b@collabora.com --- drivers/gpu/drm/panthor/panthor_device.h | 84 +++++++++++++++++++----- drivers/gpu/drm/panthor/panthor_fw.c | 3 +- drivers/gpu/drm/panthor/panthor_gpu.c | 2 +- drivers/gpu/drm/panthor/panthor_mmu.c | 47 ++++++------- drivers/gpu/drm/panthor/panthor_pwr.c | 2 +- 5 files changed, 97 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 8597b388cc40..b6696f73a536 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -84,9 +84,19 @@ struct panthor_irq { /** @irq: IRQ number. */ int irq; - /** @mask: Current mask being applied to xxx_INT_MASK. */ + /** @mask: Values to write to xxx_INT_MASK if active. */ u32 mask; + /** + * @mask_lock: protects modifications to _INT_MASK and @mask. + * + * In paths where _INT_MASK is updated based on a state + * transition/check, it's crucial for the state update/check to be + * inside the locked section, otherwise it introduces a race window + * leading to potential _INT_MASK inconsistencies. + */ + spinlock_t mask_lock; + /** @state: one of &enum panthor_irq_state reflecting the current state. */ atomic_t state; }; @@ -425,6 +435,7 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) if (!gpu_read(ptdev, __reg_prefix ## _INT_STAT)) \ return IRQ_NONE; \ \ + guard(spinlock_irqsave)(&pirq->mask_lock); \ old_state = atomic_cmpxchg(&pirq->state, \ PANTHOR_IRQ_STATE_ACTIVE, \ PANTHOR_IRQ_STATE_PROCESSING); \ @@ -439,10 +450,17 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da { \ struct panthor_irq *pirq = data; \ struct panthor_device *ptdev = pirq->ptdev; \ - enum panthor_irq_state old_state; \ irqreturn_t ret = IRQ_NONE; \ \ while (true) { \ + /* It's safe to access pirq->mask without the lock held here. If a new \ + * event gets added to the mask and the corresponding IRQ is pending, \ + * we'll process it right away instead of adding an extra raw -> threaded \ + * round trip. If an event is removed and the status bit is set, it will \ + * be ignored, just like it would have been if the mask had been adjusted \ + * right before the HW event kicks in. TLDR; it's all expected races we're \ + * covered for. \ + */ \ u32 status = gpu_read(ptdev, __reg_prefix ## _INT_RAWSTAT) & pirq->mask; \ \ if (!status) \ @@ -452,30 +470,36 @@ static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *da ret = IRQ_HANDLED; \ } \ \ - old_state = atomic_cmpxchg(&pirq->state, \ - PANTHOR_IRQ_STATE_PROCESSING, \ - PANTHOR_IRQ_STATE_ACTIVE); \ - if (old_state == PANTHOR_IRQ_STATE_PROCESSING) \ - gpu_write(ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ + scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \ + enum panthor_irq_state old_state; \ + \ + old_state = atomic_cmpxchg(&pirq->state, \ + PANTHOR_IRQ_STATE_PROCESSING, \ + PANTHOR_IRQ_STATE_ACTIVE); \ + if (old_state == PANTHOR_IRQ_STATE_PROCESSING) \ + gpu_write(ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ + } \ \ return ret; \ } \ \ static inline void panthor_ ## __name ## _irq_suspend(struct panthor_irq *pirq) \ { \ - pirq->mask = 0; \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, 0); \ - atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); \ + scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \ + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); \ + gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, 0); \ + } \ synchronize_irq(pirq->irq); \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ } \ \ -static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq, u32 mask) \ +static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq) \ { \ - pirq->mask = mask; \ + guard(spinlock_irqsave)(&pirq->mask_lock); \ + \ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE); \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_CLEAR, mask); \ - gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, mask); \ + gpu_write(pirq->ptdev, __reg_prefix ## _INT_CLEAR, pirq->mask); \ + gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ } \ \ static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ @@ -484,13 +508,43 @@ static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ { \ pirq->ptdev = ptdev; \ pirq->irq = irq; \ - panthor_ ## __name ## _irq_resume(pirq, mask); \ + pirq->mask = mask; \ + spin_lock_init(&pirq->mask_lock); \ + panthor_ ## __name ## _irq_resume(pirq); \ \ return devm_request_threaded_irq(ptdev->base.dev, irq, \ panthor_ ## __name ## _irq_raw_handler, \ panthor_ ## __name ## _irq_threaded_handler, \ IRQF_SHARED, KBUILD_MODNAME "-" # __name, \ pirq); \ +} \ + \ +static inline void panthor_ ## __name ## _irq_enable_events(struct panthor_irq *pirq, u32 mask) \ +{ \ + guard(spinlock_irqsave)(&pirq->mask_lock); \ + pirq->mask |= mask; \ + \ + /* The only situation where we need to write the new mask is if the IRQ is active. \ + * If it's being processed, the mask will be restored for us in _irq_threaded_handler() \ + * on the PROCESSING -> ACTIVE transition. \ + * If the IRQ is suspended/suspending, the mask is restored at resume time. \ + */ \ + if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ + gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ +} \ + \ +static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq *pirq, u32 mask)\ +{ \ + guard(spinlock_irqsave)(&pirq->mask_lock); \ + pirq->mask &= ~mask; \ + \ + /* The only situation where we need to write the new mask is if the IRQ is active. \ + * If it's being processed, the mask will be restored for us in _irq_threaded_handler() \ + * on the PROCESSING -> ACTIVE transition. \ + * If the IRQ is suspended/suspending, the mask is restored at resume time. \ + */ \ + if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \ + gpu_write(pirq->ptdev, __reg_prefix ## _INT_MASK, pirq->mask); \ } extern struct workqueue_struct *panthor_cleanup_wq; diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index a64ec8756bed..0e46625f7621 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1080,7 +1080,8 @@ static int panthor_fw_start(struct panthor_device *ptdev) bool timedout = false; ptdev->fw->booted = false; - panthor_job_irq_resume(&ptdev->fw->irq, ~0); + panthor_job_irq_enable_events(&ptdev->fw->irq, ~0); + panthor_job_irq_resume(&ptdev->fw->irq); gpu_write(ptdev, MCU_CONTROL, MCU_CONTROL_AUTO); if (!wait_event_timeout(ptdev->fw->req_waitqueue, diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index 057e167468d0..9304469a711a 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -395,7 +395,7 @@ void panthor_gpu_suspend(struct panthor_device *ptdev) */ void panthor_gpu_resume(struct panthor_device *ptdev) { - panthor_gpu_irq_resume(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK); + panthor_gpu_irq_resume(&ptdev->gpu->irq); panthor_hw_l2_power_on(ptdev); } diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index b888fff05efe..912c833b4980 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -562,9 +562,21 @@ static u64 pack_region_range(struct panthor_device *ptdev, u64 *region_start, u6 return region_width | *region_start; } +static u32 panthor_mmu_as_fault_mask(struct panthor_device *ptdev, u32 as) +{ + return BIT(as); +} + +/* Forward declaration to call helpers within as_enable/disable */ +static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status); +PANTHOR_IRQ_HANDLER(mmu, MMU, panthor_mmu_irq_handler); + static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr, u64 transtab, u64 transcfg, u64 memattr) { + panthor_mmu_irq_enable_events(&ptdev->mmu->irq, + panthor_mmu_as_fault_mask(ptdev, as_nr)); + gpu_write64(ptdev, AS_TRANSTAB(as_nr), transtab); gpu_write64(ptdev, AS_MEMATTR(as_nr), memattr); gpu_write64(ptdev, AS_TRANSCFG(as_nr), transcfg); @@ -580,6 +592,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr, lockdep_assert_held(&ptdev->mmu->as.slots_lock); + panthor_mmu_irq_disable_events(&ptdev->mmu->irq, + panthor_mmu_as_fault_mask(ptdev, as_nr)); + /* Flush+invalidate RW caches, invalidate RO ones. */ ret = panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV, CACHE_CLEAN | CACHE_INV, CACHE_INV); @@ -612,11 +627,6 @@ static u32 panthor_mmu_fault_mask(struct panthor_device *ptdev, u32 value) return value & GENMASK(15, 0); } -static u32 panthor_mmu_as_fault_mask(struct panthor_device *ptdev, u32 as) -{ - return BIT(as); -} - /** * panthor_vm_has_unhandled_faults() - Check if a VM has unhandled faults * @vm: VM to check. @@ -670,6 +680,7 @@ int panthor_vm_active(struct panthor_vm *vm) struct io_pgtable_cfg *cfg = &io_pgtable_ops_to_pgtable(vm->pgtbl_ops)->cfg; int ret = 0, as, cookie; u64 transtab, transcfg; + u32 fault_mask; if (!drm_dev_enter(&ptdev->base, &cookie)) return -ENODEV; @@ -743,14 +754,13 @@ int panthor_vm_active(struct panthor_vm *vm) /* If the VM is re-activated, we clear the fault. */ vm->unhandled_fault = false; - /* Unhandled pagefault on this AS, clear the fault and re-enable interrupts - * before enabling the AS. + /* Unhandled pagefault on this AS, clear the fault and enable the AS, + * which re-enables interrupts. */ - if (ptdev->mmu->as.faulty_mask & panthor_mmu_as_fault_mask(ptdev, as)) { - gpu_write(ptdev, MMU_INT_CLEAR, panthor_mmu_as_fault_mask(ptdev, as)); - ptdev->mmu->as.faulty_mask &= ~panthor_mmu_as_fault_mask(ptdev, as); - ptdev->mmu->irq.mask |= panthor_mmu_as_fault_mask(ptdev, as); - gpu_write(ptdev, MMU_INT_MASK, ~ptdev->mmu->as.faulty_mask); + fault_mask = panthor_mmu_as_fault_mask(ptdev, as); + if (ptdev->mmu->as.faulty_mask & fault_mask) { + gpu_write(ptdev, MMU_INT_CLEAR, fault_mask); + ptdev->mmu->as.faulty_mask &= ~fault_mask; } /* The VM update is guarded by ::op_lock, which we take at the beginning @@ -1708,7 +1718,6 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) while (status) { u32 as = ffs(status | (status >> 16)) - 1; u32 mask = panthor_mmu_as_fault_mask(ptdev, as); - u32 new_int_mask; u64 addr; u32 fault_status; u32 exception_type; @@ -1726,8 +1735,6 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) mutex_lock(&ptdev->mmu->as.slots_lock); ptdev->mmu->as.faulty_mask |= mask; - new_int_mask = - panthor_mmu_fault_mask(ptdev, ~ptdev->mmu->as.faulty_mask); /* terminal fault, print info about the fault */ drm_err(&ptdev->base, @@ -1751,11 +1758,6 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) */ gpu_write(ptdev, MMU_INT_CLEAR, mask); - /* Ignore MMU interrupts on this AS until it's been - * re-enabled. - */ - ptdev->mmu->irq.mask = new_int_mask; - if (ptdev->mmu->as.slots[as].vm) ptdev->mmu->as.slots[as].vm->unhandled_fault = true; @@ -1770,7 +1772,6 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) if (has_unhandled_faults) panthor_sched_report_mmu_fault(ptdev); } -PANTHOR_IRQ_HANDLER(mmu, MMU, panthor_mmu_irq_handler); /** * panthor_mmu_suspend() - Suspend the MMU logic @@ -1815,7 +1816,7 @@ void panthor_mmu_resume(struct panthor_device *ptdev) ptdev->mmu->as.faulty_mask = 0; mutex_unlock(&ptdev->mmu->as.slots_lock); - panthor_mmu_irq_resume(&ptdev->mmu->irq, panthor_mmu_fault_mask(ptdev, ~0)); + panthor_mmu_irq_resume(&ptdev->mmu->irq); } /** @@ -1869,7 +1870,7 @@ void panthor_mmu_post_reset(struct panthor_device *ptdev) mutex_unlock(&ptdev->mmu->as.slots_lock); - panthor_mmu_irq_resume(&ptdev->mmu->irq, panthor_mmu_fault_mask(ptdev, ~0)); + panthor_mmu_irq_resume(&ptdev->mmu->irq); /* Restart the VM_BIND queues. */ mutex_lock(&ptdev->mmu->vm.lock); diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index 57cfc7ce715b..ed3b2b4479ca 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -545,5 +545,5 @@ void panthor_pwr_resume(struct panthor_device *ptdev) if (!ptdev->pwr) return; - panthor_pwr_irq_resume(&ptdev->pwr->irq, PWR_INTERRUPTS_MASK); + panthor_pwr_irq_resume(&ptdev->pwr->irq); } From 52ebfd8d2feb1f37bc75c6b662b620323de676ea Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Fri, 16 Jan 2026 13:57:32 +0100 Subject: [PATCH 0059/1735] drm/panthor: Add tracepoint for hardware utilisation changes Mali GPUs have three registers that indicate which parts of the hardware are powered at any moment. These take the form of bitmaps. In the case of SHADER_READY for example, a high bit indicates that the shader core corresponding to that bit index is powered on. These bitmaps aren't solely contiguous bits, as it's common to have holes in the sequence of shader core indices, and the actual set of which cores are present is defined by the "shader present" register. When the GPU finishes a power state transition, it fires a GPU_IRQ_POWER_CHANGED_ALL interrupt. After such an interrupt is received, the _READY registers will contain new interesting data. During power transitions, the GPU_IRQ_POWER_CHANGED interrupt will fire, and the registers will likewise contain potentially changed data. This is not to be confused with the PWR_IRQ_POWER_CHANGED_ALL interrupt, which is something related to Mali v14+'s power control logic. The _READY registers and corresponding interrupts are already available in v9 and onwards. Expose the data as a tracepoint to userspace. This allows users to debug various scenarios and gather interesting information, such as: knowing how much hardware is lit up at any given time, correlating graphics corruption with a specific powered shader core, measuring when hardware is allowed to go to a powered off state again, and so on. The registration/unregistration functions for the tracepoint go through a wrapper in panthor_hw.c, so that v14+ can implement the same tracepoint by adding its hardware specific IRQ on/off callbacks to the panthor_hw.ops member. Signed-off-by: Nicolas Frattaroli Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260116-panthor-tracepoints-v10-3-d925986e3d1b@collabora.com Signed-off-by: Boris Brezillon --- drivers/gpu/drm/panthor/panthor_gpu.c | 28 +++++++++++ drivers/gpu/drm/panthor/panthor_gpu.h | 2 + drivers/gpu/drm/panthor/panthor_hw.c | 62 +++++++++++++++++++++++++ drivers/gpu/drm/panthor/panthor_hw.h | 8 ++++ drivers/gpu/drm/panthor/panthor_trace.h | 58 +++++++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 drivers/gpu/drm/panthor/panthor_trace.h diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index 9304469a711a..2ab444ee8c71 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -22,6 +22,9 @@ #include "panthor_hw.h" #include "panthor_regs.h" +#define CREATE_TRACE_POINTS +#include "panthor_trace.h" + /** * struct panthor_gpu - GPU block management data. */ @@ -48,6 +51,9 @@ struct panthor_gpu { GPU_IRQ_RESET_COMPLETED | \ GPU_IRQ_CLEAN_CACHES_COMPLETED) +#define GPU_POWER_INTERRUPTS_MASK \ + (GPU_IRQ_POWER_CHANGED | GPU_IRQ_POWER_CHANGED_ALL) + static void panthor_gpu_coherency_set(struct panthor_device *ptdev) { gpu_write(ptdev, GPU_COHERENCY_PROTOCOL, @@ -80,6 +86,12 @@ static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) { gpu_write(ptdev, GPU_INT_CLEAR, status); + if (tracepoint_enabled(gpu_power_status) && (status & GPU_POWER_INTERRUPTS_MASK)) + trace_gpu_power_status(ptdev->base.dev, + gpu_read64(ptdev, SHADER_READY), + gpu_read64(ptdev, TILER_READY), + gpu_read64(ptdev, L2_READY)); + if (status & GPU_IRQ_FAULT) { u32 fault_status = gpu_read(ptdev, GPU_FAULT_STATUS); u64 address = gpu_read64(ptdev, GPU_FAULT_ADDR); @@ -157,6 +169,22 @@ int panthor_gpu_init(struct panthor_device *ptdev) return 0; } +int panthor_gpu_power_changed_on(struct panthor_device *ptdev) +{ + guard(pm_runtime_active)(ptdev->base.dev); + + panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK); + + return 0; +} + +void panthor_gpu_power_changed_off(struct panthor_device *ptdev) +{ + guard(pm_runtime_active)(ptdev->base.dev); + + panthor_gpu_irq_disable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK); +} + /** * panthor_gpu_block_power_off() - Power-off a specific block of the GPU * @ptdev: Device. diff --git a/drivers/gpu/drm/panthor/panthor_gpu.h b/drivers/gpu/drm/panthor/panthor_gpu.h index 12e66f48ced1..12c263a39928 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.h +++ b/drivers/gpu/drm/panthor/panthor_gpu.h @@ -51,5 +51,7 @@ int panthor_gpu_l2_power_on(struct panthor_device *ptdev); int panthor_gpu_flush_caches(struct panthor_device *ptdev, u32 l2, u32 lsc, u32 other); int panthor_gpu_soft_reset(struct panthor_device *ptdev); +void panthor_gpu_power_changed_off(struct panthor_device *ptdev); +int panthor_gpu_power_changed_on(struct panthor_device *ptdev); #endif diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c index 80c521784cd3..d135aa6724fa 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.c +++ b/drivers/gpu/drm/panthor/panthor_hw.c @@ -2,6 +2,8 @@ /* Copyright 2025 ARM Limited. All rights reserved. */ #include +#include + #include #include "panthor_device.h" @@ -30,6 +32,8 @@ static struct panthor_hw panthor_hw_arch_v10 = { .soft_reset = panthor_gpu_soft_reset, .l2_power_off = panthor_gpu_l2_power_off, .l2_power_on = panthor_gpu_l2_power_on, + .power_changed_off = panthor_gpu_power_changed_off, + .power_changed_on = panthor_gpu_power_changed_on, }, }; @@ -54,6 +58,64 @@ static struct panthor_hw_entry panthor_hw_match[] = { }, }; +static int panthor_hw_set_power_tracing(struct device *dev, void *data) +{ + struct panthor_device *ptdev = dev_get_drvdata(dev); + + if (!ptdev) + return -ENODEV; + + if (!ptdev->hw) + return 0; + + if (data) { + if (ptdev->hw->ops.power_changed_on) + return ptdev->hw->ops.power_changed_on(ptdev); + } else { + if (ptdev->hw->ops.power_changed_off) + ptdev->hw->ops.power_changed_off(ptdev); + } + + return 0; +} + +int panthor_hw_power_status_register(void) +{ + struct device_driver *drv; + int ret; + + drv = driver_find("panthor", &platform_bus_type); + if (!drv) + return -ENODEV; + + ret = driver_for_each_device(drv, NULL, (void *)true, + panthor_hw_set_power_tracing); + + return ret; +} + +void panthor_hw_power_status_unregister(void) +{ + struct device_driver *drv; + int ret; + + drv = driver_find("panthor", &platform_bus_type); + if (!drv) + return; + + ret = driver_for_each_device(drv, NULL, NULL, panthor_hw_set_power_tracing); + + /* + * Ideally, it'd be possible to ask driver_for_each_device to hand us + * another "start" to keep going after the failing device, but it + * doesn't do that. Minor inconvenience in what is probably a bad day + * on the computer already though. + */ + if (ret) + pr_warn("Couldn't mask power IRQ for at least one device: %pe\n", + ERR_PTR(ret)); +} + static char *get_gpu_model_name(struct panthor_device *ptdev) { const u32 gpu_id = ptdev->gpu_info.gpu_id; diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h index 56c68c1e9c26..2c28aea82841 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.h +++ b/drivers/gpu/drm/panthor/panthor_hw.h @@ -19,6 +19,12 @@ struct panthor_hw_ops { /** @l2_power_on: L2 power on function pointer */ int (*l2_power_on)(struct panthor_device *ptdev); + + /** @power_changed_on: Start listening to power change IRQs */ + int (*power_changed_on)(struct panthor_device *ptdev); + + /** @power_changed_off: Stop listening to power change IRQs */ + void (*power_changed_off)(struct panthor_device *ptdev); }; /** @@ -32,6 +38,8 @@ struct panthor_hw { }; int panthor_hw_init(struct panthor_device *ptdev); +int panthor_hw_power_status_register(void); +void panthor_hw_power_status_unregister(void); static inline int panthor_hw_soft_reset(struct panthor_device *ptdev) { diff --git a/drivers/gpu/drm/panthor/panthor_trace.h b/drivers/gpu/drm/panthor/panthor_trace.h new file mode 100644 index 000000000000..5bd420894745 --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_trace.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ +/* Copyright 2025 Collabora ltd. */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM panthor + +#if !defined(__PANTHOR_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __PANTHOR_TRACE_H__ + +#include +#include + +#include "panthor_hw.h" + +/** + * gpu_power_status - called whenever parts of GPU hardware are turned on or off + * @dev: pointer to the &struct device, for printing the device name + * @shader_bitmap: bitmap where a high bit indicates the shader core at a given + * bit index is on, and a low bit indicates a shader core is + * either powered off or absent + * @tiler_bitmap: bitmap where a high bit indicates the tiler unit at a given + * bit index is on, and a low bit indicates a tiler unit is + * either powered off or absent + * @l2_bitmap: bitmap where a high bit indicates the L2 cache at a given bit + * index is on, and a low bit indicates the L2 cache is either + * powered off or absent + */ +TRACE_EVENT_FN(gpu_power_status, + TP_PROTO(const struct device *dev, u64 shader_bitmap, u64 tiler_bitmap, + u64 l2_bitmap), + TP_ARGS(dev, shader_bitmap, tiler_bitmap, l2_bitmap), + TP_STRUCT__entry( + __string(dev_name, dev_name(dev)) + __field(u64, shader_bitmap) + __field(u64, tiler_bitmap) + __field(u64, l2_bitmap) + ), + TP_fast_assign( + __assign_str(dev_name); + __entry->shader_bitmap = shader_bitmap; + __entry->tiler_bitmap = tiler_bitmap; + __entry->l2_bitmap = l2_bitmap; + ), + TP_printk("%s: shader_bitmap=0x%llx tiler_bitmap=0x%llx l2_bitmap=0x%llx", + __get_str(dev_name), __entry->shader_bitmap, __entry->tiler_bitmap, + __entry->l2_bitmap + ), + panthor_hw_power_status_register, panthor_hw_power_status_unregister +); + +#endif /* __PANTHOR_TRACE_H__ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE panthor_trace + +#include From 15bd2f5d52de890f745ac0c60a44cd27d095bb0d Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Fri, 16 Jan 2026 13:57:33 +0100 Subject: [PATCH 0060/1735] drm/panthor: Add gpu_job_irq tracepoint Mali's CSF firmware triggers the job IRQ whenever there's new firmware events for processing. While this can be a global event (BIT(31) of the status register), it's usually an event relating to a command stream group (the other bit indices). Panthor throws these events onto a workqueue for processing outside the IRQ handler. It's therefore useful to have an instrumented tracepoint that goes beyond the generic IRQ tracepoint for this specific case, as it can be augmented with additional data, namely the events bit mask. This can then be used to debug problems relating to GPU jobs events not being processed quickly enough. The duration_ns field can be used to work backwards from when the tracepoint fires (at the end of the IRQ handler) to figure out when the interrupt itself landed, providing not just information on how long the work queueing took, but also when the actual interrupt itself arrived. With this information in hand, the IRQ handler itself being slow can be excluded as a possible source of problems, and attention can be directed to the workqueue processing instead. Signed-off-by: Nicolas Frattaroli Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260116-panthor-tracepoints-v10-4-d925986e3d1b@collabora.com Signed-off-by: Boris Brezillon --- drivers/gpu/drm/panthor/panthor_fw.c | 13 ++++++++++++ drivers/gpu/drm/panthor/panthor_trace.h | 28 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 0e46625f7621..5a904ca64525 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -26,6 +26,7 @@ #include "panthor_mmu.h" #include "panthor_regs.h" #include "panthor_sched.h" +#include "panthor_trace.h" #define CSF_FW_NAME "mali_csffw.bin" @@ -1060,6 +1061,12 @@ static void panthor_fw_init_global_iface(struct panthor_device *ptdev) static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status) { + u32 duration; + u64 start = 0; + + if (tracepoint_enabled(gpu_job_irq)) + start = ktime_get_ns(); + gpu_write(ptdev, JOB_INT_CLEAR, status); if (!ptdev->fw->booted && (status & JOB_INT_GLOBAL_IF)) @@ -1072,6 +1079,12 @@ static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status) return; panthor_sched_report_fw_events(ptdev, status); + + if (tracepoint_enabled(gpu_job_irq) && start) { + if (check_sub_overflow(ktime_get_ns(), start, &duration)) + duration = U32_MAX; + trace_gpu_job_irq(ptdev->base.dev, status, duration); + } } PANTHOR_IRQ_HANDLER(job, JOB, panthor_job_irq_handler); diff --git a/drivers/gpu/drm/panthor/panthor_trace.h b/drivers/gpu/drm/panthor/panthor_trace.h index 5bd420894745..6ffeb4fe6599 100644 --- a/drivers/gpu/drm/panthor/panthor_trace.h +++ b/drivers/gpu/drm/panthor/panthor_trace.h @@ -48,6 +48,34 @@ TRACE_EVENT_FN(gpu_power_status, panthor_hw_power_status_register, panthor_hw_power_status_unregister ); +/** + * gpu_job_irq - called after a job interrupt from firmware completes + * @dev: pointer to the &struct device, for printing the device name + * @events: bitmask of BIT(CSG id) | BIT(31) for a global event + * @duration_ns: Nanoseconds between job IRQ handler entry and exit + * + * The panthor_job_irq_handler() function instrumented by this tracepoint exits + * once it has queued the firmware interrupts for processing, not when the + * firmware interrupts are fully processed. This tracepoint allows for debugging + * issues with delays in the workqueue's processing of events. + */ +TRACE_EVENT(gpu_job_irq, + TP_PROTO(const struct device *dev, u32 events, u32 duration_ns), + TP_ARGS(dev, events, duration_ns), + TP_STRUCT__entry( + __string(dev_name, dev_name(dev)) + __field(u32, events) + __field(u32, duration_ns) + ), + TP_fast_assign( + __assign_str(dev_name); + __entry->events = events; + __entry->duration_ns = duration_ns; + ), + TP_printk("%s: events=0x%x duration_ns=%d", __get_str(dev_name), + __entry->events, __entry->duration_ns) +); + #endif /* __PANTHOR_TRACE_H__ */ #undef TRACE_INCLUDE_PATH From 70ea362b84449b7061cefd63a532f648d2919916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:19 +0200 Subject: [PATCH 0061/1735] drm/i915/vga: Register vgaarb client later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we register to vgaarb way too early. Thus it is possible that the entire VGA decode logic gets nuked via GMCH_CTRL before intel_vga_disable() has even disabled the VGA plane. This could even cause a system hang because we'll be unable to turn off the VGA plane gracefully. Move the vgaarb registration into intel_display_driver_register(). I suppose we could do it a bit earlier (after intel_vga_disable()), but not convinced there's any point. Also the error handling here is pointless since the registration can't fail (unless the device isn't a VGA class in which case all VGA decode logic should aleady be disabled by the BIOS via GMCH_CTRL). But let's toss in a WARN to catch any future breakage of vga_client_register(). Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- .../drm/i915/display/intel_display_driver.c | 18 +++++++----------- drivers/gpu/drm/i915/display/intel_vga.c | 7 ++----- drivers/gpu/drm/i915/display/intel_vga.h | 2 +- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 268b1de45b81..967da2b41316 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -208,16 +208,12 @@ int intel_display_driver_probe_noirq(struct intel_display *display) intel_bios_init(display); - ret = intel_vga_register(display); - if (ret) - goto cleanup_bios; - intel_psr_dc5_dc6_wa_init(display); /* FIXME: completely on the wrong abstraction layer */ ret = intel_power_domains_init(display); if (ret < 0) - goto cleanup_vga; + goto cleanup_bios; intel_pmdemand_init_early(display); @@ -229,7 +225,7 @@ int intel_display_driver_probe_noirq(struct intel_display *display) display->hotplug.dp_wq = alloc_ordered_workqueue("intel-dp", 0); if (!display->hotplug.dp_wq) { ret = -ENOMEM; - goto cleanup_vga_client_pw_domain_dmc; + goto cleanup_pw_domain_dmc; } display->wq.modeset = alloc_ordered_workqueue("i915_modeset", 0); @@ -301,11 +297,9 @@ int intel_display_driver_probe_noirq(struct intel_display *display) destroy_workqueue(display->wq.modeset); cleanup_wq_dp: destroy_workqueue(display->hotplug.dp_wq); -cleanup_vga_client_pw_domain_dmc: +cleanup_pw_domain_dmc: intel_dmc_fini(display); intel_power_domains_driver_remove(display); -cleanup_vga: - intel_vga_unregister(display); cleanup_bios: intel_bios_driver_remove(display); @@ -554,6 +548,8 @@ void intel_display_driver_register(struct intel_display *display) if (!HAS_DISPLAY(display)) return; + intel_vga_register(display); + /* Must be done after probing outputs */ intel_opregion_register(display); intel_acpi_video_register(display); @@ -646,8 +642,6 @@ void intel_display_driver_remove_nogem(struct intel_display *display) intel_power_domains_driver_remove(display); - intel_vga_unregister(display); - intel_bios_driver_remove(display); } @@ -675,6 +669,8 @@ void intel_display_driver_unregister(struct intel_display *display) acpi_video_unregister(); intel_opregion_unregister(display); + + intel_vga_unregister(display); } /* diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index c45c4bbc3f95..f13734cfd904 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -135,7 +135,7 @@ static unsigned int intel_gmch_vga_set_decode(struct pci_dev *pdev, bool enable_ return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; } -int intel_vga_register(struct intel_display *display) +void intel_vga_register(struct intel_display *display) { struct pci_dev *pdev = to_pci_dev(display->drm->dev); @@ -150,10 +150,7 @@ int intel_vga_register(struct intel_display *display) * vga_client_register() fails with -ENODEV. */ ret = vga_client_register(pdev, intel_gmch_vga_set_decode); - if (ret && ret != -ENODEV) - return ret; - - return 0; + drm_WARN_ON(display->drm, ret && ret != -ENODEV); } void intel_vga_unregister(struct intel_display *display) diff --git a/drivers/gpu/drm/i915/display/intel_vga.h b/drivers/gpu/drm/i915/display/intel_vga.h index 16d699f3b641..80084265c6cd 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.h +++ b/drivers/gpu/drm/i915/display/intel_vga.h @@ -10,7 +10,7 @@ struct intel_display; void intel_vga_reset_io_mem(struct intel_display *display); void intel_vga_disable(struct intel_display *display); -int intel_vga_register(struct intel_display *display); +void intel_vga_register(struct intel_display *display); void intel_vga_unregister(struct intel_display *display); #endif /* __INTEL_VGA_H__ */ From 4d71ff25f3a027ed3acbb186d2208113d0e1ec82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:20 +0200 Subject: [PATCH 0062/1735] drm/i915/vga: Get rid of intel_vga_reset_io_mem() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the MSR VGA register access from the power well hook, and just do it once in intel_vga_disable(). Turns out that the hardware has two levels of MDA vs. CGA decode logic: GPU level and display engine level. When we write the MSR register MDA/CGA mode selection bit both decode logics are updated accordingly, so that whichever register accessed the GPU claims will also be claimed by the display engine on the RMbus. If the two get out of sync the GPU will claim the register accesses but the display engine will not, leading to RMbus NoClaim errors. The way to get the two decode logics out of sync is by resetting the power well housing the VGA stuff, while we are in CGA mode. At that point only the display engine level decode logic will be updated with the new MSR register reset value (which selects MDA mode), and the GPU level decode logic will retain its previous state (GGA mode). To avoid the mismatch we just have to switch to MDA mode with an explicit MSR register write. Currently this is being done in a somewhat dodgy manner whenever the power well gets enabled. But doing if from the power well hook is not actually necessary since the GPU level decode logic will retain its state even when the power well is disabled. Ie. doing it just the one time is sufficient, and that can be done when we're anyway writing other VGA registers while disabling the VGA plane. See commit f9dcb0dfee98 ("drm/i915: touch VGA MSR after we enable the power well") for the original details. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-3-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- .../i915/display/intel_display_power_well.c | 3 -- drivers/gpu/drm/i915/display/intel_vga.c | 40 +++++++++---------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index db185a859133..52b20118ace6 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -204,9 +204,6 @@ int intel_power_well_refcount(struct i915_power_well *power_well) static void hsw_power_well_post_enable(struct intel_display *display, u8 irq_pipe_mask, bool has_vga) { - if (has_vga) - intel_vga_reset_io_mem(display); - if (irq_pipe_mask) gen8_irq_power_well_post_enable(display, irq_pipe_mask); } diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index f13734cfd904..39c68aec647b 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -47,8 +47,8 @@ void intel_vga_disable(struct intel_display *display) struct pci_dev *pdev = to_pci_dev(display->drm->dev); i915_reg_t vga_reg = intel_vga_cntrl_reg(display); enum pipe pipe; + u8 msr, sr1; u32 tmp; - u8 sr1; tmp = intel_de_read(display, vga_reg); if (tmp & VGA_DISP_DISABLE) @@ -66,35 +66,35 @@ void intel_vga_disable(struct intel_display *display) /* WaEnableVGAAccessThroughIOPort:ctg,elk,ilk,snb,ivb,vlv,hsw */ vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); + outb(0x01, VGA_SEQ_I); sr1 = inb(VGA_SEQ_D); outb(sr1 | VGA_SR01_SCREEN_OFF, VGA_SEQ_D); + + msr = inb(VGA_MIS_R); + /* + * VGA_MIS_COLOR controls both GPU level and display engine level + * MDA vs. CGA decode logic. But when the register gets reset + * (reset value has VGA_MIS_COLOR=0) by the power well, only the + * display engine level decode logic gets notified. + * + * Switch to MDA mode to make sure the GPU level decode logic will + * be in sync with the display engine level decode logic after the + * power well has been reset. Otherwise the GPU will claim CGA + * register accesses but the display engine will not, causing + * RMbus NoClaim errors. + */ + msr &= ~VGA_MIS_COLOR; + outb(msr, VGA_MIS_W); + vga_put(pdev, VGA_RSRC_LEGACY_IO); + udelay(300); intel_de_write(display, vga_reg, VGA_DISP_DISABLE); intel_de_posting_read(display, vga_reg); } -void intel_vga_reset_io_mem(struct intel_display *display) -{ - struct pci_dev *pdev = to_pci_dev(display->drm->dev); - - /* - * After we re-enable the power well, if we touch VGA register 0x3d5 - * we'll get unclaimed register interrupts. This stops after we write - * anything to the VGA MSR register. The vgacon module uses this - * register all the time, so if we unbind our driver and, as a - * consequence, bind vgacon, we'll get stuck in an infinite loop at - * console_unlock(). So make here we touch the VGA MSR register, making - * sure vgacon can keep working normally without triggering interrupts - * and error messages. - */ - vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); - outb(inb(VGA_MIS_R), VGA_MIS_W); - vga_put(pdev, VGA_RSRC_LEGACY_IO); -} - static int intel_gmch_vga_set_state(struct intel_display *display, bool enable_decode) { struct pci_dev *pdev = to_pci_dev(display->drm->dev); From 284c6d8043a82549ec4f3e0300ea14c53713d974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:21 +0200 Subject: [PATCH 0063/1735] drm/i915/power: Remove i915_power_well_desc::has_vga MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We no longer have any need for the has_vga flag in the display power well descriptor. Get rid of it. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-4-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- .../gpu/drm/i915/display/intel_display_power_map.c | 13 ------------- .../gpu/drm/i915/display/intel_display_power_well.c | 5 ++--- .../gpu/drm/i915/display/intel_display_power_well.h | 2 -- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power_map.c b/drivers/gpu/drm/i915/display/intel_display_power_map.c index 9b49952994ce..638d971a3a6c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_map.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_map.c @@ -112,7 +112,6 @@ static const struct i915_power_well_desc hsw_power_wells_main[] = { .id = HSW_DISP_PW_GLOBAL), ), .ops = &hsw_power_well_ops, - .has_vga = true, }, }; @@ -146,7 +145,6 @@ static const struct i915_power_well_desc bdw_power_wells_main[] = { .id = HSW_DISP_PW_GLOBAL), ), .ops = &hsw_power_well_ops, - .has_vga = true, .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), }, }; @@ -390,7 +388,6 @@ static const struct i915_power_well_desc skl_power_wells_main[] = { .id = SKL_DISP_PW_2), ), .ops = &hsw_power_well_ops, - .has_vga = true, .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), .has_fuses = true, }, { @@ -469,7 +466,6 @@ static const struct i915_power_well_desc bxt_power_wells_main[] = { .id = SKL_DISP_PW_2), ), .ops = &hsw_power_well_ops, - .has_vga = true, .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), .has_fuses = true, }, { @@ -572,7 +568,6 @@ static const struct i915_power_well_desc glk_power_wells_main[] = { .id = SKL_DISP_PW_2), ), .ops = &hsw_power_well_ops, - .has_vga = true, .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), .has_fuses = true, }, { @@ -748,7 +743,6 @@ static const struct i915_power_well_desc icl_power_wells_main[] = { .id = ICL_DISP_PW_3), ), .ops = &hsw_power_well_ops, - .has_vga = true, .irq_pipe_mask = BIT(PIPE_B), .has_fuses = true, }, { @@ -914,7 +908,6 @@ static const struct i915_power_well_desc tgl_power_wells_main[] = { .id = ICL_DISP_PW_3), ), .ops = &hsw_power_well_ops, - .has_vga = true, .irq_pipe_mask = BIT(PIPE_B), .has_fuses = true, }, { @@ -1071,7 +1064,6 @@ static const struct i915_power_well_desc rkl_power_wells_main[] = { ), .ops = &hsw_power_well_ops, .irq_pipe_mask = BIT(PIPE_B), - .has_vga = true, .has_fuses = true, }, { .instances = &I915_PW_INSTANCES( @@ -1166,7 +1158,6 @@ static const struct i915_power_well_desc dg1_power_wells_main[] = { ), .ops = &hsw_power_well_ops, .irq_pipe_mask = BIT(PIPE_B), - .has_vga = true, .has_fuses = true, }, { .instances = &I915_PW_INSTANCES( @@ -1325,7 +1316,6 @@ static const struct i915_power_well_desc xelpd_power_wells_main[] = { .id = SKL_DISP_PW_2), ), .ops = &hsw_power_well_ops, - .has_vga = true, .has_fuses = true, }, { .instances = &I915_PW_INSTANCES( @@ -1482,7 +1472,6 @@ static const struct i915_power_well_desc xelpdp_power_wells_main[] = { .id = SKL_DISP_PW_2), ), .ops = &hsw_power_well_ops, - .has_vga = true, .has_fuses = true, }, { .instances = &I915_PW_INSTANCES( @@ -1649,7 +1638,6 @@ static const struct i915_power_well_desc xe3lpd_power_wells_main[] = { .id = SKL_DISP_PW_2), ), .ops = &hsw_power_well_ops, - .has_vga = true, .has_fuses = true, }, { .instances = &I915_PW_INSTANCES( @@ -1722,7 +1710,6 @@ static const struct i915_power_well_desc wcl_power_wells_main[] = { .id = SKL_DISP_PW_2), ), .ops = &hsw_power_well_ops, - .has_vga = true, .has_fuses = true, }, { .instances = &I915_PW_INSTANCES( diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 52b20118ace6..68f293c3ac01 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -202,7 +202,7 @@ int intel_power_well_refcount(struct i915_power_well *power_well) * requesting it to be enabled. */ static void hsw_power_well_post_enable(struct intel_display *display, - u8 irq_pipe_mask, bool has_vga) + u8 irq_pipe_mask) { if (irq_pipe_mask) gen8_irq_power_well_post_enable(display, irq_pipe_mask); @@ -415,8 +415,7 @@ static void hsw_power_well_enable(struct intel_display *display, } hsw_power_well_post_enable(display, - power_well->desc->irq_pipe_mask, - power_well->desc->has_vga); + power_well->desc->irq_pipe_mask); } static void hsw_power_well_disable(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.h b/drivers/gpu/drm/i915/display/intel_display_power_well.h index ec8e508d0593..8f5524da2d06 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.h +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.h @@ -103,8 +103,6 @@ struct i915_power_well_desc { * the well enabled. */ u16 fixed_enable_delay:1; - /* The pw is backing the VGA functionality */ - u16 has_vga:1; u16 has_fuses:1; /* * The pw is for an ICL+ TypeC PHY port in From d4470842e415e8e1b170bfac98c65077babdffcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:22 +0200 Subject: [PATCH 0064/1735] drm/i915/vga: Extract intel_gmch_ctrl_reg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the GMCH_CTLR register offset determination into a helper rather than using a local varaible. I'll be needing this in another function soon. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-5-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_vga.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index 39c68aec647b..84fd5475d336 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -18,6 +18,11 @@ #include "intel_vga.h" #include "intel_vga_regs.h" +static unsigned int intel_gmch_ctrl_reg(struct intel_display *display) +{ + return DISPLAY_VER(display) >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL; +} + static i915_reg_t intel_vga_cntrl_reg(struct intel_display *display) { if (display->platform.valleyview || display->platform.cherryview) @@ -98,10 +103,10 @@ void intel_vga_disable(struct intel_display *display) static int intel_gmch_vga_set_state(struct intel_display *display, bool enable_decode) { struct pci_dev *pdev = to_pci_dev(display->drm->dev); - unsigned int reg = DISPLAY_VER(display) >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL; u16 gmch_ctrl; - if (pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0), reg, &gmch_ctrl)) { + if (pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0), + intel_gmch_ctrl_reg(display), &gmch_ctrl)) { drm_err(display->drm, "failed to read control word\n"); return -EIO; } @@ -114,7 +119,8 @@ static int intel_gmch_vga_set_state(struct intel_display *display, bool enable_d else gmch_ctrl |= INTEL_GMCH_VGA_DISABLE; - if (pci_bus_write_config_word(pdev->bus, PCI_DEVFN(0, 0), reg, gmch_ctrl)) { + if (pci_bus_write_config_word(pdev->bus, PCI_DEVFN(0, 0), + intel_gmch_ctrl_reg(display), gmch_ctrl)) { drm_err(display->drm, "failed to write control word\n"); return -EIO; } From 46ccf3fb55d5260e6e3535fec7bcb85f3e9ba667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:23 +0200 Subject: [PATCH 0065/1735] drm/i915/vga: Don't touch VGA registers if VGA decode is fully disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some systems the BIOS will disable the VGA decode logic in the iGPU (via GMCH_CTRL) when an external GPU is used as the primary VGA device. In that case the iGPU will never claim any VGA register accesses, and any access we do will in fact end up on the external GPU. Don't go poking around in the other GPUs' registers. Note that (at least on the g4x board where I tested this) the BIOS forgets to set the VGACNTR VGA_DISP_DISABLE bit, and the reset value for said bit is 0. That apparently prevents the pipes from running, so we must still remember to set the bit, despite the VGA plane was never actually enabled. On more modern platforms (hsw+ maybe?) the reset value for VGACNTR was changed to have VGA_DISP_DISABLE already set. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-6-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_vga.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index 84fd5475d336..744812260ae3 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -23,6 +23,18 @@ static unsigned int intel_gmch_ctrl_reg(struct intel_display *display) return DISPLAY_VER(display) >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL; } +static bool intel_vga_decode_is_enabled(struct intel_display *display) +{ + struct pci_dev *pdev = to_pci_dev(display->drm->dev); + u16 gmch_ctrl = 0; + + if (pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0), + intel_gmch_ctrl_reg(display), &gmch_ctrl)) + return false; + + return !(gmch_ctrl & INTEL_GMCH_VGA_DISABLE); +} + static i915_reg_t intel_vga_cntrl_reg(struct intel_display *display) { if (display->platform.valleyview || display->platform.cherryview) @@ -55,6 +67,17 @@ void intel_vga_disable(struct intel_display *display) u8 msr, sr1; u32 tmp; + if (!intel_vga_decode_is_enabled(display)) { + drm_dbg_kms(display->drm, "VGA decode is disabled\n"); + + /* + * On older hardware VGA_DISP_DISABLE defaults to 0, but + * it *must* be set or else the pipe will be completely + * stuck (at least on g4x). + */ + goto reset_vgacntr; + } + tmp = intel_de_read(display, vga_reg); if (tmp & VGA_DISP_DISABLE) return; @@ -96,6 +119,7 @@ void intel_vga_disable(struct intel_display *display) udelay(300); +reset_vgacntr: intel_de_write(display, vga_reg, VGA_DISP_DISABLE); intel_de_posting_read(display, vga_reg); } From f1640f9d7fa44b19f06aa0e9b2e99bdd8e62863a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:24 +0200 Subject: [PATCH 0066/1735] drm/i915/vga: Clean up VGA registers even if VGA plane is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out at least some systems (eg. HSW Lenovo ThinkCentre E73) configure the VGA registers even when booting in UEFI mode. So in order to avoid any issues with the MSR register we should clean up the VGA registers anyway. For now this mostly avoids the potential for unclaimed register accesses due to the power well vs. MDA/CGA selection. But this will become more important soon as we'll start to rely on the MSR register to control VGA memory decode as well. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-7-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_vga.c | 36 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index 744812260ae3..6a19fb242248 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -63,7 +63,6 @@ void intel_vga_disable(struct intel_display *display) { struct pci_dev *pdev = to_pci_dev(display->drm->dev); i915_reg_t vga_reg = intel_vga_cntrl_reg(display); - enum pipe pipe; u8 msr, sr1; u32 tmp; @@ -79,18 +78,33 @@ void intel_vga_disable(struct intel_display *display) } tmp = intel_de_read(display, vga_reg); - if (tmp & VGA_DISP_DISABLE) - return; - if (display->platform.cherryview) - pipe = REG_FIELD_GET(VGA_PIPE_SEL_MASK_CHV, tmp); - else if (has_vga_pipe_sel(display)) - pipe = REG_FIELD_GET(VGA_PIPE_SEL_MASK, tmp); - else - pipe = PIPE_A; + if ((tmp & VGA_DISP_DISABLE) == 0) { + enum pipe pipe; - drm_dbg_kms(display->drm, "Disabling VGA plane on pipe %c\n", - pipe_name(pipe)); + if (display->platform.cherryview) + pipe = REG_FIELD_GET(VGA_PIPE_SEL_MASK_CHV, tmp); + else if (has_vga_pipe_sel(display)) + pipe = REG_FIELD_GET(VGA_PIPE_SEL_MASK, tmp); + else + pipe = PIPE_A; + + drm_dbg_kms(display->drm, "Disabling VGA plane on pipe %c\n", + pipe_name(pipe)); + } else { + drm_dbg_kms(display->drm, "VGA plane is disabled\n"); + + /* + * Unfortunately at least some BIOSes (eg. HSW Lenovo + * ThinkCentre E73) set up the VGA registers even when + * in UEFI mode with the VGA plane disabled. So we need to + * always clean up the mess for iGPUs. For discrete GPUs we + * don't really care about the state of the VGA registers + * since all VGA accesses can be blocked via the bridge. + */ + if (display->platform.dgfx) + goto reset_vgacntr; + } /* WaEnableVGAAccessThroughIOPort:ctg,elk,ilk,snb,ivb,vlv,hsw */ vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); From cc6ed470caa201b25e9e7e1ca21144e17914644c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:25 +0200 Subject: [PATCH 0067/1735] drm/i915/vga: Avoid VGA arbiter during intel_vga_disable() for iGPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using the VGA arbiter during intel_vga_get() for iGPUs because that will clobber the VGA routing for whatever external GPU is the current VGA device. That will cause all reads from VGA memory to come back as 0xff/white, and thus we get a white rectangle on screen when the external GPU switches from vgacon to fbcon. The iGPU has the highest VGA decode priority so it will steal all VGA register accesses whenever its IO decoding is enabled. We'll only keep the IO decode enabled for a short time so hopefully we don't end up eating too many unrelated VGA register accesses. For discrete GPUs we need all the bridges to have their VGA forwarding bits correctly configured so we can't really avoid the VGA arbiter there. Although we only do this stuff on dGPUs when the VGA plane was actaully enabled, so the dGPU should be the current VGA device and thus have VGA routed to it already anyway. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-8-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_vga.c | 54 ++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index 6a19fb242248..a2a1c33d053e 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -58,11 +58,58 @@ static bool has_vga_pipe_sel(struct intel_display *display) return DISPLAY_VER(display) < 7; } +static bool intel_pci_set_io_decode(struct pci_dev *pdev, bool enable) +{ + u16 old = 0, cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &old); + cmd = old & ~PCI_COMMAND_IO; + if (enable) + cmd |= PCI_COMMAND_IO; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + + return old & PCI_COMMAND_IO; +} + +static bool intel_vga_get(struct intel_display *display) +{ + struct pci_dev *pdev = to_pci_dev(display->drm->dev); + + /* WaEnableVGAAccessThroughIOPort:ctg+ */ + + /* + * Bypass the VGA arbiter on the iGPU and just enable + * IO decode by hand. This avoids clobbering the VGA + * routing for an external GPU when it's the current + * VGA device, and thus prevents the all 0xff/white + * readout from VGA memory when taking over from vgacon. + * + * The iGPU has the highest VGA decode priority so it will + * grab any VGA IO access when IO decode is enabled, regardless + * of how any other VGA routing bits are configured. + */ + if (display->platform.dgfx) + vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); + + return intel_pci_set_io_decode(pdev, true); +} + +static void intel_vga_put(struct intel_display *display, bool io_decode) +{ + struct pci_dev *pdev = to_pci_dev(display->drm->dev); + + /* see intel_vga_get() */ + intel_pci_set_io_decode(pdev, io_decode); + + if (display->platform.dgfx) + vga_put(pdev, VGA_RSRC_LEGACY_IO); +} + /* Disable the VGA plane that we never use */ void intel_vga_disable(struct intel_display *display) { - struct pci_dev *pdev = to_pci_dev(display->drm->dev); i915_reg_t vga_reg = intel_vga_cntrl_reg(display); + bool io_decode; u8 msr, sr1; u32 tmp; @@ -106,8 +153,7 @@ void intel_vga_disable(struct intel_display *display) goto reset_vgacntr; } - /* WaEnableVGAAccessThroughIOPort:ctg,elk,ilk,snb,ivb,vlv,hsw */ - vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); + io_decode = intel_vga_get(display); outb(0x01, VGA_SEQ_I); sr1 = inb(VGA_SEQ_D); @@ -129,7 +175,7 @@ void intel_vga_disable(struct intel_display *display) msr &= ~VGA_MIS_COLOR; outb(msr, VGA_MIS_W); - vga_put(pdev, VGA_RSRC_LEGACY_IO); + intel_vga_put(display, io_decode); udelay(300); From 01f827140bcbde9f6b4ce68bc7657f9bb9739a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:26 +0200 Subject: [PATCH 0068/1735] drm/i915/vga: Stop trying to use GMCH_CTRL for VGA decode control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_gmch_vga_set_state() is a complete lie on ILK+ because the GMCH_CTRL register is locked and can't actually be written. But we still need to remove the iGPU from the VGA arbitration on iGPU+dGPU systems, or else Xorg performance will tank due to the constant VGA arbiter accesses. For VGA memory decode we can't turn off the PCI_COMMAND memory deocde as that would disable even normal MMIO. Instead we can disable just the VGA memory decode via the VGA MSR register. And we can do that just once when disabling the VGA plane. That way we don't have to touch VGA registers anywhere else. We can also inform the arbiter that we're no longer decoding VGA memory. This will stop the arbiter from disabling all memory decode for the iGPU via PCI_COMMAND (and thus breaking everything) whenever some other GPU wants to own the VGA memory accesses. For IO we can disable all IO decode via the PCI_COMMAND register, except around the few VGA register accesses that we need to do in intel_vga_disable(). Unfortunately we can't disable IO decode permanently as it makes some laptops (eg. Dell Latitude E5400) hang during reboot/shutdown. One option would be to re-enable IO decode from the poweroff hooks, but that won't help the sysrq emergency reboot/shutdown since it won't call said hooks. So let's try to keep IO decode in its original setting unless we really need to disable it to exclude the GPU from VGA arbitration. I suppose we could keep frobbing GMCH_CTRL on pre-ILK, but it seems better to not do it since it has other side effects such as changing the class code of the PCI device. For discrete GPUs we'll rely on the bridge control instead. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-9-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_vga.c | 93 +++++++++++++++--------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index a2a1c33d053e..f2f7d396c556 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -71,6 +71,19 @@ static bool intel_pci_set_io_decode(struct pci_dev *pdev, bool enable) return old & PCI_COMMAND_IO; } +static bool intel_pci_bridge_set_vga(struct pci_dev *pdev, bool enable) +{ + u16 old = 0, ctl; + + pci_read_config_word(pdev->bus->self, PCI_BRIDGE_CONTROL, &old); + ctl = old & ~PCI_BRIDGE_CTL_VGA; + if (enable) + ctl |= PCI_BRIDGE_CTL_VGA; + pci_write_config_word(pdev->bus->self, PCI_BRIDGE_CONTROL, ctl); + + return old & PCI_BRIDGE_CTL_VGA; +} + static bool intel_vga_get(struct intel_display *display) { struct pci_dev *pdev = to_pci_dev(display->drm->dev); @@ -108,6 +121,7 @@ static void intel_vga_put(struct intel_display *display, bool io_decode) /* Disable the VGA plane that we never use */ void intel_vga_disable(struct intel_display *display) { + struct pci_dev *pdev = to_pci_dev(display->drm->dev); i915_reg_t vga_reg = intel_vga_cntrl_reg(display); bool io_decode; u8 msr, sr1; @@ -160,6 +174,12 @@ void intel_vga_disable(struct intel_display *display) outb(sr1 | VGA_SR01_SCREEN_OFF, VGA_SEQ_D); msr = inb(VGA_MIS_R); + /* + * Always disable VGA memory decode for iGPU so that + * intel_vga_set_decode() doesn't need to access VGA registers. + * VGA_MIS_ENB_MEM_ACCESS=0 is also the reset value. + */ + msr &= ~VGA_MIS_ENB_MEM_ACCESS; /* * VGA_MIS_COLOR controls both GPU level and display engine level * MDA vs. CGA decode logic. But when the register gets reset @@ -177,6 +197,14 @@ void intel_vga_disable(struct intel_display *display) intel_vga_put(display, io_decode); + /* + * Inform the arbiter about VGA memory decode being disabled so + * that it doesn't disable all memory decode for the iGPU when + * targeting another GPU. + */ + if (!display->platform.dgfx) + vga_set_legacy_decoding(pdev, VGA_RSRC_LEGACY_IO); + udelay(300); reset_vgacntr: @@ -184,45 +212,38 @@ void intel_vga_disable(struct intel_display *display) intel_de_posting_read(display, vga_reg); } -static int intel_gmch_vga_set_state(struct intel_display *display, bool enable_decode) -{ - struct pci_dev *pdev = to_pci_dev(display->drm->dev); - u16 gmch_ctrl; - - if (pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0), - intel_gmch_ctrl_reg(display), &gmch_ctrl)) { - drm_err(display->drm, "failed to read control word\n"); - return -EIO; - } - - if (!!(gmch_ctrl & INTEL_GMCH_VGA_DISABLE) == !enable_decode) - return 0; - - if (enable_decode) - gmch_ctrl &= ~INTEL_GMCH_VGA_DISABLE; - else - gmch_ctrl |= INTEL_GMCH_VGA_DISABLE; - - if (pci_bus_write_config_word(pdev->bus, PCI_DEVFN(0, 0), - intel_gmch_ctrl_reg(display), gmch_ctrl)) { - drm_err(display->drm, "failed to write control word\n"); - return -EIO; - } - - return 0; -} - -static unsigned int intel_gmch_vga_set_decode(struct pci_dev *pdev, bool enable_decode) +static unsigned int intel_vga_set_decode(struct pci_dev *pdev, bool enable_decode) { struct intel_display *display = to_intel_display(pdev); + unsigned int decodes = VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; - intel_gmch_vga_set_state(display, enable_decode); + drm_dbg_kms(display->drm, "%s VGA decode due to VGA arbitration\n", + str_enable_disable(enable_decode)); - if (enable_decode) - return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | - VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; - else - return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; + /* + * Can't use GMCH_CTRL INTEL_GMCH_VGA_DISABLE to disable VGA + * decode on ILK+ since the register is locked. Instead + * intel_disable_vga() will disable VGA memory decode for the + * iGPU, and here we just need to take care of the IO decode. + * For discrete GPUs we rely on the bridge VGA control. + * + * We can't disable IO decode already in intel_vga_disable() + * because at least some laptops (eg. CTG Dell Latitude E5400) + * will hang during reboot/shutfown with IO decode disabled. + */ + if (display->platform.dgfx) { + if (!enable_decode) + intel_pci_bridge_set_vga(pdev, false); + else + decodes |= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; + } else { + if (!enable_decode) + intel_pci_set_io_decode(pdev, false); + else + decodes |= VGA_RSRC_LEGACY_IO; + } + + return decodes; } void intel_vga_register(struct intel_display *display) @@ -239,7 +260,7 @@ void intel_vga_register(struct intel_display *display) * then we do not take part in VGA arbitration and the * vga_client_register() fails with -ENODEV. */ - ret = vga_client_register(pdev, intel_gmch_vga_set_decode); + ret = vga_client_register(pdev, intel_vga_set_decode); drm_WARN_ON(display->drm, ret && ret != -ENODEV); } From 7e47a14b02fe288029448f8ba80852fe2b0b2d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:27 +0200 Subject: [PATCH 0069/1735] drm/i915/vga: Assert that VGA register accesses are going to the right GPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want our VGA register accesses to land on the correct GPU. Check that the VGA routing is appropriately configured. For the iGPU this just means the IO decode enable on the GPU, but for dGPUs we also need the entire chain of bridges to forward the VGA accesses. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-10-ville.syrjala@linux.intel.com Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_vga.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vga.c b/drivers/gpu/drm/i915/display/intel_vga.c index f2f7d396c556..e51451966f72 100644 --- a/drivers/gpu/drm/i915/display/intel_vga.c +++ b/drivers/gpu/drm/i915/display/intel_vga.c @@ -58,6 +58,28 @@ static bool has_vga_pipe_sel(struct intel_display *display) return DISPLAY_VER(display) < 7; } +static bool intel_pci_has_vga_io_decode(struct pci_dev *pdev) +{ + u16 cmd = 0; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + if ((cmd & PCI_COMMAND_IO) == 0) + return false; + + pdev = pdev->bus->self; + while (pdev) { + u16 ctl = 0; + + pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &ctl); + if ((ctl & PCI_BRIDGE_CTL_VGA) == 0) + return false; + + pdev = pdev->bus->self; + } + + return true; +} + static bool intel_pci_set_io_decode(struct pci_dev *pdev, bool enable) { u16 old = 0, cmd; @@ -169,6 +191,8 @@ void intel_vga_disable(struct intel_display *display) io_decode = intel_vga_get(display); + drm_WARN_ON(display->drm, !intel_pci_has_vga_io_decode(pdev)); + outb(0x01, VGA_SEQ_I); sr1 = inb(VGA_SEQ_D); outb(sr1 | VGA_SR01_SCREEN_OFF, VGA_SEQ_D); From d2bfe35f840d9fdfe76efcd4ffab185abad44046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:28 +0200 Subject: [PATCH 0070/1735] drm/i915/de: Simplify intel_de_read8() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_de_read8() is only needed for VGA register MMIO access by the CRT code on gen2/3. Remove the redundant wakelock stuff, and add a platform check to make sure this won't get used on any platform where MMIO VGA register accesses don't work. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-11-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_de.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h index a7ce3b875e06..5c1b37d30045 100644 --- a/drivers/gpu/drm/i915/display/intel_de.h +++ b/drivers/gpu/drm/i915/display/intel_de.h @@ -6,6 +6,8 @@ #ifndef __INTEL_DE_H__ #define __INTEL_DE_H__ +#include + #include "intel_display_core.h" #include "intel_dmc_wl.h" #include "intel_dsb.h" @@ -34,15 +36,10 @@ intel_de_read(struct intel_display *display, i915_reg_t reg) static inline u8 intel_de_read8(struct intel_display *display, i915_reg_t reg) { - u8 val; + /* this is only used on VGA registers (possible on pre-g4x) */ + drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x); - intel_dmc_wl_get(display, reg); - - val = intel_uncore_read8(__to_uncore(display), reg); - - intel_dmc_wl_put(display, reg); - - return val; + return intel_uncore_read8(__to_uncore(display), reg); } static inline u64 From 005a496d0f5b2c5818e320d58edc0a47f7bdb923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:29 +0200 Subject: [PATCH 0071/1735] drm/i915/de: Add intel_de_write8() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a write counterpart to intel_de_read8(). Will be used for MMIO access to VGA registers on pre-g4x. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-12-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_de.h | 8 ++++++++ drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h | 8 ++++++++ drivers/gpu/drm/xe/xe_mmio.c | 9 +++++++++ drivers/gpu/drm/xe/xe_mmio.h | 1 + 4 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h index 5c1b37d30045..f30f3f8ebee1 100644 --- a/drivers/gpu/drm/i915/display/intel_de.h +++ b/drivers/gpu/drm/i915/display/intel_de.h @@ -42,6 +42,14 @@ intel_de_read8(struct intel_display *display, i915_reg_t reg) 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/compat-i915-headers/intel_uncore.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h index c05d4c4292d3..c5e198ace7bc 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h @@ -38,6 +38,14 @@ static inline u8 intel_uncore_read8(struct intel_uncore *uncore, return xe_mmio_read8(__compat_uncore_to_mmio(uncore), reg); } +static inline void intel_uncore_write8(struct intel_uncore *uncore, + i915_reg_t i915_reg, u8 val) +{ + struct xe_reg reg = XE_REG(i915_mmio_reg_offset(i915_reg)); + + xe_mmio_write8(__compat_uncore_to_mmio(uncore), reg, val); +} + static inline u16 intel_uncore_read16(struct intel_uncore *uncore, i915_reg_t i915_reg) { diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c index 350dca1f0925..6bdaedc1da73 100644 --- a/drivers/gpu/drm/xe/xe_mmio.c +++ b/drivers/gpu/drm/xe/xe_mmio.c @@ -158,6 +158,15 @@ u8 xe_mmio_read8(struct xe_mmio *mmio, struct xe_reg reg) return val; } +void xe_mmio_write8(struct xe_mmio *mmio, struct xe_reg reg, u8 val) +{ + u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); + + trace_xe_reg_rw(mmio, true, addr, val, sizeof(val)); + + writeb(val, mmio->regs + addr); +} + u16 xe_mmio_read16(struct xe_mmio *mmio, struct xe_reg reg) { u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); diff --git a/drivers/gpu/drm/xe/xe_mmio.h b/drivers/gpu/drm/xe/xe_mmio.h index 15362789ab99..cd355a43af3d 100644 --- a/drivers/gpu/drm/xe/xe_mmio.h +++ b/drivers/gpu/drm/xe/xe_mmio.h @@ -17,6 +17,7 @@ int xe_mmio_probe_tiles(struct xe_device *xe); void xe_mmio_init(struct xe_mmio *mmio, struct xe_tile *tile, void __iomem *ptr, u32 size); u8 xe_mmio_read8(struct xe_mmio *mmio, struct xe_reg reg); +void xe_mmio_write8(struct xe_mmio *mmio, struct xe_reg reg, u8 val); u16 xe_mmio_read16(struct xe_mmio *mmio, struct xe_reg reg); void xe_mmio_write32(struct xe_mmio *mmio, struct xe_reg reg, u32 val); u32 xe_mmio_read32(struct xe_mmio *mmio, struct xe_reg reg); From 3acd8cbbd738eb8c85fc5ec3c7d9fbee26db6d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 8 Dec 2025 20:26:30 +0200 Subject: [PATCH 0072/1735] drm/i915/vga: Introduce intel_vga_{read,write}() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VGA register are rather special since they either get accessed via the global IO addresses, or possibly through MMIO on pre-g4x platforms. Wrap all VGA register accesses in intel_vga_{read,write}() to make it obvious where they get accessed. Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20251208182637.334-13-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_crt.c | 6 +++-- drivers/gpu/drm/i915/display/intel_crt_regs.h | 2 -- drivers/gpu/drm/i915/display/intel_vga.c | 27 +++++++++++++++---- drivers/gpu/drm/i915/display/intel_vga.h | 3 +++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 5f9a03877ea9..dedc26f6a2b2 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -33,6 +33,7 @@ #include #include #include +#include