diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-26 22:25:08 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-26 22:25:08 +0100 |
| commit | 943c160ac32335200c8548386d2273f6aac077c4 (patch) | |
| tree | 79e7078ecfe40693fe2cf4f45b54b5c131da45d5 /drivers | |
| parent | efba40297ebc8914028f083cdfb5610dd349bd37 (diff) | |
| parent | f0cabcc880aba3f05ca59d735f6d166b7fceca80 (diff) | |
| download | linux-next-943c160ac32335200c8548386d2273f6aac077c4.tar.gz linux-next-943c160ac32335200c8548386d2273f6aac077c4.zip | |
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/i915/kernel.git
Diffstat (limited to 'drivers')
65 files changed, 3669 insertions, 685 deletions
diff --git a/drivers/gpu/drm/display/drm_dp_tunnel.c b/drivers/gpu/drm/display/drm_dp_tunnel.c index 76c6bc84a806..d29ad4116e64 100644 --- a/drivers/gpu/drm/display/drm_dp_tunnel.c +++ b/drivers/gpu/drm/display/drm_dp_tunnel.c @@ -111,6 +111,8 @@ DPTUN_REG(DP_ALLOCATED_BW) | \ DPTUN_REG(DP_TUNNELING_MAX_LINK_RATE) | \ DPTUN_REG(DP_TUNNELING_MAX_LANE_COUNT) | \ + DPTUN_REG(DP_TUNNELING_MAIN_LINK_CHANNEL_CODING) | \ + DPTUN_REG(DP_TUNNELING_128B132B_LINK_RATE) | \ DPTUN_REG(DP_DPTX_BW_ALLOCATION_MODE_CONTROL)) static const DECLARE_BITMAP(dptun_info_regs, 64) = { @@ -140,11 +142,14 @@ struct drm_dp_tunnel { int estimated_bw; int allocated_bw; + u8 dprx_128b132b_rates; int max_dprx_rate; u8 max_dprx_lane_count; u8 adapter_id; + bool dprx_128b132b_support:1; + bool dprx_128b132b_lane0_mapping_support:1; bool bw_alloc_supported:1; bool bw_alloc_enabled:1; bool has_io_error:1; @@ -260,9 +265,46 @@ static int tunnel_reg_bw_granularity(const struct drm_dp_tunnel_regs *regs) return (250000 << gr) / 8; } +static bool tunnel_reg_dprx_128b132b_support(const struct drm_dp_tunnel_regs *regs) +{ + return tunnel_reg(regs, DP_TUNNELING_MAIN_LINK_CHANNEL_CODING) & DP_128B132B_DP_SUPPORTED; +} + +static bool tunnel_reg_dprx_128b132b_lane0_mapping_support(const struct drm_dp_tunnel_regs *regs) +{ + return tunnel_reg(regs, DP_TUNNELING_128B132B_LINK_RATE) & + DP_TUNNELING_128B132B_LL_LANE0_MAPPING_SUPPORT; +} + +static u8 tunnel_reg_dprx_128b132b_rates(const struct drm_dp_tunnel_regs *regs) +{ + if (!tunnel_reg_dprx_128b132b_support(regs)) + return 0; + + return tunnel_reg(regs, DP_TUNNELING_128B132B_LINK_RATE) & + DP_TUNNELING_128B132B_LINK_RATE_MASK; +} + +static u8 max_128b132b_rate(u8 rates) +{ + if (rates & DP_TUNNELING_20GBPS_PER_LANE_SUPPORT) + return DP_TUNNELING_20GBPS_PER_LANE_SUPPORT; + else if (rates & DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT) + return DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT; + else if (rates & DP_TUNNELING_10GBPS_PER_LANE_SUPPORT) + return DP_TUNNELING_10GBPS_PER_LANE_SUPPORT; + + WARN_ON(rates); + + return 0; +} + static int tunnel_reg_max_dprx_rate(const struct drm_dp_tunnel_regs *regs) { - u8 bw_code = tunnel_reg(regs, DP_TUNNELING_MAX_LINK_RATE); + u8 bw_code = max_128b132b_rate(tunnel_reg_dprx_128b132b_rates(regs)); + + if (!bw_code) + bw_code = tunnel_reg(regs, DP_TUNNELING_MAX_LINK_RATE); return drm_dp_bw_code_to_link_rate(bw_code); } @@ -706,6 +748,23 @@ static bool update_dprx_caps(struct drm_dp_tunnel *tunnel, const struct drm_dp_t { bool changed = false; + if (tunnel_reg_dprx_128b132b_support(regs) != tunnel->dprx_128b132b_support) { + tunnel->dprx_128b132b_support = tunnel_reg_dprx_128b132b_support(regs); + changed = true; + } + + if (tunnel_reg_dprx_128b132b_lane0_mapping_support(regs) != + tunnel->dprx_128b132b_lane0_mapping_support) { + tunnel->dprx_128b132b_lane0_mapping_support = + tunnel_reg_dprx_128b132b_lane0_mapping_support(regs); + changed = true; + } + + if (tunnel_reg_dprx_128b132b_rates(regs) != tunnel->dprx_128b132b_rates) { + tunnel->dprx_128b132b_rates = tunnel_reg_dprx_128b132b_rates(regs); + changed = true; + } + if (tunnel_reg_max_dprx_rate(regs) != tunnel->max_dprx_rate) { tunnel->max_dprx_rate = tunnel_reg_max_dprx_rate(regs); changed = true; @@ -1332,6 +1391,61 @@ int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, struct drm_dp_aux *a EXPORT_SYMBOL(drm_dp_tunnel_handle_irq); /** + * drm_dp_tunnel_128b132b_supported - Query if 128b132b is supported by the tunnel's DPRX + * @tunnel: Tunnel object + * + * The function is used to query if 128b132b is supported by the DPRX connected + * to @tunnel. + * + * Returns %true if 128b132b is supported by the DPRX. + */ +bool drm_dp_tunnel_128b132b_supported(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->dprx_128b132b_support; +} +EXPORT_SYMBOL(drm_dp_tunnel_128b132b_supported); + +/** + * drm_dp_tunnel_128b132b_lane0_mapping_supported - Check 128b/132b lane 0 mapping support + * @tunnel: Tunnel object + * + * Check whether the DP-out adapter always maps lane 0 as expected by the + * DPRX on a tunneled 128b/132b link. If the function returns %true, one- and + * two-lane configurations with UHBR link rates can always be used. If it + * returns %false, using one or two lanes with UHBR link rates may cause a + * lane-count conversion failure in the DPRX, requiring corrective action by + * the source during link training. See DP Standard v2.1b, section + * 3.5.2.16.3, 128b/132b DPRX Lane Count Conversion Failure Indication and + * Corrective Action. + * + * A four-lane configuration can always be used, provided that the DPRX + * supports it, regardless of the function's return value. + * + * Returns %true if the DP-out adapter supports the 128b/132b lane 0 mapping. + */ +bool drm_dp_tunnel_128b132b_lane0_mapping_supported(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->dprx_128b132b_lane0_mapping_support; +} +EXPORT_SYMBOL(drm_dp_tunnel_128b132b_lane0_mapping_supported); + +/** + * drm_dp_tunnel_128b132b_dprx_rates - Query the supported 128b132b rates of the tunnel's DPRX + * @tunnel: Tunnel object + * + * The function is used to query the supported 128b132b rates of the DPRX connected + * to @tunnel. Note that the related DP_128B132B_SUPPROTED_LINK_RATES DPCD + * register will indicate no supported 128B132B rates for a tunneled DPRX. + * + * Returns the mask of supported 128b132b rates. + */ +u8 drm_dp_tunnel_128b132b_dprx_rates(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->dprx_128b132b_rates; +} +EXPORT_SYMBOL(drm_dp_tunnel_128b132b_dprx_rates); + +/** * drm_dp_tunnel_max_dprx_rate - Query the maximum rate of the tunnel's DPRX * @tunnel: Tunnel object * diff --git a/drivers/gpu/drm/i915/.kunitconfig b/drivers/gpu/drm/i915/.kunitconfig new file mode 100644 index 000000000000..1b47fe62b285 --- /dev/null +++ b/drivers/gpu/drm/i915/.kunitconfig @@ -0,0 +1,9 @@ +CONFIG_EXPERT=y +CONFIG_MODULES=y +CONFIG_KUNIT=y +CONFIG_PCI=y +CONFIG_DEBUG_FS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DRM=y +CONFIG_DRM_I915=y +CONFIG_DRM_I915_KUNIT_TEST=y diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug index 52a3a59b4ba2..dc43dcfbadb6 100644 --- a/drivers/gpu/drm/i915/Kconfig.debug +++ b/drivers/gpu/drm/i915/Kconfig.debug @@ -220,6 +220,18 @@ config DRM_I915_SELFTEST_BROKEN If in doubt, say "N". +config DRM_I915_KUNIT_TEST + tristate "KUnit tests for the drm i915 driver" if !KUNIT_ALL_TESTS + depends on DRM_I915 && KUNIT && DEBUG_FS + default KUNIT_ALL_TESTS + help + Choose this option to allow the driver to perform selftests under + the kunit framework + + Recommended for driver developers only. + + If in doubt, say "N". + config DRM_I915_LOW_LEVEL_TRACEPOINTS bool "Enable low level request tracing events" depends on DRM_I915 diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index c4de717505d7..a83fa8be0aba 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -386,6 +386,8 @@ i915-y += \ i915-$(CONFIG_DRM_I915_DP_TUNNEL) += \ display/intel_dp_tunnel.o +obj-$(CONFIG_DRM_I915_KUNIT_TEST) += display/tests/ + i915-$(CONFIG_DRM_I915_GVT) += \ display/intel_gvt_api.o diff --git a/drivers/gpu/drm/i915/display/hsw_ips.c b/drivers/gpu/drm/i915/display/hsw_ips.c index cbaef3f13f00..f828ec962164 100644 --- a/drivers/gpu/drm/i915/display/hsw_ips.c +++ b/drivers/gpu/drm/i915/display/hsw_ips.c @@ -207,7 +207,7 @@ static int _hsw_ips_min_cdclk(const struct intel_crtc_state *crtc_state) struct intel_display *display = to_intel_display(crtc_state); if (display->platform.broadwell) - return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95); + return DIV_ROUND_UP(crtc_state->pixel_rate_cdclk * 100, 95); /* no IPS specific limits to worry about */ return 0; diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.c b/drivers/gpu/drm/i915/display/i9xx_plane.c index 70734d32a409..df6da1c6a359 100644 --- a/drivers/gpu/drm/i915/display/i9xx_plane.c +++ b/drivers/gpu/drm/i915/display/i9xx_plane.c @@ -418,13 +418,13 @@ static int i9xx_plane_min_cdclk(const struct intel_crtc_state *crtc_state, unsigned int num, den; /* - * Note that crtc_state->pixel_rate accounts for both + * Note that crtc_state->pixel_rate_cdclk accounts for both * horizontal and vertical panel fitter downscaling factors. * Pre-HSW bspec tells us to only consider the horizontal * downscaling factor here. We ignore that and just consider * both for simplicity. */ - pixel_rate = crtc_state->pixel_rate; + pixel_rate = crtc_state->pixel_rate_cdclk; i9xx_plane_ratio(crtc_state, plane_state, &num, &den); @@ -1108,6 +1108,10 @@ intel_primary_plane_create(struct intel_display *display, enum pipe pipe) DRM_MODE_ROTATE_0, supported_rotations); + if (display->platform.valleyview || display->platform.cherryview) + drm_plane_create_blend_mode_property(&plane->base, + BIT(DRM_MODE_BLEND_PREMULTI)); + zpos = 0; drm_plane_create_zpos_immutable_property(&plane->base, zpos); diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index 9729f1837d2c..eae76e961105 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -38,6 +38,7 @@ #include "intel_de.h" #include "intel_display_types.h" #include "intel_display_wa.h" +#include "intel_dp.h" #include "intel_lpe_audio.h" /** @@ -696,6 +697,13 @@ static void ibx_audio_codec_enable(struct intel_encoder *encoder, mutex_unlock(&display->audio.mutex); } +static +bool intel_audio_needs_cpu_transcoder_id(const struct intel_crtc_state *crtc_state) +{ + return intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST) || + intel_dp_is_uhbr(crtc_state); +} + bool intel_audio_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) @@ -762,6 +770,8 @@ void intel_audio_codec_enable(struct intel_encoder *encoder, audio_state = &display->audio.state[cpu_transcoder]; audio_state->encoder = encoder; + audio_state->needs_cpu_transcoder_id = + intel_audio_needs_cpu_transcoder_id(crtc_state); BUILD_BUG_ON(sizeof(audio_state->eld) != sizeof(crtc_state->eld)); memcpy(audio_state->eld, crtc_state->eld, sizeof(audio_state->eld)); @@ -769,8 +779,12 @@ void intel_audio_codec_enable(struct intel_encoder *encoder, if (acomp && acomp->base.audio_ops && acomp->base.audio_ops->pin_eld_notify) { - /* audio drivers expect cpu_transcoder = -1 to indicate Non-MST cases */ - if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) + /* + * Audio drivers expect cpu_transcoder = -1 to indicate + * Non-MST/HBR cases. MST and UHBR SST are addressed by + * a real cpu_transcoder. + */ + if (!intel_audio_needs_cpu_transcoder_id(crtc_state)) cpu_transcoder = -1; acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr, (int)port, (int)cpu_transcoder); @@ -819,14 +833,19 @@ void intel_audio_codec_disable(struct intel_encoder *encoder, audio_state = &display->audio.state[cpu_transcoder]; audio_state->encoder = NULL; + audio_state->needs_cpu_transcoder_id = false; memset(audio_state->eld, 0, sizeof(audio_state->eld)); mutex_unlock(&display->audio.mutex); if (acomp && acomp->base.audio_ops && acomp->base.audio_ops->pin_eld_notify) { - /* audio drivers expect cpu_transcoder = -1 to indicate Non-MST cases */ - if (!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST)) + /* + * Audio drivers expect cpu_transcoder = -1 to indicate + * Non-MST/HBR cases. MST and UHBR SST are addressed by + * a real cpu_transcoder. + */ + if (!intel_audio_needs_cpu_transcoder_id(old_crtc_state)) cpu_transcoder = -1; acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr, (int)port, (int)cpu_transcoder); @@ -1118,18 +1137,24 @@ static int intel_audio_component_get_cdclk_freq(struct device *kdev) } /* - * get the intel audio state according to the parameter port and cpu_transcoder - * MST & (cpu_transcoder >= 0): return the audio.state[cpu_transcoder].encoder], - * when port is matched - * MST & (cpu_transcoder < 0): this is invalid - * Non-MST & (cpu_transcoder >= 0): only cpu_transcoder = 0 (the first device entry) - * will get the right intel_encoder with port matched - * Non-MST & (cpu_transcoder < 0): get the right intel_encoder with port matched + * Get the intel audio state for a given (port, cpu_transcoder). + * + * Streams are addressed either by a real cpu_transcoder (DP MST and UHBR SST, + * i.e. entries whose stored needs_cpu_transcoder_id is true) or by port alone + * (legacy SST). Both the signalling side (pin_eld_notify()) and the lookup + * side use the same predicate, so the two are symmetric. + * + * cpu_transcoder >= 0 & needs_cpu_transcoder_id: return audio.state[cpu_transcoder] + * when the port matches. + * cpu_transcoder < 0 & !needs_cpu_transcoder_id: return the first port-matching + * entry. + * cpu_transcoder = 0 & !needs_cpu_transcoder_id: falls to the port-only + * loop so the first device entry of a legacy SST port is still found. */ static struct intel_audio_state *find_audio_state(struct intel_display *display, int port, int cpu_transcoder) { - /* MST */ + /* MST, or UHBR SST. */ if (cpu_transcoder >= 0) { struct intel_audio_state *audio_state; struct intel_encoder *encoder; @@ -1142,11 +1167,11 @@ static struct intel_audio_state *find_audio_state(struct intel_display *display, encoder = audio_state->encoder; if (encoder && encoder->port == port && - encoder->type == INTEL_OUTPUT_DP_MST) + audio_state->needs_cpu_transcoder_id) return audio_state; } - /* Non-MST */ + /* Legacy SST. */ if (cpu_transcoder > 0) return NULL; @@ -1158,7 +1183,7 @@ static struct intel_audio_state *find_audio_state(struct intel_display *display, encoder = audio_state->encoder; if (encoder && encoder->port == port && - encoder->type != INTEL_OUTPUT_DP_MST) + !audio_state->needs_cpu_transcoder_id) return audio_state; } diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index 41539fdfeac5..99fd84a5ac13 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -14,6 +14,7 @@ #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_display_utils.h" +#include "intel_display_wa.h" #include "intel_dram.h" #include "intel_mchbar.h" #include "intel_parent.h" @@ -52,6 +53,8 @@ struct intel_qgv_point { #define DEPROGBWPCLIMIT 60 +#define PEAK_BW_THRESHOLD 20000 + struct intel_psf_gv_point { u8 clk; /* clock in multiples of 16.6666 MHz */ }; @@ -184,7 +187,7 @@ static int icl_pcode_restrict_qgv_points(struct intel_display *display, { int ret; - if (DISPLAY_VER(display) >= 14) + if (HAS_PMDEMAND(display)) return 0; /* bspec says to keep retrying for at least 1 ms */ @@ -246,12 +249,10 @@ static bool is_y_tile(struct intel_display *display) return !HAS_4TILE(display); } -static int icl_get_qgv_points(struct intel_display *display, - const struct dram_info *dram_info, - struct intel_qgv_info *qi) +static int icl_init_qgv_info(struct intel_display *display, + const struct dram_info *dram_info, + struct intel_qgv_info *qi) { - int i, ret; - qi->num_qgv_points = dram_info->num_qgv_points; qi->num_psf_points = dram_info->num_psf_gv_points; @@ -272,7 +273,14 @@ static int icl_get_qgv_points(struct intel_display *display, case INTEL_DRAM_LPDDR4: case INTEL_DRAM_LPDDR5: qi->t_bl = 16; - qi->max_numchannels = 8; + /* + * Wa_16030862157 + * Xe3p supports a fully-populated 16-channel LPDDR + * config (4 memory controllers x 4 channels); earlier + * D14+ platforms top out at 8. + */ + qi->max_numchannels = + intel_display_wa(display, INTEL_DISPLAY_WA_16030862157) ? 16 : 8; qi->channel_width = 16; qi->deinterleave = 4; break; @@ -323,6 +331,18 @@ static int icl_get_qgv_points(struct intel_display *display, qi->max_numchannels = 1; } + return 0; +} + +static int icl_get_qgv_points(struct intel_display *display, + const struct dram_info *dram_info, + struct intel_qgv_info *qi) +{ + int i, ret; + + if (icl_init_qgv_info(display, dram_info, qi)) + return -EINVAL; + if (drm_WARN_ON(display->drm, qi->num_qgv_points > ARRAY_SIZE(qi->points))) qi->num_qgv_points = ARRAY_SIZE(qi->points); @@ -511,6 +531,19 @@ static const struct intel_display_bw_params *get_display_bw_params(struct intel_ return NULL; } +static void update_sagv_status(struct intel_display *display, int qgv_points) +{ + /* + * In case if SAGV is disabled in BIOS, we always get 1 + * SAGV point, but we can't send PCode commands to restrict it + * as it will fail and pointless anyway. + */ + if (qgv_points == 1) + display->sagv.status = I915_SAGV_NOT_CONTROLLED; + else + display->sagv.status = I915_SAGV_ENABLED; +} + static int icl_get_bw_info(struct intel_display *display, const struct dram_info *dram_info, const struct intel_soc_bw_params *soc_bw_params, @@ -569,15 +602,6 @@ static int icl_get_bw_info(struct intel_display *display, i, j, bi->num_planes, bi->deratedbw[j]); } } - /* - * In case if SAGV is disabled in BIOS, we always get 1 - * SAGV point, but we can't send PCode commands to restrict it - * as it will fail and pointless anyway. - */ - if (qi.num_qgv_points == 1) - display->sagv.status = I915_SAGV_NOT_CONTROLLED; - else - display->sagv.status = I915_SAGV_ENABLED; return 0; } @@ -587,6 +611,32 @@ static int tgl_peakbw(int num_channels, int channel_width, int dclk) return num_channels * (channel_width / 8) * dclk; } +static void xe3_add_peakbw_threshold(struct intel_display *display) +{ + u8 qgv_points = display->bw.num_qgv_points; + + if (!HAS_PEAK_BW_THRESHOLD(display)) + return; + + if (qgv_points >= I915_NUM_QGV_POINTS) { + drm_dbg_kms(display->drm, "QGV points maxed out; skipping peak bandwidth threshold.\n"); + return; + } + + if (qgv_points <= 1) + return; + + display->bw.num_qgv_points++; + + display->bw.peakbw[qgv_points] = PEAK_BW_THRESHOLD; + + for (int i = 0; i < ARRAY_SIZE(display->bw.max); i++) + display->bw.max[i].deratedbw[qgv_points] = PEAK_BW_THRESHOLD; + + drm_dbg_kms(display->drm, "An extra QGV point %d added for Peak bw threshold of %d\n", + qgv_points, PEAK_BW_THRESHOLD); +} + static int tgl_get_bw_info(struct intel_display *display, const struct dram_info *dram_info, const struct intel_soc_bw_params *soc_bw_params, @@ -624,10 +674,16 @@ static int tgl_get_bw_info(struct intel_display *display, ipqdepth = min(ipqdepthpch, display_bw_params->displayrtids / num_channels); /* + * Wa_16030862157 * clperchgroup = 4kpagespermempage * clperchperblock, - * clperchperblock = 8 / num_channels * interleave + * clperchperblock = max(8 / num_channels, 1) * interleave + * + * The 8 / num_channels truncating divide collapses to 0 for + * >8-channel configs (16-channel: 8 / 16 = 0); the max(..., 1) floor + * keeps clperchperblock >= 1 there while preserving the literal + * truncating divide for <=8-channel configs. */ - clperchgroup = 4 * (8 / num_channels) * qi.deinterleave; + clperchgroup = 4 * max(8 / num_channels, 1) * qi.deinterleave; display->bw.num_qgv_points = qi.num_qgv_points; display->bw.num_psf_gv_points = qi.num_psf_points; @@ -681,6 +737,9 @@ static int tgl_get_bw_info(struct intel_display *display, drm_dbg_kms(display->drm, "QGV %d: peakbw=%u\n", i, display->bw.peakbw[i]); } + /* For xe3 cases add an extra qgv point for Peak bw threshold */ + xe3_add_peakbw_threshold(display); + for (i = 0; i < qi.num_psf_points; i++) { const struct intel_psf_gv_point *sp = &qi.psf_points[i]; @@ -689,16 +748,6 @@ static int tgl_get_bw_info(struct intel_display *display, drm_dbg_kms(display->drm, "PSF GV %d: bw=%u\n", i, display->bw.psf_bw[i]); } - /* - * In case if SAGV is disabled in BIOS, we always get 1 - * SAGV point, but we can't send PCode commands to restrict it - * as it will fail and pointless anyway. - */ - if (qi.num_qgv_points == 1) - display->sagv.status = I915_SAGV_NOT_CONTROLLED; - else - display->sagv.status = I915_SAGV_ENABLED; - return 0; } @@ -718,8 +767,6 @@ static void dg2_get_bw_info(struct intel_display *display) /* Bandwidth does not depend on # of planes; set all groups the same */ for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) display->bw.max[i] = display->bw.max[0]; - - display->sagv.status = I915_SAGV_NOT_CONTROLLED; } static int xe2_hpd_get_bw_info(struct intel_display *display, @@ -767,7 +814,6 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, * battery and plugged-in operation. */ drm_WARN_ON(display->drm, qi.num_qgv_points != 2); - display->sagv.status = I915_SAGV_ENABLED; return 0; } @@ -868,6 +914,8 @@ void intel_bw_init_hw(struct intel_display *display) } else if (DISPLAY_VER(display) == 11) { icl_get_bw_info(display, dram_info, soc_bw_params, display_bw_params); } + + update_sagv_status(display, display->bw.num_qgv_points); } static unsigned int intel_bw_num_active_planes(struct intel_display *display, @@ -1238,7 +1286,7 @@ static int intel_bw_check_qgv_points(struct intel_display *display, data_rate = DIV_ROUND_UP(data_rate, 1000); - if (DISPLAY_VER(display) >= 14) + if (HAS_PMDEMAND(display)) return mtl_find_qgv_points(display, data_rate, num_active_planes, new_bw_state); else diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index d3c5e3438d19..a53d88727177 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2908,9 +2908,9 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) } /* pixels per CDCLK */ -static int intel_cdclk_ppc(struct intel_display *display, bool double_wide) +int intel_cdclk_ppc(struct intel_display *display, bool double_wide) { - return DISPLAY_VER(display) >= 10 || double_wide ? 2 : 1; + return HAS_2PPC(display) || double_wide ? 2 : 1; } /* max pixel rate as % of CDCLK (not accounting for PPC) */ @@ -2936,7 +2936,7 @@ static int _intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state, static int intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state) { - return _intel_pixel_rate_to_cdclk(crtc_state, crtc_state->pixel_rate); + return _intel_pixel_rate_to_cdclk(crtc_state, crtc_state->pixel_rate_cdclk); } static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.h b/drivers/gpu/drm/i915/display/intel_cdclk.h index 1ff7d078b42c..a60cbf745ee2 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.h +++ b/drivers/gpu/drm/i915/display/intel_cdclk.h @@ -22,6 +22,7 @@ struct intel_cdclk_config { bool joined_mbus; }; +int intel_cdclk_ppc(struct intel_display *display, bool double_wide); void intel_cdclk_init_hw(struct intel_display *display); void intel_cdclk_uninit_hw(struct intel_display *display); void intel_init_cdclk_hooks(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 88384dea868b..0673f16f6fd0 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -1078,6 +1078,9 @@ intel_cursor_plane_create(struct intel_display *display, intel_cursor_add_size_hints_property(cursor); + drm_plane_create_blend_mode_property(&cursor->base, + BIT(DRM_MODE_BLEND_PREMULTI)); + zpos = DISPLAY_RUNTIME_INFO(display)->num_sprites[pipe] + 1; drm_plane_create_zpos_immutable_property(&cursor->base, zpos); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 2b7eb010511b..b8985e1e0a81 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -469,8 +469,10 @@ void intel_ddi_set_dp_msa(const struct intel_crtc_state *crtc_state, * of Color Encoding Format and Content Color Gamut] while sending * YCBCR 420, HDR BT.2020 signals we should program MSA MISC1 fields * which indicate VSC SDP for the Pixel Encoding/Colorimetry Format. + * Only set the delegation bit when the content needs it and + * the sink advertises support. */ - if (intel_dp_needs_vsc_sdp(crtc_state, conn_state)) + if (intel_dp_needs_vsc_colorimetry(crtc_state, conn_state)) temp |= DP_MSA_MISC_COLOR_VSC_SDP; intel_de_write(display, TRANS_MSA_MISC(display, cpu_transcoder), @@ -4563,7 +4565,7 @@ static bool crtcs_port_sync_compatible(const struct intel_crtc_state *crtc_state m_n_equal(&crtc_state1->dp_m_n, &crtc_state2->dp_m_n); } -static u8 +static u16 intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state, int tile_group_id) { @@ -4572,7 +4574,7 @@ intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state, const struct drm_connector_state *conn_state; struct intel_atomic_state *state = to_intel_atomic_state(ref_crtc_state->uapi.state); - u8 transcoders = 0; + u16 transcoders = 0; int i; /* @@ -4616,7 +4618,7 @@ static int intel_ddi_compute_config_late(struct intel_atomic_state *state, { struct intel_display *display = to_intel_display(encoder); struct drm_connector *connector = conn_state->connector; - u8 port_sync_transcoders = 0; + u16 port_sync_transcoders = 0; int ret = 0; if (intel_crtc_has_dp_encoder(crtc_state)) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 90c05ad08f86..9dfd095f325f 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -45,6 +45,7 @@ #include <drm/drm_probe_helper.h> #include <drm/drm_rect.h> #include <drm/drm_vblank.h> +#include <drm/intel/step.h> #include "g4x_dp.h" #include "g4x_hdmi.h" @@ -2233,6 +2234,29 @@ static u32 ilk_pipe_pixel_rate(const struct intel_crtc_state *crtc_state) pixel_rate); } +static u32 ilk_pipe_pixel_rate_cdclk(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + u32 pixel_rate = crtc_state->hw.pipe_mode.crtc_clock; + unsigned int ppc = HAS_2PPC(display) ? 2 : 1; + struct drm_rect src; + + /* + * We only use IF-ID interlacing. If we ever use + * PF-ID we'll need to adjust the pixel_rate here. + */ + + if (!crtc_state->pch_pfit.enabled) + return pixel_rate; + + drm_rect_init(&src, 0, 0, + drm_rect_width(&crtc_state->pipe_src) << 16, + drm_rect_height(&crtc_state->pipe_src) << 16); + + return intel_adjusted_rate_cdclk(&src, &crtc_state->pch_pfit.dst, + pixel_rate, ppc); +} + static void intel_mode_from_crtc_timings(struct drm_display_mode *mode, const struct drm_display_mode *timings) { @@ -2258,13 +2282,17 @@ static void intel_crtc_compute_pixel_rate(struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); - if (HAS_GMCH(display)) + if (HAS_GMCH(display)) { /* FIXME calculate proper pipe pixel rate for GMCH pfit */ crtc_state->pixel_rate = crtc_state->hw.pipe_mode.crtc_clock; - else + crtc_state->pixel_rate_cdclk = crtc_state->pixel_rate; + } else { crtc_state->pixel_rate = ilk_pipe_pixel_rate(crtc_state); + crtc_state->pixel_rate_cdclk = + ilk_pipe_pixel_rate_cdclk(crtc_state); + } } static void intel_joiner_adjust_timings(const struct intel_crtc_state *crtc_state, @@ -2458,6 +2486,19 @@ static int intel_crtc_set_context_latency(struct intel_crtc_state *crtc_state) set_context_latency = max(set_context_latency, intel_psr_min_set_context_latency(crtc_state)); + /* + * From PTL onwards, the set context latency can be in the vactive + * region, letting the safe window start some lines before the vblank + * start. With modes that have a smaller vblank region, the computed + * guardband is clamped to the vblank length, making the undelayed and + * delayed vblank coincide. If the SCL is also 0, the 'safe window' + * becomes effectively 0, and the DSB configured to wait for it gets + * stalled, since the hardware never signals the safe window. Keep the + * set context latency at a minimum of 1 to avoid this. + */ + if (DISPLAY_VER(display) >= 30) + set_context_latency = max(1, set_context_latency); + return set_context_latency; } @@ -2737,6 +2778,10 @@ void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, HSYNC_START(adjusted_mode->crtc_hsync_start - 1) | HSYNC_END(adjusted_mode->crtc_hsync_end - 1)); + if (display->platform.novalake && + IS_DISPLAY_STEP(display, STEP_A0, STEP_C0)) + crtc_vtotal = 1; + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); @@ -2830,6 +2875,10 @@ void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, * The double buffer latch point for TRANS_VTOTAL * is the transcoder's undelayed vblank. */ + if (display->platform.novalake && + IS_DISPLAY_STEP(display, STEP_A0, STEP_C0)) + crtc_vtotal = 1; + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); @@ -3758,9 +3807,9 @@ static void enabled_joiner_pipes(struct intel_display *display, } } -static u8 hsw_panel_transcoders(struct intel_display *display) +static u16 hsw_panel_transcoders(struct intel_display *display) { - u8 panel_transcoder_mask = BIT(TRANSCODER_EDP); + u16 panel_transcoder_mask = BIT(TRANSCODER_EDP); if (DISPLAY_VER(display) >= 11) panel_transcoder_mask |= BIT(TRANSCODER_DSI_0) | BIT(TRANSCODER_DSI_1); @@ -3768,13 +3817,13 @@ static u8 hsw_panel_transcoders(struct intel_display *display) return panel_transcoder_mask; } -static u8 hsw_enabled_transcoders(struct intel_crtc *crtc) +static u16 hsw_enabled_transcoders(struct intel_crtc *crtc) { struct intel_display *display = to_intel_display(crtc); - u8 panel_transcoder_mask = hsw_panel_transcoders(display); + u16 panel_transcoder_mask = hsw_panel_transcoders(display); enum transcoder cpu_transcoder; u8 primary_pipe, secondary_pipes; - u8 enabled_transcoders = 0; + u16 enabled_transcoders = 0; /* * XXX: Do intel_display_power_get_if_enabled before reading this (for @@ -3835,18 +3884,18 @@ static u8 hsw_enabled_transcoders(struct intel_crtc *crtc) return enabled_transcoders; } -static bool has_edp_transcoders(u8 enabled_transcoders) +static bool has_edp_transcoders(u16 enabled_transcoders) { return enabled_transcoders & BIT(TRANSCODER_EDP); } -static bool has_dsi_transcoders(u8 enabled_transcoders) +static bool has_dsi_transcoders(u16 enabled_transcoders) { return enabled_transcoders & (BIT(TRANSCODER_DSI_0) | BIT(TRANSCODER_DSI_1)); } -static bool has_pipe_transcoders(u8 enabled_transcoders) +static bool has_pipe_transcoders(u16 enabled_transcoders) { return enabled_transcoders & ~(BIT(TRANSCODER_EDP) | BIT(TRANSCODER_DSI_0) | @@ -3854,7 +3903,7 @@ static bool has_pipe_transcoders(u8 enabled_transcoders) } static void assert_enabled_transcoders(struct intel_display *display, - u8 enabled_transcoders) + u16 enabled_transcoders) { /* Only one type of transcoder please */ drm_WARN_ON(display->drm, @@ -5373,6 +5422,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(pch_pfit.casf.strength); PIPE_CONF_CHECK_I(scaler_state.scaler_id); + PIPE_CONF_CHECK_I(pixel_rate_cdclk); PIPE_CONF_CHECK_I(pixel_rate); PIPE_CONF_CHECK_X(gamma_mode); @@ -5871,7 +5921,7 @@ static int intel_atomic_check_crtcs(struct intel_atomic_state *state) } static bool intel_cpu_transcoders_need_modeset(struct intel_atomic_state *state, - u8 transcoders) + u16 transcoders) { const struct intel_crtc_state *new_crtc_state; struct intel_crtc *crtc; @@ -6507,7 +6557,7 @@ int intel_atomic_check(struct drm_device *dev, } if (is_trans_port_sync_mode(new_crtc_state)) { - u8 trans = new_crtc_state->sync_mode_slaves_mask; + u16 trans = new_crtc_state->sync_mode_slaves_mask; if (new_crtc_state->master_transcoder != INVALID_TRANSCODER) trans |= BIT(new_crtc_state->master_transcoder); diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 17f7d3abdb9c..7e988b7b1fe7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -97,6 +97,8 @@ struct intel_wm_funcs { struct intel_audio_state { struct intel_encoder *encoder; u8 eld[MAX_ELD_BYTES]; + /* MST, or SST on UHBR link */ + bool needs_cpu_transcoder_id; }; struct intel_audio { diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index f77b3da2cff5..7121e7cd9512 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -146,6 +146,7 @@ struct intel_display_platforms { func(supports_tv); #define HAS_128B_Y_TILING(__display) (!(__display)->platform.i915g && !(__display)->platform.i915gm) +#define HAS_2PPC(__display) (DISPLAY_VER(__display) >= 10) #define HAS_4TILE(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14) #define HAS_ASYNC_FLIPS(__display) (DISPLAY_VER(__display) >= 5) #define HAS_AS_SDP(__display) (DISPLAY_VER(__display) >= 13) @@ -192,8 +193,10 @@ struct intel_display_platforms { #define HAS_MBUS_JOINING(__display) ((__display)->platform.alderlake_p || DISPLAY_VER(__display) >= 14) #define HAS_MSO(__display) (DISPLAY_VER(__display) >= 12) #define HAS_OVERLAY(__display) (DISPLAY_INFO(__display)->has_overlay) +#define HAS_PEAK_BW_THRESHOLD(__display) (DISPLAY_VER(__display) >= 30) #define HAS_PIPEDMC(__display) (DISPLAY_VER(__display) >= 12) #define HAS_PIXEL_NORMALIZER(__display) (DISPLAY_VER(__display) >= 35) +#define HAS_PMDEMAND(__display) (DISPLAY_VER(__display) >= 14) #define HAS_PSR(__display) (DISPLAY_INFO(__display)->has_psr) #define HAS_PSR_HW_TRACKING(__display) (DISPLAY_INFO(__display)->has_psr_hw_tracking) #define HAS_PSR2_SEL_FETCH(__display) (DISPLAY_VER(__display) >= 12) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index bb5301b90231..70d112fd4287 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -887,3 +887,74 @@ void intel_display_driver_pm_resume(struct intel_display *display) intel_display_power_enable(display); } + +void intel_display_driver_runtime_pm_enable(struct intel_display *display) +{ + intel_display_power_enable(display); +} + +void intel_display_driver_runtime_pm_disable(struct intel_display *display) +{ + intel_display_power_disable(display); +} + +/* before irq suspend */ +void intel_display_driver_pm_runtime_suspend(struct intel_display *display) +{ +} + +/* after irq suspend */ +void intel_display_driver_pm_runtime_suspend_late(struct intel_display *display) +{ + intel_display_power_runtime_suspend(display); + + /* + * FIXME: We really should find a document that references the arguments + * used below! + */ + if (display->platform.broadwell) { + /* + * On Broadwell, if we use PCI_D1 the PCH DDI ports will stop + * being detected, and the call we do at i915_pm_runtime_resume() + * won't be able to restore them. Since PCI_D3hot matches the + * actual specification and appears to be working, use it. + */ + intel_opregion_notify_adapter(display, PCI_D3hot); + } else { + /* + * current versions of firmware which depend on this opregion + * notification have repurposed the D1 definition to mean + * "runtime suspended" vs. what you would normally expect (D3) + * to distinguish it from notifications that might be sent via + * the suspend path. + */ + intel_opregion_notify_adapter(display, PCI_D1); + } + + if (!display->platform.valleyview && !display->platform.cherryview) + intel_hpd_poll_enable(display); +} + +/* before irq resume */ +void intel_display_driver_pm_runtime_resume_early(struct intel_display *display) +{ + intel_opregion_notify_adapter(display, PCI_D0); + + intel_display_power_runtime_resume(display); +} + +/* after irq resume */ +void intel_display_driver_pm_runtime_resume(struct intel_display *display) +{ + /* + * On VLV/CHV display interrupts are part of the display + * power well, so hpd is reinitialized from there. For + * everyone else do it here. + */ + if (!display->platform.valleyview && !display->platform.cherryview) { + intel_hpd_init(display); + intel_hpd_poll_disable(display); + } + + skl_watermark_ipc_update(display); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h index 7eca3d17dd82..1ae2ad7e95f6 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.h +++ b/drivers/gpu/drm/i915/display/intel_display_driver.h @@ -43,5 +43,13 @@ void intel_display_driver_suspend_access(struct intel_display *display); void intel_display_driver_resume_access(struct intel_display *display); bool intel_display_driver_check_access(struct intel_display *display); +void intel_display_driver_runtime_pm_enable(struct intel_display *display); +void intel_display_driver_runtime_pm_disable(struct intel_display *display); + +void intel_display_driver_pm_runtime_suspend(struct intel_display *display); +void intel_display_driver_pm_runtime_suspend_late(struct intel_display *display); +void intel_display_driver_pm_runtime_resume_early(struct intel_display *display); +void intel_display_driver_pm_runtime_resume(struct intel_display *display); + #endif /* __INTEL_DISPLAY_DRIVER_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index bcb0ee22fb56..a59b75830bd1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1262,7 +1262,7 @@ gen8_de_misc_irq_handler(struct intel_display *display, u32 iir) } } - if (DISPLAY_VER(display) >= 14) { + if (HAS_PMDEMAND(display)) { if (iir & (XELPDP_PMDEMAND_RSP | XELPDP_PMDEMAND_RSPTOUT_ERR)) { if (iir & XELPDP_PMDEMAND_RSPTOUT_ERR) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 8e312f5b9f6d..0ebec6e0c240 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -1290,7 +1290,7 @@ static void gen9_dbuf_enable(struct intel_display *display) slices_mask = BIT(DBUF_S1) | display->dbuf.enabled_slices; - if (DISPLAY_VER(display) >= 14) + if (HAS_PMDEMAND(display)) intel_pmdemand_program_dbuf(display, slices_mask); /* @@ -1304,7 +1304,7 @@ static void gen9_dbuf_disable(struct intel_display *display) { gen9_dbuf_slices_update(display, 0); - if (DISPLAY_VER(display) >= 14) + if (HAS_PMDEMAND(display)) intel_pmdemand_program_dbuf(display, 0); } diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 39e50423132f..0f5018482497 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -1747,6 +1747,9 @@ #define XELPD_CHICKEN_DCPR_3 _MMIO(0x46438) #define DMD_RSP_TIMEOUT_DISABLE REG_BIT(19) +#define XE3P_CHICKEN_DCPR_4 _MMIO(0x454a0) +#define DCPR4_BLOCK_DC3CO_ACTIVE_FRAME REG_BIT(24) + #define SKL_DFSM _MMIO(0x51000) #define SKL_DFSM_DISPLAY_PM_DISABLE (1 << 27) #define SKL_DFSM_DISPLAY_HDCP_DISABLE (1 << 25) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index c048da7d6fea..b7cc361fd955 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1083,6 +1083,13 @@ struct intel_crtc_state { */ unsigned int pixel_rate; + /* + * Pipe pixel rate for CDCLK, adjusted for + * panel fitter/pipe scaler downscaling. + * CDCLK use cases need further adjustment. + */ + unsigned int pixel_rate_cdclk; + /* Whether to set up the PCH/FDI. Note that we never allow sharing * between pch encoders and cpu encoders. */ bool has_pch_encoder; @@ -1372,7 +1379,7 @@ struct intel_crtc_state { enum transcoder master_transcoder; /* Bitmask to indicate slaves attached */ - u8 sync_mode_slaves_mask; + u16 sync_mode_slaves_mask; /* Only valid on TGL+ */ enum transcoder mst_master_transcoder; @@ -1871,6 +1878,7 @@ struct intel_dp { struct drm_dp_tunnel *tunnel; bool tunnel_suspended:1; + u8 disabled_uhbr_lane_mask; struct { struct intel_dp_mst_encoder *stream_encoders[I915_MAX_PIPES]; diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.c b/drivers/gpu/drm/i915/display/intel_display_wa.c index 2094eda09c91..3662e0f17c69 100644 --- a/drivers/gpu/drm/i915/display/intel_display_wa.c +++ b/drivers/gpu/drm/i915/display/intel_display_wa.c @@ -112,6 +112,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa, DISPLAY_VERx100(display) == 1401; case INTEL_DISPLAY_WA_14025769978: return DISPLAY_VER(display) == 35; + case INTEL_DISPLAY_WA_14026643300: + return DISPLAY_VER(display) == 35; case INTEL_DISPLAY_WA_15013987218: return DISPLAY_VER(display) == 20; case INTEL_DISPLAY_WA_15018326506: @@ -138,6 +140,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa, STEP_A0, STEP_B0); case INTEL_DISPLAY_WA_16029024088: return DISPLAY_VER(display) >= 35; + case INTEL_DISPLAY_WA_16030862157: + return DISPLAY_VER(display) == 35; case INTEL_DISPLAY_WA_18034343758: return DISPLAY_VER(display) == 20 || (display->platform.pantherlake && diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.h b/drivers/gpu/drm/i915/display/intel_display_wa.h index 9cdd148ea4fa..338b32e4162d 100644 --- a/drivers/gpu/drm/i915/display/intel_display_wa.h +++ b/drivers/gpu/drm/i915/display/intel_display_wa.h @@ -43,6 +43,7 @@ enum intel_display_wa { INTEL_DISPLAY_WA_14016740474, INTEL_DISPLAY_WA_14020863754, INTEL_DISPLAY_WA_14025769978, + INTEL_DISPLAY_WA_14026643300, INTEL_DISPLAY_WA_15013987218, INTEL_DISPLAY_WA_15018326506, INTEL_DISPLAY_WA_16011181250, @@ -53,6 +54,7 @@ enum intel_display_wa { INTEL_DISPLAY_WA_16025573575, INTEL_DISPLAY_WA_16025596647, INTEL_DISPLAY_WA_16029024088, + INTEL_DISPLAY_WA_16030862157, INTEL_DISPLAY_WA_18034343758, INTEL_DISPLAY_WA_22010178259, INTEL_DISPLAY_WA_22010947358, diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 11f5dbf91e68..a191eee240d9 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -505,14 +505,16 @@ static void pipedmc_clock_gating_wa(struct intel_display *display, bool enable) static u32 pipedmc_interrupt_mask(struct intel_display *display) { - /* - * TODO: Check if PIPEDMC_ERROR bit enabling causes errors - * on PTL, enable it if validation passes - */ if (DISPLAY_VER(display) >= 35) return PIPEDMC_FLIPQ_PROG_DONE | PIPEDMC_ERROR; + if (DISPLAY_VER(display) >= 30) + return PIPEDMC_FLIPQ_PROG_DONE | + PIPEDMC_GTT_FAULT | + PIPEDMC_ATS_FAULT | + PIPEDMC_ERROR; + /* * FIXME PIPEDMC_ERROR not enabled for now due to LNL pipe B * triggering it during the first DC state transition. Figure diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index ade7e51e7590..113d767e62e9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -191,6 +191,29 @@ static void intel_dp_set_default_sink_rates(struct intel_dp *intel_dp) intel_dp->num_sink_rates = 1; } +static bool dprx_supports_128b132b(struct intel_dp *intel_dp) +{ + if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + return drm_dp_tunnel_128b132b_supported(intel_dp->tunnel); + else + return drm_dp_128b132b_supported(intel_dp->dpcd); +} + +static u8 dprx_128b132b_link_rates(struct intel_dp *intel_dp, u8 no_bwa_rates) +{ + u8 ret; + + if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + return no_bwa_rates; + + ret = drm_dp_tunnel_128b132b_dprx_rates(intel_dp->tunnel); + static_assert(DP_TUNNELING_10GBPS_PER_LANE_SUPPORT == DP_UHBR10 && + DP_TUNNELING_13_5GBPS_PER_LANE_SUPPORT == DP_UHBR13_5 && + DP_TUNNELING_20GBPS_PER_LANE_SUPPORT == DP_UHBR20); + + return ret; +} + /* update sink rates from dpcd */ static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp) { @@ -199,6 +222,7 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp) }; int i, max_rate; int max_lttpr_rate; + u8 uhbr_rates = 0; if (drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS)) { /* Needed, e.g., for Apple MBP 2017, 15 inch eDP Retina panel */ @@ -225,16 +249,19 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp) } /* + * The following register must be read unconditionally for the later + * DP tunnel 128b132b detection to work, see DP Standard v2.1 5.14.3 . + */ + drm_dp_dpcd_read_byte(&intel_dp->aux, DP_128B132B_SUPPORTED_LINK_RATES, &uhbr_rates); + + /* * Sink rates for 128b/132b. If set, sink should support all 8b/10b * rates and 10 Gbps. */ - if (drm_dp_128b132b_supported(intel_dp->dpcd)) { - u8 uhbr_rates = 0; - + if (dprx_supports_128b132b(intel_dp)) { BUILD_BUG_ON(ARRAY_SIZE(intel_dp->sink_rates) < ARRAY_SIZE(dp_rates) + 3); - drm_dp_dpcd_readb(&intel_dp->aux, - DP_128B132B_SUPPORTED_LINK_RATES, &uhbr_rates); + uhbr_rates = dprx_128b132b_link_rates(intel_dp, uhbr_rates); if (drm_dp_lttpr_count(intel_dp->lttpr_common_caps)) { /* We have a repeater */ @@ -354,44 +381,6 @@ static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp) return min3(source_max, sink_max, lane_max); } -int intel_dp_max_lane_count(struct intel_dp *intel_dp) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config max_link_limits; - struct intel_dp_link_config forced_params; - int lane_count; - - intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - - if (forced_params.lane_count) - lane_count = forced_params.lane_count; - else - lane_count = max_link_limits.lane_count; - - switch (lane_count) { - case 1: - case 2: - case 4: - return lane_count; - default: - MISSING_CASE(lane_count); - return 1; - } -} - -static int intel_dp_min_lane_count(struct intel_dp *intel_dp) -{ - struct intel_dp_link_config forced_params; - - intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - - if (forced_params.lane_count) - return forced_params.lane_count; - - return 1; -} - int intel_dp_link_bw_overhead(int link_clock, int lane_count, int hdisplay, int dsc_slice_count, int bpp_x16, unsigned long flags) { @@ -701,7 +690,8 @@ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); if (intel_dp_link_caps_update(intel_dp->link.caps, common_rates, num_common_rates, - intel_dp_get_max_common_lane_count(intel_dp))) + intel_dp_get_max_common_lane_count(intel_dp), + intel_dp->reset_link_params)) params_changed = true; return params_changed; @@ -1330,6 +1320,7 @@ intel_dp_mode_valid_format(struct intel_connector *connector, struct intel_dp *intel_dp = intel_attached_dp(connector); enum intel_output_format output_format; int max_rate, mode_rate, max_lanes, max_link_clock; + struct intel_dp_link_config max_bw_config; u16 dsc_max_compressed_bpp = 0; enum drm_mode_status status; bool dsc = false; @@ -1342,8 +1333,9 @@ intel_dp_mode_valid_format(struct intel_connector *connector, output_format = intel_dp_output_format(connector, sink_format); - max_link_clock = intel_dp_max_link_rate(intel_dp); - max_lanes = intel_dp_max_lane_count(intel_dp); + intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config); + max_link_clock = max_bw_config.rate; + max_lanes = max_bw_config.lane_count; max_rate = intel_dp_max_link_data_rate(intel_dp, max_link_clock, max_lanes); @@ -1537,39 +1529,6 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) intel_dp_link_caps_print_common_rates(intel_dp->link.caps); } -int -intel_dp_max_link_rate(struct intel_dp *intel_dp) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config max_link_limits; - struct intel_dp_link_config forced_params; - int len; - - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - - if (forced_params.rate) - return forced_params.rate; - - intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); - len = intel_dp_common_len_rate_limit(link_caps, max_link_limits.rate); - - return intel_dp_common_rate(link_caps, len - 1); -} - -static int -intel_dp_min_link_rate(struct intel_dp *intel_dp) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config forced_params; - - intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - - if (forced_params.rate) - return forced_params.rate; - - return intel_dp_common_rate(link_caps, 0); -} - int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) { struct intel_display *display = to_intel_display(intel_dp); @@ -1751,48 +1710,54 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state, const struct link_config_limits *limits) { + struct intel_connector *connector = to_intel_connector(conn_state->connector); + int bpp, clock = intel_dp_mode_clock(pipe_config, conn_state); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state); - int link_rate, link_avail; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + int err = -EINVAL; + int link_avail; for (bpp = fxp_q4_to_int(limits->link.max_bpp_x16); bpp >= fxp_q4_to_int(limits->link.min_bpp_x16); bpp -= 2 * 3) { int link_bpp_x16 = intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp); + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_iter iter; - for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { - link_rate = intel_dp_common_rate(link_caps, i); - if (link_rate < limits->min_rate || - link_rate > limits->max_rate) - continue; - - for (lane_count = limits->min_lane_count; - lane_count <= limits->max_lane_count; - lane_count <<= 1) { - const struct drm_display_mode *adjusted_mode = + intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter); + for_each_dp_link_config(&iter, &link_config) { + const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - int mode_rate = - intel_dp_link_required(link_rate, lane_count, - clock, adjusted_mode->hdisplay, - link_bpp_x16, 0); - - link_avail = intel_dp_max_link_data_rate(intel_dp, - link_rate, - lane_count); - - if (mode_rate <= link_avail) { - pipe_config->lane_count = lane_count; - pipe_config->pipe_bpp = bpp; - pipe_config->port_clock = link_rate; - - return 0; - } + int mode_rate; + + mode_rate = intel_dp_link_required(link_config.rate, + link_config.lane_count, + clock, adjusted_mode->hdisplay, + link_bpp_x16, 0); + + link_avail = intel_dp_max_link_data_rate(intel_dp, + link_config.rate, + link_config.lane_count); + + if (mode_rate <= link_avail) { + pipe_config->lane_count = link_config.lane_count; + pipe_config->pipe_bpp = bpp; + pipe_config->port_clock = link_config.rate; + + err = 0; + + break; } } + intel_dp_link_caps_iter_end(&iter); + + if (!err) + break; } - return -EINVAL; + return err; } int intel_dp_dsc_max_src_input_bpc(struct intel_display *display) @@ -1986,63 +1951,63 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, const struct link_config_limits *limits, int dsc_bpp_x16) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - int link_rate, lane_count; - int i; + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_iter iter; + int err = -EINVAL; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter); + for_each_dp_link_config(&iter, &link_config) { + /* + * FIXME: intel_dp_mtp_tu_compute_config() requires + * ->lane_count and ->port_clock set before we know + * they'll work. If we end up failing altogether, + * they'll remain in crtc state. This shouldn't matter, + * as we'd then bail out from compute config, but it's + * just ugly. + */ + pipe_config->lane_count = link_config.lane_count; + pipe_config->port_clock = link_config.rate; - for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { - link_rate = intel_dp_common_rate(link_caps, i); - if (link_rate < limits->min_rate || link_rate > limits->max_rate) - continue; + if (drm_dp_is_uhbr_rate(link_config.rate)) { + int ret; - for (lane_count = limits->min_lane_count; - lane_count <= limits->max_lane_count; - lane_count <<= 1) { + ret = intel_dp_mtp_tu_compute_config(intel_dp, + pipe_config, + conn_state, + dsc_bpp_x16, + dsc_bpp_x16, + 0, true); + if (ret) + continue; + } 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_config.rate, + link_config.lane_count, + adjusted_mode->crtc_clock, + adjusted_mode->hdisplay, + line_slice_count, + dsc_bpp_x16, + bw_overhead_flags)) + continue; + } - /* - * FIXME: intel_dp_mtp_tu_compute_config() requires - * ->lane_count and ->port_clock set before we know - * they'll work. If we end up failing altogether, - * they'll remain in crtc state. This shouldn't matter, - * as we'd then bail out from compute config, but it's - * just ugly. - */ - pipe_config->lane_count = lane_count; - pipe_config->port_clock = link_rate; - - if (drm_dp_is_uhbr_rate(link_rate)) { - int ret; - - ret = intel_dp_mtp_tu_compute_config(intel_dp, - pipe_config, - conn_state, - dsc_bpp_x16, - dsc_bpp_x16, - 0, true); - if (ret) - continue; - } 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, - line_slice_count, - dsc_bpp_x16, - bw_overhead_flags)) - continue; - } + err = 0; - return 0; - } + break; } + intel_dp_link_caps_iter_end(&iter); - return -EINVAL; + return err; } static u16 intel_dp_dsc_max_delta_bppx16(const struct intel_connector *connector, @@ -2250,7 +2215,7 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp, int pipe_bpp) { struct intel_display *display = to_intel_display(intel_dp); - const struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_connector *connector = to_intel_connector(conn_state->connector); int min_bpp_x16, max_bpp_x16, bpp_step_x16; int bpp_x16; int ret; @@ -2262,8 +2227,19 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp, max_bpp_x16 = align_max_compressed_bpp_x16(connector, pipe_config->output_format, pipe_bpp, max_bpp_x16); if (intel_dp_is_edp(intel_dp)) { - pipe_config->port_clock = limits->max_rate; - pipe_config->lane_count = limits->max_lane_count; + struct intel_dp_link_config max_link_config; + + /* + * FIXME: Clarify why eDP does not use the regular SST BW + * check and instead always uses the maximum link config, + * regardless of intel_dp::use_max_params. Then unify this eDP + * path with the regular DP path. + */ + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return -EINVAL; + + pipe_config->port_clock = max_link_config.rate; + pipe_config->lane_count = max_link_config.lane_count; pipe_config->dsc.compressed_bpp_x16 = max_bpp_x16; @@ -2576,6 +2552,20 @@ bool intel_dp_mode_valid_with_dsc(struct intel_connector *connector, bw_overhead_flags); } +bool +intel_dp_get_connector_max_link_config(struct intel_connector *connector, + const struct link_config_limits *limits, + struct intel_dp_link_config *max_link_config) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + + return intel_dp_link_caps_get_max_config(link_caps, order.key, limits->link_config_filter, + max_link_config); +} + /* * Calculate the output link min, max bpp values in limits based on the pipe bpp * range, crtc_state and dsc mode. Return true on success. @@ -2592,6 +2582,7 @@ intel_dp_compute_config_link_bpp_limits(struct intel_connector *connector, &crtc_state->hw.adjusted_mode; const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); const struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_dp_link_config max_link_config; int max_link_bpp_x16; max_link_bpp_x16 = min(crtc_state->max_link_bpp_x16, @@ -2621,14 +2612,17 @@ intel_dp_compute_config_link_bpp_limits(struct intel_connector *connector, limits->link.max_bpp_x16 = max_link_bpp_x16; + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return false; + drm_dbg_kms(display->drm, - "[ENCODER:%d:%s][CRTC:%d:%s] DP link limits: pixel clock %d kHz DSC %s max lanes %d max rate %d max pipe_bpp %d min link_bpp " FXP_Q4_FMT " max link_bpp " FXP_Q4_FMT "\n", + "[ENCODER:%d:%s][CRTC:%d:%s] DP link limits: pixel clock %d kHz DSC %s max link %dx%d max pipe_bpp %d min link_bpp " FXP_Q4_FMT " max link_bpp " FXP_Q4_FMT "\n", encoder->base.base.id, encoder->base.name, crtc->base.base.id, crtc->base.name, adjusted_mode->crtc_clock, str_on_off(dsc), - limits->max_lane_count, - limits->max_rate, + max_link_config.lane_count, + max_link_config.rate, limits->pipe.max_bpp, FXP_Q4_ARGS(limits->link.min_bpp_x16), FXP_Q4_ARGS(limits->link.max_bpp_x16)); @@ -2679,17 +2673,12 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, struct link_config_limits *limits) { struct intel_display *display = to_intel_display(intel_dp); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST); struct intel_connector *connector = to_intel_connector(conn_state->connector); - limits->min_rate = intel_dp_min_link_rate(intel_dp); - limits->max_rate = intel_dp_max_link_rate(intel_dp); - - limits->min_rate = min(limits->min_rate, limits->max_rate); - - limits->min_lane_count = intel_dp_min_lane_count(intel_dp); - limits->max_lane_count = intel_dp_max_lane_count(intel_dp); + limits->link_config_filter = INTEL_DP_LINK_CAPS_FILTER_ALL; limits->pipe.min_bpp = intel_dp_min_bpp(crtc_state->output_format); if (is_mst) { @@ -2720,6 +2709,20 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, crtc_state))); } + /* + * HDMI knows no 6 bpc transport format, and DSC with an at least + * 8 bpc input provides a better output quality than a dithered + * 6 bpc output. Prefer it for HDMI sinks, by failing the + * uncompressed link config for modes which would fit only with a + * 6 bpc pipe BPP. Honor a lower limit set via the max bpc + * connector property. + */ + if (!dsc && intel_dp_has_hdmi_sink(intel_dp) && + intel_dp_supports_dsc(intel_dp, connector, crtc_state) && + limits->pipe.max_bpp >= 24 && + crtc_state->pipe_bpp >= 24) + limits->pipe.min_bpp = max(limits->pipe.min_bpp, 24); + if (limits->pipe.min_bpp <= 0 || limits->pipe.min_bpp > limits->pipe.max_bpp) { drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] Invalid pipe bpp range: %d-%d\n", @@ -2754,6 +2757,9 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, crtc_state->pipe_bpp, limits->pipe.max_bpp); if (is_mst || intel_dp->use_max_params) { + struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE; + struct intel_dp_link_config max_config; + /* * For MST we always configure max link bw - the spec doesn't * seem to suggest we should do otherwise. @@ -2765,11 +2771,17 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, * configuration, and typically on older panels these * values correspond to the native resolution of the panel. */ - limits->min_lane_count = limits->max_lane_count; - limits->min_rate = limits->max_rate; + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_config)) + return false; + + if (!intel_dp_link_caps_filter_add(link_caps, &new_filter, &max_config)) + return false; + + limits->link_config_filter = new_filter; } - intel_dp_test_compute_config(intel_dp, crtc_state, limits); + if (!intel_dp_test_compute_config(connector, crtc_state, limits)) + return false; return intel_dp_compute_config_link_bpp_limits(connector, crtc_state, @@ -3163,8 +3175,7 @@ static void intel_dp_compute_vsc_sdp(struct intel_dp *intel_dp, { struct drm_dp_vsc_sdp *vsc; - if ((!intel_dp->colorimetry_support || - !intel_dp_needs_vsc_sdp(crtc_state, conn_state)) && + if (!intel_dp_needs_vsc_colorimetry(crtc_state, conn_state) && !crtc_state->has_psr) return; @@ -3173,8 +3184,7 @@ static void intel_dp_compute_vsc_sdp(struct intel_dp *intel_dp, crtc_state->infoframes.enable |= intel_hdmi_infoframe_enable(DP_SDP_VSC); vsc->sdp_type = DP_SDP_VSC; - /* Needs colorimetry */ - if (intel_dp_needs_vsc_sdp(crtc_state, conn_state)) { + if (intel_dp_needs_vsc_colorimetry(crtc_state, conn_state)) { intel_dp_compute_vsc_colorimetry(crtc_state, conn_state, vsc); } else if (crtc_state->has_panel_replay) { @@ -3652,7 +3662,14 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, void intel_dp_reset_link_params(struct intel_dp *intel_dp) { + /* + * TODO: Remove the following reset of link capabilities, as + * this isn't needed after intel_dp_link_caps_update(reset=true) + * was called. + */ intel_dp_link_caps_reset(intel_dp->link.caps); + intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp); + intel_dp->link.mst_probed_lane_count = 0; intel_dp->link.mst_probed_rate = 0; intel_dp_link_training_reset(intel_dp->link.training); @@ -4121,7 +4138,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) rate_per_lane = info->hdmi.max_frl_rate_per_lane; max_frl_rate = max_lanes * rate_per_lane; - if (info->hdmi.dsc_cap.v_1p2) { + /* + * The sink's DSC max FRL rate only applies to compressed video + * transport, which requires a DSC 1.2 encoder in the PCON. Without + * one the HDMI link always carries uncompressed video, for which + * the regular max FRL rate is the limit. + */ + if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && + info->hdmi.dsc_cap.v_1p2) { max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; if (max_dsc_lanes && dsc_rate_per_lane) @@ -5100,14 +5124,22 @@ static bool intel_dp_get_and_ack_sink_irq_esi_sst(struct intel_dp *intel_dp, u8 } bool -intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state, - const struct drm_connector_state *conn_state) +intel_dp_needs_vsc_colorimetry(const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { + struct intel_dp *intel_dp = + enc_to_intel_dp(to_intel_encoder(conn_state->best_encoder)); + /* * As per DP 1.4a spec section 2.2.4.3 [MSA Field for Indication * of Color Encoding Format and Content Color Gamut], in order to - * sending YCBCR 420 or HDR BT.2020 signals we should use DP VSC SDP. + * send YCBCR 420 or HDR BT.2020 signals we should use DP VSC SDP. + * Only signal this when the sink advertises VSC SDP colorimetry + * support. */ + if (!intel_dp->colorimetry_support) + return false; + if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) return true; @@ -6305,6 +6337,8 @@ intel_dp_detect(struct drm_connector *_connector, intel_dp_tunnel_disconnect(intel_dp); + intel_dp_tunnel_uhbr_lanes_wa_reset(intel_dp); + goto out_unset_edid; } @@ -6369,6 +6403,9 @@ intel_dp_detect(struct drm_connector *_connector, if (intel_dp_is_edp(intel_dp) || connector->detect_edid) status = connector_status_connected; + if (intel_dp_tunnel_uhbr_lanes_wa_setup(intel_dp)) + intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp); + out_unset_edid: if (status != connector_status_connected && !intel_dp->is_mst) intel_dp_unset_edid(intel_dp); @@ -6583,7 +6620,7 @@ static int intel_modeset_tile_group(struct intel_atomic_state *state, return ret; } -static int intel_modeset_affected_transcoders(struct intel_atomic_state *state, u8 transcoders) +static int intel_modeset_affected_transcoders(struct intel_atomic_state *state, u16 transcoders) { struct intel_display *display = to_intel_display(state); struct intel_crtc *crtc; @@ -6631,7 +6668,7 @@ static int intel_modeset_synced_crtcs(struct intel_atomic_state *state, drm_atomic_get_old_connector_state(&state->base, &connector->base); const struct intel_crtc_state *old_crtc_state; struct intel_crtc *crtc; - u8 transcoders; + u16 transcoders; crtc = to_intel_crtc(old_conn_state->crtc); if (!crtc) diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 02b691df6755..04fae203bb69 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -8,6 +8,8 @@ #include <linux/types.h> +#include "intel_dp_link_caps.h" + enum intel_output_format; enum pipe; enum port; @@ -22,11 +24,11 @@ struct intel_crtc_state; struct intel_digital_port; struct intel_display; struct intel_dp; +struct intel_dp_link_config; struct intel_encoder; struct link_config_limits { - int min_rate, max_rate; - int min_lane_count, max_lane_count; + struct intel_dp_link_caps_filter link_config_filter; struct { /* Uncompressed DSC input or link output bpp in 1 bpp units */ int min_bpp, max_bpp; @@ -103,8 +105,6 @@ void intel_dp_mst_suspend(struct intel_display *display); void intel_dp_mst_resume(struct intel_display *display); int intel_dp_rate_limit_len(const int *rates, int len, int max_rate); int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port); -int intel_dp_max_link_rate(struct intel_dp *intel_dp); -int intel_dp_max_lane_count(struct intel_dp *intel_dp); int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); int intel_dp_rate_index(const int *rates, int len, int rate); @@ -128,8 +128,8 @@ int intel_dp_max_link_data_rate(struct intel_dp *intel_dp, bool intel_dp_joiner_needs_dsc(struct intel_display *display, int num_joined_pipes); bool intel_dp_has_joiner(struct intel_dp *intel_dp); -bool intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state, - const struct drm_connector_state *conn_state); +bool intel_dp_needs_vsc_colorimetry(const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state); void intel_dp_set_infoframes(struct intel_encoder *encoder, bool enable, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); @@ -144,6 +144,9 @@ int intel_dp_dsc_compute_max_bpp(const struct intel_connector *connector, u8 dsc_max_bpc); int intel_dp_compute_min_compressed_bpp_x16(struct intel_connector *connector, enum intel_output_format output_format); +bool intel_dp_get_connector_max_link_config(struct intel_connector *connector, + const struct link_config_limits *limits, + struct intel_dp_link_config *max_link_config); bool intel_dp_mode_valid_with_dsc(struct intel_connector *connector, int link_clock, int lane_count, int mode_clock, int mode_hdisplay, diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c index 1c34ba6c49c3..7b6cc6055da8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -16,9 +16,91 @@ #include "intel_display_core.h" #include "intel_display_types.h" +#include "intel_display_utils.h" #include "intel_dp.h" #include "intel_dp_link_caps.h" +/** + * DOC: DisplayPort link capabilities + * + * The Intel DP link caps API tracks the supported and allowed + * DisplayPort link configurations for a DP encoder and its attached + * connectors, and provides helpers to iterate over the allowed + * configurations and constrain them by filtering, disabling, or + * limiting them to maximum link parameters. + * + * Locking + * ------- + * + * All accesses to this API must be serialized. The only exception + * is intel_dp_link_caps_get_max_limits(), which allow lockless + * lookup. Such lookups may observe an out-of-sync &struct + * intel_dp_link_config tuple, i.e. a rate from one state and a lane + * count from another. + * + * The Intel i915/xe drivers ensure the above serialization by holding + * &drm_mode_config.connection_mutex and, while holding the lock, + * waiting for any pending asynchronous atomic commits. This also allows + * use of the API from the tails of asynchronous atomic commits, which + * cannot hold the lock. + * + * Iterating and restricting link configurations + * --------------------------------------------- + * + * The link configuration iterators can iterate the ``allowed + * configurations`` during modeset configuration selection or link + * training fallback handling in a configurable order. + * + * The iteration order can depend on connector type (eDP, DP SST, + * DP MST) and modeset-specific conditions or driver policies, such + * as DSC vs. non-DSC modes, power saving vs. better user experience, + * or policy changes after a link training failure. + * + * The configurations exposed via the iterators can be additionally + * constrained in the following ways: + * + * - Filtered for a given modeset based on modeset-specific conditions. + * Examples for such conditions include driver policies preferring + * power saving or better user experience, post-link training failure + * preference changes, or sink automated test requests limiting the + * usable configurations. + * + * - Disabled permanently for the connected sink. Examples of reasons + * to disable a configuration include a link training failure for a + * given configuration or a driver workaround preventing the use of + * a particular configuration. + * + * - Limited via a maximum link rate and lane count. For example, after + * a link training failure, subsequent modesets may be limited to + * configurations at or below the failed parameters. + * + * This mechanism exists for backward compatibility only. Eventually, + * it will be removed in favor of relying solely on individually + * disabled configurations, as described above. + * + * Terminology + * ----------- + * + * ``Common link capabilities`` (or ``common caps``) refer to the link + * rates and maximum lane count supported by both the source and the + * sink, i.e. the intersection of their respective capabilities. + * + * ``Supported configurations`` are all configurations defined by the + * ``Common link capabilities``' link rates and maximum lane count. + * + * ``Disabled configurations`` are ``Supported configurations`` disabled + * via this API. + * + * ``Enabled configurations`` are ``Supported configurations`` that are + * not disabled. + * + * ``Forced configurations`` are ``Enabled configurations`` forced via + * forced link parameter debugfs entries. + * + * ``Allowed configurations`` are the ``Enabled configurations``, or if + * forcing is in effect the ``Forced configurations``, constrained by a + * maximum rate and lane count set via the API. + */ struct intel_dp_link_caps { struct intel_dp *dp; @@ -42,6 +124,44 @@ struct intel_dp_link_caps { } configs[INTEL_DP_MAX_LINK_CONFIGS]; /* + * Indices to intel_dp_link_caps::configs[] in rate/lane count, + * lane_count/rate order. + */ + u8 rate_lane_map[INTEL_DP_MAX_LINK_CONFIGS]; + u8 lane_rate_map[INTEL_DP_MAX_LINK_CONFIGS]; + + /* + * Filter of configurations enabled for the current sink + * connection. + * + * Each bit in the filter's configuration mask corresponds to a + * configuration index in the intel_dp_link_caps::configs[] array. + * + * All configurations start out enabled in the filter after a + * new sink is connected. Users disable configurations afterwards + * via the link caps API. All configurations get re-enabled + * internally in the following cases: + * - when forcing a link rate or lane count + * - when intel_dp_link_caps_update(reset=true) is called after + * a new sink is connected + * - when intel_dp_link_caps_update(reset=false) with changed + * link capabilities is called + * - when intel_dp_link_caps_reset() is called after a new sink + * is connected + */ + struct intel_dp_link_caps_filter enabled_configs; + + /* + * Allowed configurations are the supported configurations defined by + * config_table.rates and config_table.max_lane_count, constrained by + * config_table.enabled_configs and the forced_params and + * max_limits values below. + * + * See get_allowed_config_filter() for the filter of these + * configurations. + */ + + /* * Forced parameters requested via debugfs. Remains set across sink * disconnects. */ @@ -58,16 +178,78 @@ struct intel_dp_link_caps { */ struct intel_dp_link_config max_limits; }; +static_assert(BITS_PER_TYPE(((struct intel_dp_link_caps_filter *)NULL)->config_mask) >= + ARRAY_SIZE(((struct intel_dp_link_caps *)NULL)->configs)); + +static struct intel_dp_link_caps_order bw_desc_config_order(void) +{ + struct intel_dp_link_caps_order order = { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }; + + return order; +} + +static enum intel_dp_link_caps_order_key +connector_compute_order_key(bool is_mst) +{ + if (is_mst) + return INTEL_DP_LINK_CAPS_ORDER_KEY_BW; + else + return INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE; +} + +static enum intel_dp_link_caps_order_key +connector_fallback_order_key(bool is_mst) +{ + if (is_mst) + return INTEL_DP_LINK_CAPS_ORDER_KEY_BW; + else + return INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE; +} + +static enum intel_dp_link_caps_order_direction +connector_compute_order_dir(bool is_mst, bool use_max_params) +{ + if (is_mst || use_max_params) + return INTEL_DP_LINK_CAPS_ORDER_DIR_DESC; + else + return INTEL_DP_LINK_CAPS_ORDER_DIR_ASC; +} + +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_compute_order(struct intel_connector *connector) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps_order order = { + .key = connector_compute_order_key(connector->mst.dp), + .dir = connector_compute_order_dir(connector->mst.dp, intel_dp->use_max_params) + }; + + return order; +} + +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_fallback_order(bool is_mst) +{ + struct intel_dp_link_caps_order order = { + .key = connector_fallback_order_key(is_mst), + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }; + + return order; +} /* Get length of common rates array potentially limited by max_rate. */ -int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, - int max_rate) +static int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, + int max_rate) { return intel_dp_rate_limit_len(link_caps->rates, link_caps->num_rates, max_rate); } -int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) +static int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) { struct intel_display *display = to_intel_display(link_caps->dp); @@ -78,24 +260,12 @@ int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) return link_caps->rates[index]; } -int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate) -{ - return intel_dp_rate_index(link_caps->rates, - link_caps->num_rates, - rate); -} - /* Theoretical max between source and sink */ -int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) +static int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) { return intel_dp_common_rate(link_caps, link_caps->num_rates - 1); } -int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps) -{ - return link_caps->num_rates; -} - void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) { struct intel_display *display = to_intel_display(link_caps->dp); @@ -108,7 +278,7 @@ void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); } -int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) +static int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) { return link_caps->max_lane_count; } @@ -154,20 +324,353 @@ static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_ent return 1 << lce->lane_count_exp; } -static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, - const struct intel_dp_link_config *max_link_limits) +static void +to_intel_dp_link_config(struct intel_dp_link_caps *link_caps, + int config_idx, struct intel_dp_link_config *config) +{ + const struct intel_dp_link_config_entry *lce = &link_caps->configs[config_idx]; + + config->rate = intel_dp_link_config_rate(link_caps, lce); + config->lane_count = intel_dp_link_config_lane_count(lce); +} + +static int +iter_pos_to_idx(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order config_order, + int iter_pos) +{ + int config_idx; + + if (!in_range(iter_pos, 0, link_caps->num_configs)) + return -1; + + switch (config_order.dir) { + case INTEL_DP_LINK_CAPS_ORDER_DIR_ASC: + break; + case INTEL_DP_LINK_CAPS_ORDER_DIR_DESC: + iter_pos = link_caps->num_configs - 1 - iter_pos; + + break; + default: + MISSING_CASE(config_order.dir); + + return -1; + } + + switch (config_order.key) { + case INTEL_DP_LINK_CAPS_ORDER_KEY_BW: + config_idx = iter_pos; + + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: + config_idx = link_caps->rate_lane_map[iter_pos]; + + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: + config_idx = link_caps->lane_rate_map[iter_pos]; + + break; + default: + MISSING_CASE(config_order.key); + + return -1; + } + + return config_idx; +} + +static bool iter_get_next_config(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_config *config) +{ + while (true) { + int config_idx; + + iter->pos++; + + config_idx = iter_pos_to_idx(iter->link_caps, iter->order, iter->pos); + if (config_idx < 0) { + iter->pos = -1; + *config = INTEL_DP_LINK_CONFIG_NULL; + + break; + } + + if (!(BIT(config_idx) & iter->filter.config_mask)) + continue; + + to_intel_dp_link_config(iter->link_caps, config_idx, config); + + break; + } + + return iter->pos >= 0; +} + +static void iter_start(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order order, + struct intel_dp_link_caps_filter filter) +{ + iter->link_caps = link_caps; + iter->pos = -1; + iter->order = order; + iter->filter = filter; + + iter->get_next_config = iter_get_next_config; +} + +static struct intel_dp_link_caps_filter +calc_allowed_config_filter(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter enabled_configs, + const struct intel_dp_link_config *max_limits, + const struct intel_dp_link_config *forced_params) +{ + struct intel_dp_link_caps_filter allowed_configs = INTEL_DP_LINK_CAPS_FILTER_NONE; + struct intel_dp_link_caps_order order = bw_desc_config_order(); + struct intel_dp_link_caps_iter iter; + struct intel_dp_link_config config; + + iter_start(&iter, link_caps, order, enabled_configs); + for_each_dp_link_config(&iter, &config) { + if (forced_params->rate && + forced_params->rate != config.rate) + continue; + + if (forced_params->lane_count && + forced_params->lane_count != config.lane_count) + continue; + + if (config.rate > max_limits->rate) + continue; + + if (config.lane_count > max_limits->lane_count) + continue; + + allowed_configs.config_mask |= BIT(iter_pos_to_idx(link_caps, order, iter.pos)); + } + intel_dp_link_caps_iter_end(&iter); + + return allowed_configs; +} + +/* + * get_allowed_config_filter - get filter for the currently allowed configs + * @link_caps: link capabilities state + * + * Return: + * Filter of link configurations allowed after applying the current + * maximum link limits, and further narrowing them by removing any disabled + * configuration and limiting to forced link parameters. + * + * See also: + * - intel_dp_link_caps_get_max_limits() + * - intel_dp_link_caps_get_forced_params() + */ +static struct intel_dp_link_caps_filter +get_allowed_config_filter(struct intel_dp_link_caps *link_caps) +{ + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + return calc_allowed_config_filter(link_caps, link_caps->enabled_configs, + &link_caps->max_limits, &forced_params); +} + +void intel_dp_link_caps_iter_start(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order order, + struct intel_dp_link_caps_filter filter) +{ + filter.config_mask &= get_allowed_config_filter(link_caps).config_mask; + + iter_start(iter, link_caps, order, filter); +} + +void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter) +{ + memset(iter, 0, sizeof(*iter)); +} + +/** + * intel_dp_link_caps_get_max_config - get the maximum config in a given order + * @link_caps: link capabilities state + * @order_key: ordering key used to rank candidate configurations + * @filter: filter for candidate configurations + * @max_config: returned maximum link configuration + * + * Find the last configuration among the currently allowed + * configurations filtered by @filter in the iteration order + * selected by @order_key, and store it in @max_config. + * + * See also: + * - &enum intel_dp_link_caps_order_key + * + * Returns: + * %true if a maximum config is returned + * %false otherwise. + */ +bool intel_dp_link_caps_get_max_config(struct intel_dp_link_caps *link_caps, + enum intel_dp_link_caps_order_key order_key, + struct intel_dp_link_caps_filter filter, + struct intel_dp_link_config *max_config) +{ + struct intel_dp_link_caps_order order = { + .key = order_key, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC + }; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + bool found = false; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, filter); + for_each_dp_link_config(&iter, &iter_config) { + found = true; + break; + } + intel_dp_link_caps_iter_end(&iter); + + if (!found) + return false; + + *max_config = iter_config; + + return true; +} + +/** + * intel_dp_link_caps_get_max_bw_config - get maximum BW link configuration + * @link_caps: link capabilities state + * @max_config: returned maximum link configuration + * + * Return the maximum BW link configuration among the currently + * allowed configurations. + */ +void intel_dp_link_caps_get_max_bw_config(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_config) +{ + if (!intel_dp_link_caps_get_max_config(link_caps, + bw_desc_config_order().key, INTEL_DP_LINK_CAPS_FILTER_ALL, + max_config)) + *max_config = INTEL_DP_LINK_CONFIG_NULL; +} + +static int find_config_idx(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter filter, + const struct intel_dp_link_config *link_config) +{ + struct intel_dp_link_caps_order order = bw_desc_config_order(); + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int pos = -1; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, filter); + for_each_dp_link_config(&iter, &iter_config) { + if (iter_config.rate == link_config->rate && + iter_config.lane_count == link_config->lane_count) { + pos = iter.pos; + + break; + } + } + intel_dp_link_caps_iter_end(&iter); + + if (pos < 0) + return pos; + + return iter_pos_to_idx(link_caps, order, pos); +} + +bool intel_dp_link_caps_filter_add(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter *filter, + const struct intel_dp_link_config *config) +{ + int idx; + + idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config); + if (idx < 0) + return false; + + filter->config_mask |= BIT(idx); + + return true; +} + +static bool intel_dp_link_caps_filter_remove(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter *filter, + const struct intel_dp_link_config *config) +{ + int idx; + + idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config); + if (idx < 0) + return false; + + filter->config_mask &= ~BIT(idx); + + return true; +} + +static void set_max_link_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) { link_caps->max_limits = *max_link_limits; } -static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps) +static void reset_max_link_limits(struct intel_dp_link_caps *link_caps) { struct intel_dp_link_config max_link_limits = { .rate = intel_dp_max_common_rate(link_caps), .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps), }; - set_max_link_limits_no_update(link_caps, &max_link_limits); + set_max_link_limits(link_caps, &max_link_limits); +} + +static void reset_max_link_limits_reenable_all(struct intel_dp_link_caps *link_caps) +{ + link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL; + reset_max_link_limits(link_caps); +} + +/** + * intel_dp_link_caps_disable_config - disable a configuration + * @link_caps: link capabilities state + * @config: configuration to disable + * + * Disable the configuration identified by @config. This removes the + * configuration from the set of allowed configurations. The disabling + * shouldn't leave the remaining configuration set empty. + * + * The configuration remains disallowed until intel_dp_link_caps() with + * reset=%true or changed sink capabilities is called, or + * intel_dp_link_caps_reset() is called. Each of these happens after a + * new sink is connected or the currently connected sink changes its + * capabilities. + * + * Return: + * - %true if @config was valid and the derived state was updated. + * - %false if @config was invalid or the remaining configuration set + * would remain empty. + */ +bool intel_dp_link_caps_disable_config(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *config) +{ + struct intel_dp_link_caps_filter enabled_configs = link_caps->enabled_configs; + struct intel_dp_link_config forced_params; + + if (!intel_dp_link_caps_filter_remove(link_caps, &enabled_configs, config)) + return false; + + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + if (!calc_allowed_config_filter(link_caps, enabled_configs, + &link_caps->max_limits, &forced_params).config_mask) + return false; + + link_caps->enabled_configs = enabled_configs; + + return true; } /** @@ -197,6 +700,25 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, *max_link_limits = link_caps->max_limits; } +static bool max_link_limits_valid(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + struct intel_dp_link_caps_filter allowed_configs; + struct intel_dp_link_config forced_params; + + if (max_link_limits->lane_count > INTEL_DP_MAX_LANE_COUNT || + !is_power_of_2(max_link_limits->lane_count)) + return false; + + /* TODO: Validate max_link_limits->rate against the source supported rates. */ + + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + allowed_configs = calc_allowed_config_filter(link_caps, link_caps->enabled_configs, + max_link_limits, &forced_params); + + return allowed_configs.config_mask != 0; +} + /** * intel_dp_link_caps_set_max_limits - set the current maximum link limits * @link_caps: link capabilities state @@ -205,6 +727,10 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, * Set the current maximum rate and lane count limits to @max_link_limits, * constraining the set of allowed configurations. * + * The new limits must leave at least one configuration allowed: the limits + * must not be below the currently active forced parameters or below all the + * configurations that remain after disabled configurations are excluded. + * * Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize * this call against concurrent queries and updates to @link_caps, in line * with the rest of the API. @@ -217,9 +743,11 @@ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits) { - set_max_link_limits_no_update(link_caps, max_link_limits); + if (!max_link_limits_valid(link_caps, max_link_limits)) + return false; + + set_max_link_limits(link_caps, max_link_limits); - /* TODO: validate max_link_limits */ return true; } @@ -232,7 +760,7 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, */ void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) { - reset_max_link_limits_no_update(link_caps); + reset_max_link_limits(link_caps); } static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps, @@ -259,14 +787,70 @@ static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) intel_dp_link_config_rate(link_caps, lce_b); } -/* Return %true if the supported link parameters have changed. */ +static int link_config_cmp_by_rate_lane(const void *a, const void *b, const void *p) +{ + const struct intel_dp_link_caps *link_caps = p; + u8 *lce_a_idx = (u8 *)a; + u8 *lce_b_idx = (u8 *)b; + const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx]; + const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx]; + + if (lce_a->link_rate_idx != lce_b->link_rate_idx) + return lce_a->link_rate_idx - lce_b->link_rate_idx; + + return lce_a->lane_count_exp - lce_b->lane_count_exp; +} + +static int link_config_cmp_by_lane_rate(const void *a, const void *b, const void *p) +{ + const struct intel_dp_link_caps *link_caps = p; + u8 *lce_a_idx = (u8 *)a; + u8 *lce_b_idx = (u8 *)b; + const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx]; + const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx]; + + if (lce_a->lane_count_exp != lce_b->lane_count_exp) + return lce_a->lane_count_exp - lce_b->lane_count_exp; + + return lce_a->link_rate_idx - lce_b->link_rate_idx; +} + +/** + * intel_dp_link_caps_update - rebuild the supported link configuration state + * @link_caps: link capabilities state + * @rates: supported common link rates + * @num_rates: number of entries in @rates + * @max_lane_count: supported maximum lane count + * @reset: reset limits and disabled configs + * + * Rebuild the supported link configuration state from @rates and + * @max_lane_count. + * + * If @reset is %true, reset the maximum link limits to the maximum + * supported rate and lane count, and re-enable all configurations. + * + * This function is called regularly, at least after a sink is connected, + * but it may also be called later whenever the sink capabilities may have + * changed, for example in response to HPD IRQ / RX_CAP_CHANGED signaling. + * + * In the Intel driver this function is currently called whenever the + * connector detect handler runs, after reading the sink capabilities. This + * may change if those capabilities are cached until the sink is + * disconnected, or until RX_CAP_CHANGED is signaled. In any case, this + * function should be called whenever the sink capabilities were read out + * and may have changed. + * + * Returns: + * - %true if the link capabilities have changed, %false otherwise. + */ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, - const int *rates, int num_rates, int max_lane_count) + const int *rates, int num_rates, int max_lane_count, + bool reset) { struct intel_dp *intel_dp = link_caps->dp; struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_config_entry *lce; - bool link_params_changed = false; + bool link_params_changed = reset; int num_common_lane_configs; int i; int j; @@ -313,41 +897,25 @@ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, link_config_cmp_by_bw, NULL, intel_dp); - return link_params_changed; -} - -void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, - int idx, int *link_rate, int *lane_count) -{ - struct intel_display *display = to_intel_display(link_caps->dp); - const struct intel_dp_link_config_entry *lce; - - if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs)) - idx = 0; - - lce = &link_caps->configs[idx]; - - *link_rate = intel_dp_link_config_rate(link_caps, lce); - *lane_count = intel_dp_link_config_lane_count(lce); -} + for (i = 0; i < link_caps->num_configs; i++) { + link_caps->rate_lane_map[i] = i; + link_caps->lane_rate_map[i] = i; + } -int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, - int link_rate, int lane_count) -{ - int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates, - link_rate); - int lane_count_exp = ilog2(lane_count); - int i; + sort_r(link_caps->rate_lane_map, link_caps->num_configs, + sizeof(link_caps->rate_lane_map[0]), + link_config_cmp_by_rate_lane, NULL, + link_caps); - for (i = 0; i < link_caps->num_configs; i++) { - const struct intel_dp_link_config_entry *lce = &link_caps->configs[i]; + sort_r(link_caps->lane_rate_map, link_caps->num_configs, + sizeof(link_caps->lane_rate_map[0]), + link_config_cmp_by_lane_rate, NULL, + link_caps); - if (lce->lane_count_exp == lane_count_exp && - lce->link_rate_idx == link_rate_idx) - return i; - } + if (link_params_changed) + reset_max_link_limits_reenable_all(link_caps); - return -1; + return link_params_changed; } /** @@ -364,8 +932,7 @@ int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, */ void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps) { - /* TODO: Update the maximum link information. */ - reset_max_link_limits_no_update(link_caps); + reset_max_link_limits_reenable_all(link_caps); } static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) @@ -628,6 +1195,43 @@ static int i915_dp_max_lane_count_show(void *data, u64 *val) } DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); +static int intel_dp_allowed_link_configs_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_iter iter; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + i = 0; + intel_dp_link_caps_iter_start(&iter, + link_caps, + intel_dp_link_caps_connector_compute_order(connector), + INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &link_config) { + seq_printf(m, "%s%dx%d", + i ? " " : "", + link_config.lane_count, link_config.rate); + i++; + } + intel_dp_link_caps_iter_end(&iter); + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_putc(m, '\n'); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(intel_dp_allowed_link_configs); /** * intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector @@ -654,6 +1258,9 @@ void intel_dp_link_caps_debugfs_add(struct intel_connector *connector) debugfs_create_file("i915_dp_max_lane_count", 0444, root, connector, &i915_dp_max_lane_count_fops); + + debugfs_create_file("intel_dp_allowed_link_configs", 0444, root, + connector, &intel_dp_allowed_link_configs_fops); } struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) @@ -665,6 +1272,7 @@ struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) return NULL; link_caps->dp = intel_dp; + link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL; return link_caps; } @@ -673,3 +1281,32 @@ void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps) { kfree(link_caps); } + +#if IS_ENABLED(CONFIG_KUNIT) + +#define __INIT_MEMBER(__name, __fn) \ + .__name = __fn, + +#define INTEL_DP_LINK_CAPS_TEST_OPS_INIT \ + INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__INIT_MEMBER) + +#ifdef I915 + +const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops = { + INTEL_DP_LINK_CAPS_TEST_OPS_INIT +}; +EXPORT_SYMBOL(i915_display_dp_link_caps_test_ops); + +#else + +const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops = { + INTEL_DP_LINK_CAPS_TEST_OPS_INIT +}; +EXPORT_SYMBOL(intel_display_dp_link_caps_test_ops); + +#endif /* I915 */ + +#undef INTEL_DP_LINK_CAPS_TEST_OPS_INIT +#undef __INIT_MEMBER + +#endif /* CONFIG_KUNIT */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h index af9028e7cb98..a0a88efb9546 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -4,6 +4,7 @@ #ifndef __INTEL_DP_LINK_CAPS_H__ #define __INTEL_DP_LINK_CAPS_H__ +#include <linux/bitops.h> #include <linux/types.h> struct intel_connector; @@ -11,23 +12,132 @@ struct intel_dp; struct intel_dp_link_caps; struct intel_dp_link_config; -int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, - int max_rate); -int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index); -int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate); -int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps); -int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); -int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps); +/** + * enum intel_dp_link_caps_order_key - key used to order configurations + * @INTEL_DP_LINK_CAPS_ORDER_KEY_BW: + * Order configurations by bandwidth, then by link rate. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: + * Order configurations by link rate, then by lane count. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: + * Order configurations by lane count, then by link rate. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_NUM: + * Number of ordering keys. + * + * Selects how a caller wants the configuration table to be ordered, + * together with an &enum intel_dp_link_caps_order_direction, for + * iteration queries. + * + * See also: + * - &struct intel_dp_link_caps_order + * - intel_dp_link_caps_get_max_config() + */ +enum intel_dp_link_caps_order_key { + INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE, + + INTEL_DP_LINK_CAPS_ORDER_KEY_NUM +}; + +/** + * enum intel_dp_link_caps_order_direction - iteration direction + * @INTEL_DP_LINK_CAPS_ORDER_DIR_ASC: + * Iterate in ascending order according to the selected ordering key. + * @INTEL_DP_LINK_CAPS_ORDER_DIR_DESC: + * Iterate in descending order according to the selected ordering key. + * @INTEL_DP_LINK_CAPS_ORDER_DIR_NUM: + * Number of ordering directions. + * + * Selects the direction associated with an + * &enum intel_dp_link_caps_order_key for iteration queries. + * + * See also: + * - &struct intel_dp_link_caps_order + */ +enum intel_dp_link_caps_order_direction { + INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + + INTEL_DP_LINK_CAPS_ORDER_DIR_NUM +}; + +/** + * struct intel_dp_link_caps_order - configuration ordering + * @key: + * Key used to order configurations. + * @dir: + * Direction of the selected ordering. + * + * Describes an iteration order for link configurations. + * + * See also: + * - for_each_dp_link_config() + */ +struct intel_dp_link_caps_order { + enum intel_dp_link_caps_order_key key; + enum intel_dp_link_caps_order_direction dir; +}; + +struct intel_dp_link_caps_filter { + u32 config_mask; +}; + +#define INTEL_DP_LINK_CAPS_FILTER_NONE \ + ((struct intel_dp_link_caps_filter){ .config_mask = 0 }) +#define INTEL_DP_LINK_CAPS_FILTER_ALL \ + ((struct intel_dp_link_caps_filter){ .config_mask = (u32)-1 }) + +struct intel_dp_link_caps_iter { + struct intel_dp_link_caps *link_caps; + int pos; + struct intel_dp_link_caps_order order; + struct intel_dp_link_caps_filter filter; + + bool (*get_next_config)(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_config *config); +}; + +/** + * for_each_dp_link_config - iterate allowed link configurations + * @__iter: + * &struct intel_dp_link_caps_iter being iterated + * @__config: + * pointer to &struct intel_dp_link_config filled for each match + */ +#define for_each_dp_link_config(__iter, __config) \ + while ((__iter)->get_next_config((__iter), (__config))) + +void intel_dp_link_caps_iter_start(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order order, + struct intel_dp_link_caps_filter filter); + +void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter); + +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_compute_order(struct intel_connector *connector); +struct intel_dp_link_caps_order +intel_dp_link_caps_connector_fallback_order(bool is_mst); void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps); void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params); -int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, - int link_rate, int lane_count); -void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, - int idx, int *link_rate, int *lane_count); +bool intel_dp_link_caps_filter_add(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_filter *filter, + const struct intel_dp_link_config *config); + +bool intel_dp_link_caps_get_max_config(struct intel_dp_link_caps *link_caps, + enum intel_dp_link_caps_order_key order_key, + struct intel_dp_link_caps_filter filter, + struct intel_dp_link_config *max_config); + +void intel_dp_link_caps_get_max_bw_config(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_config); + +bool intel_dp_link_caps_disable_config(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *config); void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits); @@ -36,7 +146,8 @@ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps); bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, - const int *rates, int num_rates, int max_lane_count); + const int *rates, int num_rates, int max_lane_count, + bool reset); void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps); void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); @@ -44,4 +155,41 @@ void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); +#if IS_ENABLED(CONFIG_KUNIT) + +#define INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__X) \ + __X(connector_compute_order, intel_dp_link_caps_connector_compute_order) \ + __X(connector_fallback_order, intel_dp_link_caps_connector_fallback_order) \ + __X(iter_start, intel_dp_link_caps_iter_start) \ + __X(iter_end, intel_dp_link_caps_iter_end) \ + __X(set_max_limits, intel_dp_link_caps_set_max_limits) \ + __X(get_max_limits, intel_dp_link_caps_get_max_limits) \ + __X(get_max_bw_config, intel_dp_link_caps_get_max_bw_config) \ + __X(reset_max_limits, intel_dp_link_caps_reset_max_limits) \ + __X(disable_config, intel_dp_link_caps_disable_config) \ + __X(update, intel_dp_link_caps_update) \ + __X(init, intel_dp_link_caps_init) \ + __X(cleanup, intel_dp_link_caps_cleanup) + +#define __DECLARE_MEMBER(__name, __fn) \ + typeof(__fn) *__name; + +#define INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE \ + INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__DECLARE_MEMBER) + +struct intel_dp_link_caps_test_ops { + INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE +}; + +#undef INTEL_DP_LINK_CAPS_TEST_OPS_DECLARE +#undef __DECLARE_MEMBER + +#ifdef I915 +extern const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops; +#else +extern const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops; +#endif /* I915 */ + +#endif /* CONFIG_KUNIT */ + #endif /* __INTEL_DP_LINK_CAPS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index b521dd11b62a..cb92cff90614 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -21,6 +21,8 @@ * IN THE SOFTWARE. */ +#include <kunit/visibility.h> + #include <linux/debugfs.h> #include <linux/iopoll.h> @@ -1846,124 +1848,57 @@ static bool intel_dp_can_link_train_fallback_for_edp(struct intel_dp *intel_dp, return true; } -static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - int *new_link_rate, int *new_lane_count) +static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, + int *new_link_rate, int *new_lane_count) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config forced_params; - int link_rate; - int lane_count; - int i; - - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - - i = intel_dp_link_config_index(intel_dp->link.caps, - crtc_state->port_clock, crtc_state->lane_count); - for (i--; i >= 0; i--) { - intel_dp_link_config_get(intel_dp->link.caps, i, &link_rate, &lane_count); + bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST); + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_fallback_order(is_mst); + struct intel_dp_link_config old_config = { + .rate = crtc_state->port_clock, + .lane_count = crtc_state->lane_count, + }; + struct intel_dp_link_caps_iter iter; + struct intel_dp_link_config config; + bool old_found = false; + bool new_found = false; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &config) { + if (!old_found) { + if (config.rate == old_config.rate && + config.lane_count == old_config.lane_count) + old_found = true; - if ((forced_params.rate && - forced_params.rate != link_rate) || - (forced_params.lane_count && - forced_params.lane_count != lane_count)) continue; + } + + *new_link_rate = config.rate; + *new_lane_count = config.lane_count; + new_found = true; break; } + intel_dp_link_caps_iter_end(&iter); - if (i < 0) - return false; - - *new_link_rate = link_rate; - *new_lane_count = lane_count; - - return true; + return new_found; } -static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - struct intel_dp_link_config forced_params; - int rate_index; - int new_rate; - - intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - if (forced_params.rate) - return -1; - - rate_index = intel_dp_link_caps_common_rate_idx(link_caps, - current_rate); - - if (rate_index <= 0) - return -1; - - new_rate = intel_dp_common_rate(link_caps, rate_index - 1); - - /* TODO: Make switching from UHBR to non-UHBR rates work. */ - if (drm_dp_is_uhbr_rate(current_rate) != drm_dp_is_uhbr_rate(new_rate)) - return -1; - - return new_rate; -} - -static int reduce_lane_count(struct intel_dp *intel_dp, int current_lane_count) -{ - struct intel_dp_link_config forced_params; - - intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - if (forced_params.lane_count) - return -1; - - if (current_lane_count == 1) - return -1; - - return current_lane_count >> 1; -} - -static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - int *new_link_rate, int *new_lane_count) -{ - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int link_rate; - int lane_count; - - lane_count = crtc_state->lane_count; - link_rate = reduce_link_rate(intel_dp, crtc_state->port_clock); - if (link_rate < 0) { - lane_count = reduce_lane_count(intel_dp, crtc_state->lane_count); - link_rate = intel_dp_max_common_rate(link_caps); - } - - if (lane_count < 0) - return false; - - *new_link_rate = link_rate; - *new_lane_count = lane_count; - - return true; -} - -static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, - int *new_link_rate, int *new_lane_count) -{ - /* TODO: Use the same fallback logic on SST as on MST. */ - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) - return reduce_link_params_in_bw_order(intel_dp, crtc_state, - new_link_rate, new_lane_count); - else - return reduce_link_params_in_rate_lane_order(intel_dp, crtc_state, - new_link_rate, new_lane_count); -} - -static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) +VISIBLE_IF_KUNIT +int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) { + struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_dp_link_config max_link_limits; + struct intel_dp_link_config current_config = { + .rate = crtc_state->port_clock, + .lane_count = crtc_state->lane_count, + }; int new_link_rate; int new_lane_count; + int err = -1; if (intel_dp_is_edp(intel_dp) && !intel_dp->use_max_params) { lt_dbg(intel_dp, DP_PHY_DPRX, @@ -1972,16 +1907,49 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, return 0; } + /* + * Temporarily reset the max link limit before selecting the fallback + * config. + * + * After fallback, the current logic narrows the allowed configurations + * to the selected config's rate and lane count. That can make a later + * fallback candidate fall outside the current max_limit, so reset it + * before searching. + * + * TODO: Constrain the allowed configurations by only disabling individual + * configurations and remove setting maximum link parameters. + */ + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); + intel_dp_link_caps_reset_max_limits(link_caps); + + /* + * TODO: Make fallback depend only on disabling the current config, + * once max_limit no longer constrains the allowed config set. Then + * disabling the current config will define the allowed configs for + * the subsequent modeset, so there will be no need to select a + * reduced config separately here. + */ if (!reduce_link_params(intel_dp, crtc_state, &new_link_rate, &new_lane_count)) - return -1; + goto out_restore_max_limits; if (intel_dp_is_edp(intel_dp) && !intel_dp_can_link_train_fallback_for_edp(intel_dp, new_link_rate, new_lane_count)) { lt_dbg(intel_dp, DP_PHY_DPRX, "Retrying Link training for eDP with same parameters\n"); - return 0; + + err = 0; + + goto out_restore_max_limits; } + /* + * Shouldn't fail: the current config was enabled, and reducing the + * link parameters should still leave the fallback config allowed. + */ + if (drm_WARN_ON(display->drm, + !intel_dp_link_caps_disable_config(link_caps, ¤t_config))) + return -1; + lt_dbg(intel_dp, DP_PHY_DPRX, "Reducing link parameters from %dx%d to %dx%d\n", crtc_state->lane_count, crtc_state->port_clock, @@ -1990,10 +1958,19 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, max_link_limits.rate = new_link_rate; max_link_limits.lane_count = new_lane_count; - /* TODO: handle an update failure */ - intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits); + err = 0; - return 0; +out_restore_max_limits: + /* + * Shouldn't fail: setting max_limits can only fail if they drop below + * the optionally forced rate/lane-count parameters, but the reduced + * config was chosen to satisfy those constraints. + */ + if (drm_WARN_ON(display->drm, + !intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits))) + err = -1; + + return err; } static bool intel_dp_schedule_fallback_link_training(struct intel_atomic_state *state, @@ -2834,3 +2811,32 @@ void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training { kfree(link_training); } + +#if IS_ENABLED(CONFIG_KUNIT) + +#define __INIT_MEMBER(__name, __fn) \ + .__name = __fn, + +#define INTEL_DP_LINK_TRAINING_TEST_OPS_INIT \ + INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__INIT_MEMBER) + +#ifdef I915 + +const struct intel_dp_link_training_test_ops i915_display_dp_link_training_test_ops = { + INTEL_DP_LINK_TRAINING_TEST_OPS_INIT +}; +EXPORT_SYMBOL(i915_display_dp_link_training_test_ops); + +#else + +const struct intel_dp_link_training_test_ops intel_display_dp_link_training_test_ops = { + INTEL_DP_LINK_TRAINING_TEST_OPS_INIT +}; +EXPORT_SYMBOL(intel_display_dp_link_training_test_ops); + +#endif /* I915 */ + +#undef INTEL_DP_LINK_TRAINING_TEST_OPS_INIT +#undef __INIT_MEMBER + +#endif /* CONFIG_KUNIT */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index ef16fcabd6da..581f2361fdfd 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -8,6 +8,8 @@ #include <drm/display/drm_dp_helper.h> +#include "intel_dp_link_caps.h" + struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; @@ -71,4 +73,33 @@ void intel_dp_link_training_reset(struct intel_dp_link_training *link_training); struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp); void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training); +#if IS_ENABLED(CONFIG_KUNIT) + +int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state); + +#define INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__X) \ + __X(get_fallback_values, intel_dp_get_link_train_fallback_values) + +#define __DECLARE_MEMBER(__name, __fn) \ + typeof(__fn) *__name; + +#define INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE \ + INTEL_DP_LINK_TRAINING_TEST_OPS_MEMBERS(__DECLARE_MEMBER) + +struct intel_dp_link_training_test_ops { + INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE +}; + +#undef INTEL_DP_LINK_TRAINING_TEST_OPS_DECLARE +#undef __DECLARE_MEMBER + +#ifdef I915 +extern const struct intel_dp_link_training_test_ops i915_display_dp_link_training_test_ops; +#else +extern const struct intel_dp_link_training_test_ops intel_display_dp_link_training_test_ops; +#endif /* I915 */ + +#endif /* CONFIG_KUNIT */ + #endif /* __INTEL_DP_LINK_TRAINING_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index ecc90e8faee1..3be1643f8d03 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -47,6 +47,7 @@ #include "intel_display_wa.h" #include "intel_dp.h" #include "intel_dp_hdcp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -444,8 +445,20 @@ static int mst_stream_compute_link_config(struct intel_dp *intel_dp, struct drm_connector_state *conn_state, const struct link_config_limits *limits) { - crtc_state->lane_count = limits->max_lane_count; - crtc_state->port_clock = limits->max_rate; + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp_link_config max_link_config; + + /* + * FIXME: Use a proper iteration over the link configurations, instead + * of using only the max BW config. For instance UHBR rate configs may + * have additional limitations over non-UHBR ones, due to the DSC DPT + * bpp maximum limit. + */ + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return -EINVAL; + + crtc_state->port_clock = max_link_config.rate; + crtc_state->lane_count = max_link_config.lane_count; /* * FIXME: allocate the BW according to link_bpp, which in the case of @@ -464,6 +477,7 @@ static int mst_stream_dsc_compute_link_config(struct intel_dp *intel_dp, { struct intel_display *display = to_intel_display(intel_dp); struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp_link_config max_link_config; crtc_state->pipe_bpp = limits->pipe.max_bpp; @@ -471,8 +485,17 @@ static int mst_stream_dsc_compute_link_config(struct intel_dp *intel_dp, "DSC Sink supported compressed min bpp " FXP_Q4_FMT " compressed max bpp " FXP_Q4_FMT "\n", FXP_Q4_ARGS(limits->link.min_bpp_x16), FXP_Q4_ARGS(limits->link.max_bpp_x16)); - crtc_state->lane_count = limits->max_lane_count; - crtc_state->port_clock = limits->max_rate; + /* + * FIXME: Use a proper iteration over the link configurations, instead + * of using only the max BW config. For instance UHBR rate configs may + * have additional limitations over non-UHBR ones, due to the DSC DPT + * bpp maximum limit. + */ + if (!intel_dp_get_connector_max_link_config(connector, limits, &max_link_config)) + return -EINVAL; + + crtc_state->port_clock = max_link_config.rate; + crtc_state->lane_count = max_link_config.lane_count; return intel_dp_mtp_tu_compute_config(intel_dp, crtc_state, conn_state, limits->link.min_bpp_x16, @@ -488,6 +511,20 @@ static int mode_hblank_period_ns(const struct drm_display_mode *mode) mode->crtc_clock); } +static int get_connector_max_rate(const struct intel_connector *connector, + const struct link_config_limits *limits) +{ + struct intel_dp *intel_dp = intel_attached_dp((struct intel_connector *)connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_config; + + intel_dp_link_caps_get_max_config(link_caps, + INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + limits->link_config_filter, &max_link_config); + + return max_link_config.rate; +} + static bool hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector, const struct intel_crtc_state *crtc_state, @@ -498,11 +535,13 @@ hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector, bool is_uhbr_sink = connector->mst.dp && drm_dp_128b132b_supported(connector->mst.dp->dpcd); int hblank_limit = is_uhbr_sink ? 500 : 300; + int max_rate; if (!connector->dp.dsc_hblank_expansion_quirk) return false; - if (is_uhbr_sink && !drm_dp_is_uhbr_rate(limits->max_rate)) + max_rate = get_connector_max_rate(connector, limits); + if (is_uhbr_sink && !drm_dp_is_uhbr_rate(max_rate)) return false; if (mode_hblank_period_ns(adjusted_mode) > hblank_limit) @@ -524,6 +563,7 @@ adjust_limits_for_dsc_hblank_expansion_quirk(struct intel_dp *intel_dp, struct intel_display *display = to_intel_display(connector); const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); int min_bpp_x16 = limits->link.min_bpp_x16; + int max_rate; if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state, limits)) return true; @@ -550,11 +590,10 @@ adjust_limits_for_dsc_hblank_expansion_quirk(struct intel_dp *intel_dp, return true; } - drm_WARN_ON(display->drm, limits->min_rate != limits->max_rate); - - if (limits->max_rate < 540000) + max_rate = get_connector_max_rate(connector, limits); + if (max_rate < 540000) min_bpp_x16 = fxp_q4_from_int(13); - else if (limits->max_rate < 810000) + else if (max_rate < 810000) min_bpp_x16 = fxp_q4_from_int(10); if (limits->link.min_bpp_x16 >= min_bpp_x16) @@ -776,7 +815,7 @@ intel_dp_mst_transcoder_mask(struct intel_atomic_state *state, struct intel_display *display = to_intel_display(state); const struct intel_digital_connector_state *conn_state; struct intel_connector *connector; - u8 transcoders = 0; + u16 transcoders = 0; int i; if (DISPLAY_VER(display) < 12) @@ -1414,6 +1453,11 @@ static int mst_connector_get_ddc_modes(struct drm_connector *_connector) drm_edid_free(drm_edid); + if (intel_dp_tunnel_uhbr_lanes_wa_setup(intel_dp)) { + intel_dp_flush_connector_commits(connector); + intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp); + } + return ret; } @@ -1476,6 +1520,7 @@ mst_connector_mode_valid_ctx(struct drm_connector *_connector, unsigned long bw_overhead_flags = DRM_DP_BW_OVERHEAD_MST | DRM_DP_BW_OVERHEAD_SSC_REF_CLK; int min_link_bpp_x16 = fxp_q4_from_int(18); + struct intel_dp_link_config max_bw_config; static bool supports_dsc; int ret; bool dsc = false; @@ -1508,8 +1553,9 @@ mst_connector_mode_valid_ctx(struct drm_connector *_connector, min_link_bpp_x16 = intel_dp_compute_min_compressed_bpp_x16(connector, INTEL_OUTPUT_FORMAT_RGB); - max_link_clock = intel_dp_max_link_rate(intel_dp); - max_lanes = intel_dp_max_lane_count(intel_dp); + intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config); + max_link_clock = max_bw_config.rate; + max_lanes = max_bw_config.lane_count; max_rate = intel_dp_max_link_data_rate(intel_dp, max_link_clock, max_lanes); @@ -2135,14 +2181,19 @@ bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state, */ void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp) { - int link_rate = intel_dp_max_link_rate(intel_dp); - int lane_count = intel_dp_max_lane_count(intel_dp); + struct intel_dp_link_config max_bw_config; + int link_rate; + int lane_count; u8 rate_select; u8 link_bw; if (intel_dp->link.active) return; + intel_dp_link_caps_get_max_bw_config(intel_dp->link.caps, &max_bw_config); + link_rate = max_bw_config.rate; + lane_count = max_bw_config.lane_count; + if (intel_mst_probed_link_params_valid(intel_dp, link_rate, lane_count)) return; diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.c b/drivers/gpu/drm/i915/display/intel_dp_test.c index da7632536dac..0551a1ce60d3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_test.c +++ b/drivers/gpu/drm/i915/display/intel_dp_test.c @@ -28,12 +28,92 @@ void intel_dp_test_reset(struct intel_dp *intel_dp) memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance)); } +static bool set_filter_for_lane_count(struct intel_connector *connector, + struct intel_dp_link_caps *link_caps, + int lane_count, + struct link_config_limits *limits) +{ + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE; + struct intel_dp_link_caps_iter iter; + bool found = false; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, limits->link_config_filter); + for_each_dp_link_config(&iter, &link_config) { + if (link_config.lane_count != lane_count) + continue; + + intel_dp_link_caps_filter_add(link_caps, &new_filter, &link_config); + found = true; + } + intel_dp_link_caps_iter_end(&iter); + + if (!found) + return false; + + limits->link_config_filter = new_filter; + + return true; +} + +static bool set_filter_for_link_config(struct intel_connector *connector, + struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *link_params, + struct link_config_limits *limits) +{ + struct intel_dp_link_caps_filter new_filter = INTEL_DP_LINK_CAPS_FILTER_NONE; + + if (!intel_dp_link_caps_filter_add(link_caps, &new_filter, link_params)) + return false; + + limits->link_config_filter = new_filter; + + return true; +} + +static bool set_filter_for_link_params(struct intel_connector *connector, + int link_rate, int lane_count, + struct link_config_limits *limits) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_display *display = to_intel_display(intel_dp); + struct intel_dp_link_config requested_config; + + requested_config.rate = link_rate; + requested_config.lane_count = lane_count; + + if (set_filter_for_link_config(connector, link_caps, &requested_config, limits)) + return true; + + /* + * Preserve the legacy behavior: if the requested (rate, lane_count) + * combination is not an allowed config, fall back to all configs + * matching the requested lane count. + * + * TODO: Recheck whether this behavior is actually correct. + */ + if (set_filter_for_lane_count(connector, link_caps, lane_count, limits)) + return true; + + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] Invalid autotest link config parameters: %dx%d\n", + encoder->base.base.id, encoder->base.name, + requested_config.lane_count, + requested_config.rate); + + return false; +} + /* Adjust link config limits based on compliance test requests. */ -void intel_dp_test_compute_config(struct intel_dp *intel_dp, +bool intel_dp_test_compute_config(struct intel_connector *connector, struct intel_crtc_state *pipe_config, struct link_config_limits *limits) { - struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_display *display = to_intel_display(intel_dp); /* For DP Compliance we override the computed bpp for the pipe */ @@ -49,23 +129,14 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, /* Use values requested by Compliance Test Request */ if (intel_dp->compliance.test_type == DP_TEST_LINK_TRAINING) { - int index; - - /* Validate the compliance test data since max values - * might have changed due to link train fallback. - */ - if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate, - intel_dp->compliance.test_lane_count)) { - index = intel_dp_link_caps_common_rate_idx(link_caps, - intel_dp->compliance.test_link_rate); - if (index >= 0) { - limits->min_rate = intel_dp->compliance.test_link_rate; - limits->max_rate = intel_dp->compliance.test_link_rate; - } - limits->min_lane_count = intel_dp->compliance.test_lane_count; - limits->max_lane_count = intel_dp->compliance.test_lane_count; - } + if (!set_filter_for_link_params(connector, + intel_dp->compliance.test_link_rate, + intel_dp->compliance.test_lane_count, + limits)) + return false; } + + return true; } /* Compliance test status bits */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.h b/drivers/gpu/drm/i915/display/intel_dp_test.h index dcc167e4c7f6..a08f37a63dc9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_test.h +++ b/drivers/gpu/drm/i915/display/intel_dp_test.h @@ -6,6 +6,7 @@ #include <linux/types.h> +struct intel_connector; struct intel_crtc_state; struct intel_display; struct intel_dp; @@ -13,7 +14,7 @@ struct link_config_limits; void intel_dp_test_reset(struct intel_dp *intel_dp); void intel_dp_test_request(struct intel_dp *intel_dp); -void intel_dp_test_compute_config(struct intel_dp *intel_dp, +bool intel_dp_test_compute_config(struct intel_connector *connector, struct intel_crtc_state *pipe_config, struct link_config_limits *limits); bool intel_dp_test_phy(struct intel_dp *intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index 76e9753766b9..219939634564 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -58,10 +58,12 @@ static int kbytes_to_mbits(int kbytes) static int get_current_link_bw(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps = intel_dp->link.caps; - int rate = intel_dp_max_common_rate(link_caps); - int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps); + struct intel_dp_link_config max_bw_config; - return intel_dp_max_link_data_rate(intel_dp, rate, lane_count); + intel_dp_link_caps_get_max_bw_config(link_caps, &max_bw_config); + + return intel_dp_max_link_data_rate(intel_dp, max_bw_config.rate, + max_bw_config.lane_count); } static int __update_tunnel_state(struct intel_dp *intel_dp, bool force_sink_update) @@ -829,6 +831,51 @@ void intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state) atomic_increase_bw(state); } +static u8 lane_count_mask(int lane_count) +{ + return BIT(ilog2(lane_count)); +} + +void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp) +{ + struct intel_connector *connector = intel_dp->attached_connector; + struct intel_dp_link_caps_order order = + intel_dp_link_caps_connector_compute_order(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config link_config; + struct intel_dp_link_caps_iter iter; + + if (!intel_dp->disabled_uhbr_lane_mask) + return; + + intel_dp_link_caps_iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &link_config) { + if (drm_dp_is_uhbr_rate(link_config.rate) && + lane_count_mask(link_config.lane_count) & intel_dp->disabled_uhbr_lane_mask) + intel_dp_link_caps_disable_config(link_caps, &link_config); + } + intel_dp_link_caps_iter_end(&iter); +} + +bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp) +{ + u8 old_mask = intel_dp->disabled_uhbr_lane_mask; + + if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp) || + drm_dp_tunnel_128b132b_lane0_mapping_supported(intel_dp->tunnel)) + intel_dp->disabled_uhbr_lane_mask = 0; + else + /* TODO: Add support for keeping 2 lanes enabled as well. */ + intel_dp->disabled_uhbr_lane_mask = lane_count_mask(1) | lane_count_mask(2); + + return intel_dp->disabled_uhbr_lane_mask != old_mask; +} + +void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp) +{ + intel_dp->disabled_uhbr_lane_mask = 0; +} + /** * intel_dp_tunnel_mgr_init - Initialize the DP tunnel manager * @display: display device diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h index 8273e681a512..13fc8fbb6e30 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h @@ -54,6 +54,10 @@ int intel_dp_tunnel_atomic_check_state(struct intel_atomic_state *state, void intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state); +void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp); +bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp); +void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp); + int intel_dp_tunnel_mgr_init(struct intel_display *display); void intel_dp_tunnel_mgr_cleanup(struct intel_display *display); @@ -129,6 +133,19 @@ intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state) return 0; } +static inline void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp) +{ +} + +static inline bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp) +{ + return false; +} + +static inline void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp) +{ +} + static inline int intel_dp_tunnel_mgr_init(struct intel_display *display) { diff --git a/drivers/gpu/drm/i915/display/intel_dram.c b/drivers/gpu/drm/i915/display/intel_dram.c index f103f7cba018..0763d0bfd98e 100644 --- a/drivers/gpu/drm/i915/display/intel_dram.c +++ b/drivers/gpu/drm/i915/display/intel_dram.c @@ -13,6 +13,7 @@ #include "intel_display_core.h" #include "intel_display_utils.h" #include "intel_display_regs.h" +#include "intel_display_wa.h" #include "intel_dram.h" #include "intel_mchbar.h" #include "intel_parent.h" @@ -794,7 +795,18 @@ static int xelpdp_get_dram_info(struct intel_display *display, struct dram_info dram_info->num_channels = REG_FIELD_GET(MTL_N_OF_POPULATED_CH_MASK, val); dram_info->num_qgv_points = REG_FIELD_GET(MTL_N_OF_ENABLED_QGV_POINTS_MASK, val); - /* PSF GV points not supported in D14+ */ + + /* + * Wa_16030862157 + * MEM_SS_INFO_GLOBAL populated-channel field is only 4 bits and + * cannot encode 16, so on Xe3p the BIOS programs the saturated field + * value (0xf) to indicate the fully-populated 16-channel config (4 + * memory controllers x 4 channels). Interpret it as 16. + */ + + if (intel_display_wa(display, INTEL_DISPLAY_WA_16030862157) && + dram_info->num_channels == REG_FIELD_MAX(MTL_N_OF_POPULATED_CH_MASK)) + dram_info->num_channels = 16; if (DISPLAY_VER(display) >= 35) dram_info->ecc_impacting_de_bw = REG_FIELD_GET(XE3P_ECC_IMPACTING_DE, val); diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index a7d02dd6ca96..f61b4a218d6e 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -59,6 +59,7 @@ #include "intel_fbc_regs.h" #include "intel_frontbuffer.h" #include "intel_parent.h" +#include "skl_universal_plane.h" #define for_each_fbc_id(__display, __fbc_id) \ for ((__fbc_id) = INTEL_FBC_A; (__fbc_id) < I915_MAX_FBCS; (__fbc_id)++) \ @@ -1305,6 +1306,9 @@ static bool intel_fbc_surface_size_ok(const struct intel_plane_state *plane_stat struct intel_display *display = to_intel_display(plane_state); unsigned int effective_w, effective_h, max_w, max_h; + if (DISPLAY_VER(display) >= 20) + return true; + intel_fbc_max_surface_size(display, &max_w, &max_h); effective_w = plane_state->view.color_plane[0].x + @@ -1315,10 +1319,18 @@ static bool intel_fbc_surface_size_ok(const struct intel_plane_state *plane_stat return effective_w <= max_w && effective_h <= max_h; } -static void intel_fbc_max_plane_size(struct intel_display *display, +static void intel_fbc_max_plane_size(const struct intel_plane_state *plane_state, unsigned int *w, unsigned int *h) { - if (DISPLAY_VER(display) >= 10) { + struct intel_display *display = to_intel_display(plane_state); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + const struct drm_framebuffer *fb = plane_state->hw.fb; + unsigned int rotation = plane_state->hw.rotation; + + if (DISPLAY_VER(display) >= 20) { + *w = intel_plane_max_width(plane, fb, 0, rotation); + *h = 4096; + } else if (DISPLAY_VER(display) >= 10) { *w = 5120; *h = 4096; } else if (DISPLAY_VER(display) >= 8 || display->platform.haswell) { @@ -1335,10 +1347,9 @@ static void intel_fbc_max_plane_size(struct intel_display *display, static bool intel_fbc_plane_size_valid(const struct intel_plane_state *plane_state) { - struct intel_display *display = to_intel_display(plane_state); unsigned int w, h, max_w, max_h; - intel_fbc_max_plane_size(display, &max_w, &max_h); + intel_fbc_max_plane_size(plane_state, &max_w, &max_h); w = drm_rect_width(&plane_state->uapi.src) >> 16; h = drm_rect_height(&plane_state->uapi.src) >> 16; @@ -1567,7 +1578,7 @@ static int _intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state) /* WaFbcExceedCdClockThreshold:hsw,bdw */ if (display->platform.haswell || display->platform.broadwell) - return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95); + return DIV_ROUND_UP(crtc_state->pixel_rate_cdclk * 100, 95); /* no FBC specific limits to worry about */ return 0; diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index c8d4e3a5ce6b..db0e36dd8722 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -262,6 +262,32 @@ err: } +static bool bios_fb_ok(const struct intel_framebuffer *fb, + const struct drm_fb_helper_surface_size *sizes) +{ + struct intel_display *display = to_intel_display(fb->base.dev); + int width = fb->base.width; + int height = fb->base.height; + int depth = fb->base.format->depth; + int bpp = fb->base.format->cpp[0] * 8; + + if (sizes->fb_width > width || sizes->fb_height > height) { + drm_dbg_kms(display->drm, + "BIOS fb too small (%dx%d), we require (%dx%d), releasing it\n", + width, height, sizes->fb_width, sizes->fb_height); + return false; + } + + if (sizes->surface_depth != depth || sizes->surface_bpp != bpp) { + drm_dbg_kms(display->drm, + "BIOS fb using wrong depth/bpp (%d/%d), we require (%d/%d), releasing it\n", + depth, bpp, sizes->surface_depth, sizes->surface_bpp); + return false; + } + + return true; +} + int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { @@ -279,14 +305,7 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, ifbdev->fb = NULL; - if (fb && - (sizes->fb_width > fb->base.width || - sizes->fb_height > fb->base.height)) { - drm_dbg_kms(display->drm, - "BIOS fb too small (%dx%d), we require (%dx%d)," - " releasing it\n", - fb->base.width, fb->base.height, - sizes->fb_width, sizes->fb_height); + if (fb && !bios_fb_ok(fb, sizes)) { drm_framebuffer_put(&fb->base); fb = NULL; } diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 0a076d2ed70a..e56df337dc6d 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1075,6 +1075,7 @@ static int intel_hdcp1_enable(struct intel_connector *connector) ret = intel_hdcp_auth(connector); if (!ret) { hdcp->hdcp_encrypted = true; + hdcp->hdcp2_encrypted = false; return 0; } @@ -2109,6 +2110,7 @@ static int _intel_hdcp2_enable(struct intel_atomic_state *state, hdcp->content_type); hdcp->hdcp2_encrypted = true; + hdcp->hdcp_encrypted = false; return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_initial_plane.c b/drivers/gpu/drm/i915/display/intel_initial_plane.c index e414b5d1085c..7427f41e4d7e 100644 --- a/drivers/gpu/drm/i915/display/intel_initial_plane.c +++ b/drivers/gpu/drm/i915/display/intel_initial_plane.c @@ -128,11 +128,10 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc, /* * TODO: - * Disable planes if get_initial_plane_config() failed. * Make sure things work if the surface base is not page aligned. */ if (!plane_config->fb) - return; + goto nofb; if (intel_alloc_initial_plane_obj(display, plane_config)) { fb = plane_config->fb; @@ -184,7 +183,8 @@ nofb: * simplest solution is to just disable the primary plane now and * pretend the BIOS never had it enabled. */ - intel_plane_disable_noatomic(crtc, plane); + if (plane_state->uapi.visible) + intel_plane_disable_noatomic(crtc, plane); } static void plane_config_fini(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c index e8730b5baf2a..6aed88173770 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c @@ -186,11 +186,11 @@ static void intel_crtc_disable_noatomic_complete(struct intel_crtc *crtc) * Return all the pipes using a transcoder in @transcoder_mask. * For joiner configs return only the joiner primary. */ -static u8 get_transcoder_pipes(struct intel_display *display, - u8 transcoder_mask) +static u16 get_transcoder_pipes(struct intel_display *display, + u16 transcoder_mask) { struct intel_crtc *temp_crtc; - u8 pipes = 0; + u16 pipes = 0; for_each_intel_crtc(display, temp_crtc) { struct intel_crtc_state *temp_crtc_state = @@ -214,7 +214,7 @@ static u8 get_transcoder_pipes(struct intel_display *display, * For joiner configs return only the joiner primary pipes. */ static void get_portsync_pipes(struct intel_crtc *crtc, - u8 *master_pipe_mask, u8 *slave_pipes_mask) + u16 *master_pipe_mask, u16 *slave_pipes_mask) { struct intel_display *display = to_intel_display(crtc); struct intel_crtc_state *crtc_state = @@ -243,10 +243,10 @@ static void get_portsync_pipes(struct intel_crtc *crtc, *slave_pipes_mask = get_transcoder_pipes(display, master_crtc_state->sync_mode_slaves_mask); } -static u8 get_joiner_secondary_pipes(struct intel_display *display, u8 primary_pipes_mask) +static u16 get_joiner_secondary_pipes(struct intel_display *display, u16 primary_pipes_mask) { struct intel_crtc *primary_crtc; - u8 pipes = 0; + u16 pipes = 0; for_each_intel_crtc_in_pipe_mask(display, primary_crtc, primary_pipes_mask) { struct intel_crtc_state *primary_crtc_state = @@ -263,9 +263,9 @@ static void intel_crtc_disable_noatomic(struct intel_crtc *crtc, { struct intel_display *display = to_intel_display(crtc); struct intel_crtc *temp_crtc; - u8 portsync_master_mask; - u8 portsync_slaves_mask; - u8 joiner_secondaries_mask; + u16 portsync_master_mask; + u16 portsync_slaves_mask; + u16 joiner_secondaries_mask; /* TODO: Add support for MST */ get_portsync_pipes(crtc, &portsync_master_mask, &portsync_slaves_mask); @@ -854,14 +854,10 @@ static void intel_modeset_readout_hw_state(struct intel_display *display) * FIXME don't have the fb yet, so can't * use plane->min_cdclk() :( */ - if (plane_state->uapi.visible && plane->min_cdclk) { - if (crtc_state->double_wide || DISPLAY_VER(display) >= 10) - crtc_state->plane_min_cdclk[plane->id] = - DIV_ROUND_UP(crtc_state->pixel_rate, 2); - else - crtc_state->plane_min_cdclk[plane->id] = - crtc_state->pixel_rate; - } + if (plane_state->uapi.visible && plane->min_cdclk) + crtc_state->plane_min_cdclk[plane->id] = + DIV_ROUND_UP(crtc_state->pixel_rate_cdclk, + intel_cdclk_ppc(display, crtc_state->double_wide)); drm_dbg_kms(display->drm, "[PLANE:%d:%s] min_cdclk %d kHz\n", plane->base.base.id, plane->base.name, diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index a440f92ff00c..d0f99a87c42e 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -264,6 +264,50 @@ unsigned int intel_adjusted_rate(const struct drm_rect *src, dst_w * dst_h); } +static unsigned int hscale_cdclk(const struct drm_rect *src, + const struct drm_rect *dst, + unsigned int ppc) +{ + unsigned int hscale; + + hscale = drm_rect_calc_hscale(src, dst, 0, INT_MAX); + hscale = max(hscale, 0x10000); + + /* + * Double the fractional part due to some 2 PPC granularity issue + * + * FIXME: BSpec calls for doubling only the <0.5 fractional part, + * and rounding it down to a unit fraction. In practice that is + * not sufficient, and we need a more aggressive CDCLK bump in + * many cases. The updated formula was derived empirically. + * This may need to be updated once we have better undestading + * of what's happening in the hardware... + */ + return (hscale & ~0xffff) + ppc * (hscale & 0xffff); +} + +static unsigned int vscale_cdclk(const struct drm_rect *src, + const struct drm_rect *dst) +{ + unsigned int vscale; + + vscale = drm_rect_calc_vscale(src, dst, 0, INT_MAX); + vscale = max(vscale, 0x10000); + + return vscale; +} + +unsigned int intel_adjusted_rate_cdclk(const struct drm_rect *src, + const struct drm_rect *dst, + unsigned int rate, + unsigned int ppc) +{ + unsigned int hscale = hscale_cdclk(src, dst, ppc); + unsigned int vscale = vscale_cdclk(src, dst); + + return DIV64_U64_ROUND_UP((u64)rate * hscale * vscale, 1ull << 32); +} + unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { @@ -284,6 +328,17 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, crtc_state->pixel_rate); } +unsigned int intel_plane_pixel_rate_cdclk(const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + unsigned int ppc = HAS_2PPC(display) ? 2 : 1; + + return intel_adjusted_rate_cdclk(&plane_state->uapi.src, + &plane_state->uapi.dst, + crtc_state->pixel_rate_cdclk, ppc); +} + unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, int color_plane) diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h index 31a6229aea73..dba2be24aae2 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.h +++ b/drivers/gpu/drm/i915/display/intel_plane.h @@ -29,8 +29,13 @@ bool intel_plane_can_async_flip(struct intel_plane *plane, unsigned int intel_adjusted_rate(const struct drm_rect *src, const struct drm_rect *dst, unsigned int rate); +unsigned int intel_adjusted_rate_cdclk(const struct drm_rect *src, + const struct drm_rect *dst, + unsigned int rate, unsigned int ppc); unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state); +unsigned int intel_plane_pixel_rate_cdclk(const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state); unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, diff --git a/drivers/gpu/drm/i915/display/intel_pmdemand.c b/drivers/gpu/drm/i915/display/intel_pmdemand.c index 6d32c52269a6..f96545a5b881 100644 --- a/drivers/gpu/drm/i915/display/intel_pmdemand.c +++ b/drivers/gpu/drm/i915/display/intel_pmdemand.c @@ -152,7 +152,7 @@ intel_pmdemand_update_phys_mask(struct intel_display *display, { enum phy phy; - if (DISPLAY_VER(display) < 14) + if (!HAS_PMDEMAND(display)) return; if (!encoder) @@ -174,7 +174,7 @@ intel_pmdemand_update_port_clock(struct intel_display *display, struct intel_pmdemand_state *pmdemand_state, enum pipe pipe, int port_clock) { - if (DISPLAY_VER(display) < 14) + if (!HAS_PMDEMAND(display)) return; pmdemand_state->ddi_clocks[pipe] = port_clock; @@ -324,7 +324,7 @@ int intel_pmdemand_atomic_check(struct intel_atomic_state *state) const struct intel_dbuf_state *new_dbuf_state; struct intel_pmdemand_state *new_pmdemand_state; - if (DISPLAY_VER(display) < 14) + if (!HAS_PMDEMAND(display)) return 0; if (!intel_pmdemand_needs_update(state)) @@ -404,7 +404,7 @@ intel_pmdemand_init_pmdemand_params(struct intel_display *display, { u32 reg1, reg2; - if (DISPLAY_VER(display) < 14) + if (!HAS_PMDEMAND(display)) return; mutex_lock(&display->pmdemand.lock); @@ -637,7 +637,7 @@ void intel_pmdemand_pre_plane_update(struct intel_atomic_state *state) const struct intel_pmdemand_state *old_pmdemand_state = intel_atomic_get_old_pmdemand_state(state); - if (DISPLAY_VER(display) < 14) + if (!HAS_PMDEMAND(display)) return; if (!new_pmdemand_state || @@ -660,7 +660,7 @@ void intel_pmdemand_post_plane_update(struct intel_atomic_state *state) const struct intel_pmdemand_state *old_pmdemand_state = intel_atomic_get_old_pmdemand_state(state); - if (DISPLAY_VER(display) < 14) + if (!HAS_PMDEMAND(display)) return; if (!new_pmdemand_state || diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 92af21d823a3..40e3d7095996 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -2100,6 +2100,18 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, else if (display->platform.alderlake_p) intel_de_rmw(display, CLKGATE_DIS_MISC, 0, CLKGATE_DIS_MISC_DMASC_GATING_DIS); + + /* + * Wa_14026643300 + * On Xe3P, restrict DC3CO entry during active frame when PSR2 is + * enabled without panel Early Transport; required to avoid pipe bad state. + * DMC honours CHICKEN_DCPR_4 bit 24 to block DC3CO entry during active frame. + */ + if (intel_display_wa(display, INTEL_DISPLAY_WA_14026643300) && + !intel_dp->psr.panel_replay_enabled && + !intel_dp->psr.su_region_et_enabled) + intel_de_rmw(display, XE3P_CHICKEN_DCPR_4, + 0, DCPR4_BLOCK_DC3CO_ACTIVE_FRAME); } /* Wa_16025596647 */ @@ -2341,6 +2353,10 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) else if (display->platform.alderlake_p) intel_de_rmw(display, CLKGATE_DIS_MISC, CLKGATE_DIS_MISC_DMASC_GATING_DIS, 0); + + if (intel_display_wa(display, INTEL_DISPLAY_WA_14026643300)) + intel_de_rmw(display, XE3P_CHICKEN_DCPR_4, + DCPR4_BLOCK_DC3CO_ACTIVE_FRAME, 0); } if (intel_dp_is_edp(intel_dp)) diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c index 6a65f92e8a03..2321bfbb8630 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite.c +++ b/drivers/gpu/drm/i915/display/intel_sprite.c @@ -241,13 +241,13 @@ int vlv_plane_min_cdclk(const struct intel_crtc_state *crtc_state, unsigned int num, den; /* - * Note that crtc_state->pixel_rate accounts for both + * Note that crtc_state->pixel_rate_cdclk accounts for both * horizontal and vertical panel fitter downscaling factors. * Pre-HSW bspec tells us to only consider the horizontal * downscaling factor here. We ignore that and just consider * both for simplicity. */ - pixel_rate = crtc_state->pixel_rate; + pixel_rate = crtc_state->pixel_rate_cdclk; vlv_plane_ratio(crtc_state, plane_state, &num, &den); @@ -550,13 +550,13 @@ int ivb_plane_min_cdclk(const struct intel_crtc_state *crtc_state, unsigned int num, den; /* - * Note that crtc_state->pixel_rate accounts for both + * Note that crtc_state->pixel_rate_cdclk accounts for both * horizontal and vertical panel fitter downscaling factors. * Pre-HSW bspec tells us to only consider the horizontal * downscaling factor here. We ignore that and just consider * both for simplicity. */ - pixel_rate = crtc_state->pixel_rate; + pixel_rate = crtc_state->pixel_rate_cdclk; ivb_plane_ratio(crtc_state, plane_state, &num, &den); @@ -570,13 +570,13 @@ static int ivb_sprite_min_cdclk(const struct intel_crtc_state *crtc_state, unsigned int num, den; /* - * Note that crtc_state->pixel_rate accounts for both + * Note that crtc_state->pixel_rate_cdclk accounts for both * horizontal and vertical panel fitter downscaling factors. * Pre-HSW bspec tells us to only consider the horizontal * downscaling factor here. We ignore that and just consider * both for simplicity. */ - pixel_rate = crtc_state->pixel_rate; + pixel_rate = crtc_state->pixel_rate_cdclk; src_w = drm_rect_width(&plane_state->uapi.src) >> 16; dst_w = drm_rect_width(&plane_state->uapi.dst); @@ -629,7 +629,7 @@ static void hsw_plane_ratio(const struct intel_crtc_state *crtc_state, int hsw_plane_min_cdclk(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { - unsigned int pixel_rate = crtc_state->pixel_rate; + unsigned int pixel_rate = crtc_state->pixel_rate_cdclk; unsigned int num, den; hsw_plane_ratio(crtc_state, plane_state, &num, &den); @@ -918,13 +918,13 @@ static int g4x_sprite_min_cdclk(const struct intel_crtc_state *crtc_state, unsigned int limit, decimate; /* - * Note that crtc_state->pixel_rate accounts for both + * Note that crtc_state->pixel_rate_cdclk accounts for both * horizontal and vertical panel fitter downscaling factors. * Pre-HSW bspec tells us to only consider the horizontal * downscaling factor here. We ignore that and just consider * both for simplicity. */ - pixel_rate = crtc_state->pixel_rate; + pixel_rate = crtc_state->pixel_rate_cdclk; /* Horizontal downscaling limits the maximum pixel rate */ hscale = drm_rect_calc_hscale(&plane_state->uapi.src, @@ -1722,6 +1722,10 @@ intel_sprite_plane_create(struct intel_display *display, DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE); + if (display->platform.valleyview || display->platform.cherryview) + drm_plane_create_blend_mode_property(&plane->base, + BIT(DRM_MODE_BLEND_PREMULTI)); + zpos = sprite + 1; drm_plane_create_zpos_immutable_property(&plane->base, zpos); diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index bffbdee76ee1..51e4f3309b8b 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -5,6 +5,7 @@ */ #include <drm/drm_print.h> +#include <drm/intel/step.h> #include "intel_alpm.h" #include "intel_cmtg.h" @@ -1106,6 +1107,11 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display); } + if (display->platform.novalake && + IS_DISPLAY_STEP(display, STEP_A0, STEP_C0)) + crtc_state->hw.adjusted_mode.crtc_vtotal = + intel_vrr_vmin_vtotal(crtc_state); + if (HAS_AS_SDP(display)) { trans_vrr_vsync = intel_de_read(display, diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 164b7d61c9a3..07a683293352 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -266,7 +266,7 @@ bool icl_is_hdr_plane(struct intel_display *display, enum plane_id plane_id) static int icl_plane_min_cdclk(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { - unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state); + unsigned int pixel_rate = intel_plane_pixel_rate_cdclk(crtc_state, plane_state); /* two pixels per clock */ return DIV_ROUND_UP(pixel_rate, 2); @@ -290,7 +290,7 @@ glk_plane_ratio(const struct intel_plane_state *plane_state, static int glk_plane_min_cdclk(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { - unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state); + unsigned int pixel_rate = intel_plane_pixel_rate_cdclk(crtc_state, plane_state); unsigned int num, den; glk_plane_ratio(plane_state, &num, &den); @@ -317,7 +317,7 @@ skl_plane_ratio(const struct intel_plane_state *plane_state, static int skl_plane_min_cdclk(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { - unsigned int pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state); + unsigned int pixel_rate = intel_plane_pixel_rate_cdclk(crtc_state, plane_state); unsigned int num, den; skl_plane_ratio(plane_state, &num, &den); @@ -1932,10 +1932,10 @@ static int intel_plane_min_height(struct intel_plane *plane, return 1; } -static int intel_plane_max_width(struct intel_plane *plane, - const struct drm_framebuffer *fb, - int color_plane, - unsigned int rotation) +int intel_plane_max_width(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) { if (plane->max_width) return plane->max_width(fb, color_plane, rotation); @@ -1943,10 +1943,10 @@ static int intel_plane_max_width(struct intel_plane *plane, return INT_MAX; } -static int intel_plane_max_height(struct intel_plane *plane, - const struct drm_framebuffer *fb, - int color_plane, - unsigned int rotation) +int intel_plane_max_height(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation) { if (plane->max_height) return plane->max_height(fb, color_plane, rotation); diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.h b/drivers/gpu/drm/i915/display/skl_universal_plane.h index 5e2451c21eeb..ad71105cc55a 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.h +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.h @@ -8,6 +8,7 @@ #include <linux/types.h> +struct drm_framebuffer; struct intel_crtc; struct intel_display; struct intel_initial_plane_config; @@ -42,5 +43,13 @@ bool icl_is_hdr_plane(struct intel_display *display, enum plane_id plane_id); u32 skl_plane_aux_dist(const struct intel_plane_state *plane_state, int color_plane); +int intel_plane_max_width(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation); +int intel_plane_max_height(struct intel_plane *plane, + const struct drm_framebuffer *fb, + int color_plane, + unsigned int rotation); #endif diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index a4ce21d4c024..ec1bfe46edef 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -275,6 +275,9 @@ void intel_sagv_pre_plane_update(struct intel_atomic_state *state) if (!intel_has_sagv(display)) return; + if (HAS_PMDEMAND(display)) + return; + if (DISPLAY_VER(display) >= 11) icl_sagv_pre_plane_update(state); else @@ -295,6 +298,9 @@ void intel_sagv_post_plane_update(struct intel_atomic_state *state) if (!intel_has_sagv(display)) return; + if (HAS_PMDEMAND(display)) + return; + if (DISPLAY_VER(display) >= 11) icl_sagv_post_plane_update(state); else diff --git a/drivers/gpu/drm/i915/display/tests/Makefile b/drivers/gpu/drm/i915/display/tests/Makefile new file mode 100644 index 000000000000..ad250974160f --- /dev/null +++ b/drivers/gpu/drm/i915/display/tests/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +subdir-ccflags-y += -I$(srctree)/drivers/gpu/drm/i915/display/ + +obj-$(CONFIG_DRM_I915_KUNIT_TEST) += i915_display_test.o +i915_display_test-y = \ + intel_dp_link_test.o diff --git a/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c b/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c new file mode 100644 index 000000000000..67c6fe9f1812 --- /dev/null +++ b/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c @@ -0,0 +1,1408 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include <kunit/test.h> + +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/log2.h> +#include <linux/prandom.h> +#include <linux/random.h> + +#include <drm/display/drm_dp_helper.h> + +#include <drm/intel/display_member.h> + +#include "intel_connector.h" +#include "intel_display_core.h" +#include "intel_display_types.h" +#include "intel_dp_link_caps.h" +#include "intel_dp_link_training.h" + +#define LINK_TEST_NUM_LANE_CONFIGS(__max_lane_count) \ + (ilog2(__max_lane_count) + 1) + +#define LINK_TEST_NUM_CONFIGS(__num_rates, __max_lane_count) \ + ((__num_rates) * LINK_TEST_NUM_LANE_CONFIGS(__max_lane_count)) + +#define LINK_TEST_MAX_LANE_COUNT ((u32)4) +#define LINK_TEST_MAX_CONFIGS LINK_TEST_NUM_CONFIGS(DP_MAX_SUPPORTED_RATES, \ + LINK_TEST_MAX_LANE_COUNT) + +#define LINK_TEST_NUM_RANDOM_ITERATIONS 50 + +struct test_ctx { + struct { + struct intel_display display; + struct device device; + struct __intel_generic_device generic_device; + + struct intel_connector connector; + struct intel_digital_port dig_port; + + struct intel_crtc_state crtc_state; + } dev; + + const struct intel_dp_link_caps_test_ops *link_caps_ops; + const struct intel_dp_link_training_test_ops *link_training_ops; + + struct rnd_state rnd; +}; + +struct link_rate_set { + const int *entries; + int size; +}; + +struct link_config_set { + struct intel_dp_link_config entries[LINK_TEST_MAX_CONFIGS]; + int size; +}; + +struct test_config_table { + struct kunit *test; + + struct link_rate_set rates; + int max_lane_count; + struct link_config_set disabled_configs; +}; + +static const int standard_dp_link_rates[] = { + 162000, 270000, 540000, 810000, 1000000, 1350000, 2000000 +}; + +#define LINK_TEST_NUM_STANDARD_RATES (ARRAY_SIZE(standard_dp_link_rates)) + +#define INIT_STANDARD_TABLE(__test, __num_rates, __max_lane_count) { \ + .test = (__test), \ + .rates = { \ + .entries = standard_dp_link_rates, \ + .size = (__num_rates), \ + }, \ + .max_lane_count = (__max_lane_count), \ +} + +static const struct link_config_set standard_dp_link_configs[] = { + [INTEL_DP_LINK_CAPS_ORDER_KEY_BW] = { /* MBps PBN */ + .entries = { + { .rate = 162000, .lane_count = 1 }, /* 162.0 3.00 */ + { .rate = 270000, .lane_count = 1 }, /* 270.0 5.00 */ + { .rate = 162000, .lane_count = 2 }, /* 324.0 6.00 */ + { .rate = 270000, .lane_count = 2 }, /* 540.0 10.00 */ + { .rate = 540000, .lane_count = 1 }, /* 540.0 10.00 */ + { .rate = 162000, .lane_count = 4 }, /* 648.0 12.00 */ + { .rate = 810000, .lane_count = 1 }, /* 810.0 15.00 */ + { .rate = 270000, .lane_count = 4 }, /* 1080.0 20.00 */ + { .rate = 540000, .lane_count = 2 }, /* 1080.0 20.00 */ + { .rate = 1000000, .lane_count = 1 }, /* 1208.9 22.39 */ + { .rate = 810000, .lane_count = 2 }, /* 1620.0 30.00 */ + { .rate = 1350000, .lane_count = 1 }, /* 1632.0 30.22 */ + { .rate = 540000, .lane_count = 4 }, /* 2160.0 40.00 */ + { .rate = 1000000, .lane_count = 2 }, /* 2417.8 44.77 */ + { .rate = 2000000, .lane_count = 1 }, /* 2417.8 44.77 */ + { .rate = 810000, .lane_count = 4 }, /* 3240.0 60.00 */ + { .rate = 1350000, .lane_count = 2 }, /* 3264.0 60.44 */ + { .rate = 1000000, .lane_count = 4 }, /* 4835.6 89.55 */ + { .rate = 2000000, .lane_count = 2 }, /* 4835.6 89.55 */ + { .rate = 1350000, .lane_count = 4 }, /* 6527.9 120.89 */ + { .rate = 2000000, .lane_count = 4 }, /* 9671.1 179.09 */ + }, + .size = LINK_TEST_NUM_CONFIGS(ARRAY_SIZE(standard_dp_link_rates), + LINK_TEST_MAX_LANE_COUNT), + }, + [INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE] = { + .entries = { + { .rate = 162000, .lane_count = 1 }, + { .rate = 162000, .lane_count = 2 }, + { .rate = 162000, .lane_count = 4 }, + + { .rate = 270000, .lane_count = 1 }, + { .rate = 270000, .lane_count = 2 }, + { .rate = 270000, .lane_count = 4 }, + + { .rate = 540000, .lane_count = 1 }, + { .rate = 540000, .lane_count = 2 }, + { .rate = 540000, .lane_count = 4 }, + + { .rate = 810000, .lane_count = 1 }, + { .rate = 810000, .lane_count = 2 }, + { .rate = 810000, .lane_count = 4 }, + + { .rate = 1000000, .lane_count = 1 }, + { .rate = 1000000, .lane_count = 2 }, + { .rate = 1000000, .lane_count = 4 }, + + { .rate = 1350000, .lane_count = 1 }, + { .rate = 1350000, .lane_count = 2 }, + { .rate = 1350000, .lane_count = 4 }, + + { .rate = 2000000, .lane_count = 1 }, + { .rate = 2000000, .lane_count = 2 }, + { .rate = 2000000, .lane_count = 4 }, + }, + .size = LINK_TEST_NUM_CONFIGS(ARRAY_SIZE(standard_dp_link_rates), + LINK_TEST_MAX_LANE_COUNT), + }, + [INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE] = { + .entries = { + { .rate = 162000, .lane_count = 1 }, + { .rate = 270000, .lane_count = 1 }, + { .rate = 540000, .lane_count = 1 }, + { .rate = 810000, .lane_count = 1 }, + { .rate = 1000000, .lane_count = 1 }, + { .rate = 1350000, .lane_count = 1 }, + { .rate = 2000000, .lane_count = 1 }, + + { .rate = 162000, .lane_count = 2 }, + { .rate = 270000, .lane_count = 2 }, + { .rate = 540000, .lane_count = 2 }, + { .rate = 810000, .lane_count = 2 }, + { .rate = 1000000, .lane_count = 2 }, + { .rate = 1350000, .lane_count = 2 }, + { .rate = 2000000, .lane_count = 2 }, + + { .rate = 162000, .lane_count = 4 }, + { .rate = 270000, .lane_count = 4 }, + { .rate = 540000, .lane_count = 4 }, + { .rate = 810000, .lane_count = 4 }, + { .rate = 1000000, .lane_count = 4 }, + { .rate = 1350000, .lane_count = 4 }, + { .rate = 2000000, .lane_count = 4 }, + }, + .size = LINK_TEST_NUM_CONFIGS(ARRAY_SIZE(standard_dp_link_rates), + LINK_TEST_MAX_LANE_COUNT), + }, +}; + +static int lookup_rate(const struct link_rate_set *rate_set, int rate) +{ + int i; + + for (i = 0; i < rate_set->size; i++) + if (rate_set->entries[i] == rate) + return i; + + return -1; +} + +static bool has_rate(const struct link_rate_set *rate_set, int rate) +{ + return lookup_rate(rate_set, rate) >= 0; +} + +static bool link_configs_match(const struct intel_dp_link_config *a, + const struct intel_dp_link_config *b) +{ + return a->rate == b->rate && a->lane_count == b->lane_count; +} + +static int lookup_config(const struct link_config_set *config_set, + const struct intel_dp_link_config *config) +{ + int i; + + for (i = 0; i < config_set->size; i++) + if (link_configs_match(&config_set->entries[i], config)) + return i; + + return -1; +} + +static bool has_config(const struct link_config_set *config_set, + const struct intel_dp_link_config *config) +{ + return lookup_config(config_set, config) >= 0; +} + +static void add_config(struct kunit *test, + struct link_config_set *config_set, + const struct intel_dp_link_config *config) +{ + KUNIT_ASSERT_LT(test, config_set->size, ARRAY_SIZE(config_set->entries)); + + config_set->entries[config_set->size] = *config; + config_set->size++; +} + +static const struct intel_dp_link_caps_order config_orders[] = { + { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + }, { + .key = INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE, + .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + } +}; + +static const struct link_config_set * +link_caps_config_order_key_to_set(struct kunit *test, enum intel_dp_link_caps_order_key key) +{ + return &standard_dp_link_configs[key]; +} + +/* + * TEST: Baseline with fixed reference table + * ----------------------------------------- + * Verify the link_caps config iterator using fixed standard DP config tables. + */ +static void baseline_test_for_order(struct kunit *test, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order config_order) +{ + struct test_ctx *ctx = test->priv; + const struct link_config_set *config_set = + link_caps_config_order_key_to_set(test, config_order.key); + const struct intel_dp_link_caps_test_ops *ops = ctx->link_caps_ops; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int pos = 0; + + ops->iter_start(&iter, link_caps, config_order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) { + int idx = pos; + + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_DESC) + idx = config_set->size - idx - 1; + + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, + &config_set->entries[idx])); + + pos++; + } + ops->iter_end(&iter); +} + +static void intel_dp_link_caps_test_baseline(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + int i; + + ops->update(link_caps, + standard_dp_link_rates, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT, + true); + + for (i = 0; i < ARRAY_SIZE(config_orders); i++) + baseline_test_for_order(test, link_caps, config_orders[i]); +} + +static int get_num_configs(int num_rates, int max_lane_count) +{ + return num_rates * LINK_TEST_NUM_LANE_CONFIGS(max_lane_count); +} + +static int rand_in_range(struct test_ctx *ctx, int min, int max) +{ + return min + (prandom_u32_state(&ctx->rnd) % (max - min + 1)); +} + +/* + * TEST: Update reset + * ------------------ + * Verify that resetting link_caps with the DP standard rates/lane + * counts updates the configuration table accordingly for all + * combinations. + */ +static void verify_bw_asc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + int config_bw = drm_dp_max_dprx_data_rate(config->rate, + config->lane_count); + int last_config_bw = drm_dp_max_dprx_data_rate(last_config->rate, + last_config->lane_count); + + KUNIT_EXPECT_GE(test, config_bw, last_config_bw); + if (config_bw == last_config_bw) + KUNIT_EXPECT_GT(test, config->rate, last_config->rate); +} + +static void verify_bw_desc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + int config_bw = drm_dp_max_dprx_data_rate(config->rate, + config->lane_count); + int last_config_bw = drm_dp_max_dprx_data_rate(last_config->rate, + last_config->lane_count); + + KUNIT_EXPECT_LE(test, config_bw, last_config_bw); + if (config_bw == last_config_bw) + KUNIT_EXPECT_LT(test, config->rate, last_config->rate); +} + +static void verify_rate_lane_asc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_GE(test, config->rate, last_config->rate); + if (config->rate == last_config->rate) + KUNIT_EXPECT_GT(test, config->lane_count, last_config->lane_count); +} + +static void verify_rate_lane_desc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_LE(test, config->rate, last_config->rate); + if (config->rate == last_config->rate) + KUNIT_EXPECT_LT(test, config->lane_count, last_config->lane_count); +} + +static void verify_lane_rate_asc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_GE(test, config->lane_count, last_config->lane_count); + if (config->lane_count == last_config->lane_count) + KUNIT_EXPECT_GT(test, config->rate, last_config->rate); +} + +static void verify_lane_rate_desc_config_order(struct kunit *test, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + KUNIT_EXPECT_LE(test, config->lane_count, last_config->lane_count); + if (config->lane_count == last_config->lane_count) + KUNIT_EXPECT_LT(test, config->rate, last_config->rate); +} + +static void verify_config_order(struct kunit *test, + struct intel_dp_link_caps_order config_order, + const struct intel_dp_link_config *last_config, + const struct intel_dp_link_config *config) +{ + switch (config_order.key) { + case INTEL_DP_LINK_CAPS_ORDER_KEY_BW: + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_ASC) + verify_bw_asc_config_order(test, last_config, config); + else + verify_bw_desc_config_order(test, last_config, config); + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_ASC) + verify_rate_lane_asc_config_order(test, last_config, config); + else + verify_rate_lane_desc_config_order(test, last_config, config); + break; + case INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: + if (config_order.dir == INTEL_DP_LINK_CAPS_ORDER_DIR_ASC) + verify_lane_rate_asc_config_order(test, last_config, config); + else + verify_lane_rate_desc_config_order(test, last_config, config); + break; + default: + KUNIT_FAIL_AND_ABORT(test, "Missing order key: %d", config_order.key); + } +} + +static int expected_num_configs(const struct test_config_table *expected_table, + const struct intel_dp_link_config *max_limits) +{ + int num_configs = 0; + int lane_count; + int rate_idx; + + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (config.rate > max_limits->rate || + config.lane_count > max_limits->lane_count) + continue; + + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + num_configs++; + } + } + + return num_configs; +} + +static void +verify_link_caps_for_order(const struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps, + struct intel_dp_link_caps_order config_order, + const struct intel_dp_link_config *max_limits) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + struct intel_dp_link_config expected_max_bw_config = {}; + struct intel_dp_link_config actual_max_bw_config; + struct intel_dp_link_config last_config = {}; + struct intel_dp_link_config old_max_limits; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int num_actual_configs = 0; + int max_bw = 0; + + ops->get_max_limits(link_caps, &old_max_limits); + ops->set_max_limits(link_caps, max_limits); + + ops->iter_start(&iter, link_caps, config_order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) { + int bw; + + KUNIT_EXPECT_LE(test, iter_config.rate, max_limits->rate); + KUNIT_EXPECT_LE(test, iter_config.lane_count, max_limits->lane_count); + + num_actual_configs++; + + /* + * Verify the config's rate/lane-count values and its ordering relative + * to the previous config. + */ + if (last_config.rate) + verify_config_order(test, config_order, &last_config, &iter_config); + last_config = iter_config; + + KUNIT_EXPECT_TRUE(test, has_rate(&expected_table->rates, + iter_config.rate)); + KUNIT_EXPECT_LE(test, iter_config.lane_count, + expected_table->max_lane_count); + KUNIT_EXPECT_TRUE(test, is_power_of_2(iter_config.lane_count)); + + /* Verify the config's disabled state */ + KUNIT_EXPECT_FALSE(test, has_config(&expected_table->disabled_configs, + &iter_config)); + + /* + * Update the max limits for allowed configs, verified at the + * end for the whole config table. + */ + + bw = drm_dp_max_dprx_data_rate(iter_config.rate, iter_config.lane_count); + if (bw > max_bw || + (bw == max_bw && iter_config.rate > expected_max_bw_config.rate)) { + max_bw = bw; + expected_max_bw_config = iter_config; + } + } + ops->iter_end(&iter); + + KUNIT_EXPECT_EQ(test, num_actual_configs, expected_num_configs(expected_table, max_limits)); + + ops->get_max_bw_config(link_caps, &actual_max_bw_config); + KUNIT_EXPECT_TRUE(test, link_configs_match(&expected_max_bw_config, + &actual_max_bw_config)); + + KUNIT_ASSERT_TRUE(test, ops->set_max_limits(link_caps, &old_max_limits)); +} + +static bool max_limits_valid(const struct test_config_table *expected_table, + const struct intel_dp_link_config *max_limits) +{ + int lane_count; + int rate_idx; + + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + if (config.rate <= max_limits->rate && + config.lane_count <= max_limits->lane_count) + return true; + } + } + + return false; +} + +static void get_max_limits(const struct test_config_table *expected_table, + struct intel_dp_link_config *max_limits) +{ + int lane_count; + int rate_idx; + + max_limits->rate = 0; + max_limits->lane_count = 0; + + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + max_limits->rate = max(max_limits->rate, config.rate); + max_limits->lane_count = max(max_limits->lane_count, config.lane_count); + } + } +} + +static void verify_link_caps(const struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = ctx->link_caps_ops; + struct intel_dp_link_config max_limits; + int i; + + get_max_limits(expected_table, &max_limits); + + for (i = 0; i < ARRAY_SIZE(config_orders); i++) { + int lane_count; + int rate_idx; + + verify_link_caps_for_order(expected_table, link_caps, config_orders[i], &max_limits); + /* + * Verify iteration after setting the max limits to each + * configurations. + */ + for (rate_idx = 0; rate_idx < expected_table->rates.size; rate_idx++) { + for (lane_count = 1; lane_count <= expected_table->max_lane_count; lane_count <<= 1) { + struct intel_dp_link_config config = { + .rate = expected_table->rates.entries[rate_idx], + .lane_count = lane_count, + }; + + if (!max_limits_valid(expected_table, &config)) { + /* Verify that invalid max limits are rejected. */ + KUNIT_EXPECT_FALSE(test, ops->set_max_limits(link_caps, &config)); + + continue; + } + + verify_link_caps_for_order(expected_table, link_caps, config_orders[i], + &config); + } + } + } +} + +static void update_link_caps_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps, + bool reset) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + bool link_params_changed; + + link_params_changed = ops->update(link_caps, + expected_table->rates.entries, + expected_table->rates.size, + expected_table->max_lane_count, + reset); + KUNIT_EXPECT_TRUE(test, !reset || link_params_changed); + + /* + * ops->update() re-enables all configurations when called with + * reset=true, or changed link parameters. + */ + if (link_params_changed) + expected_table->disabled_configs.size = 0; + + verify_link_caps(expected_table, link_caps); +} + +static void intel_dp_link_caps_test_update_reset(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + int max_lane_count; + int num_rates; + + for (max_lane_count = 1; + max_lane_count <= LINK_TEST_MAX_LANE_COUNT; + max_lane_count <<= 1) { + for (num_rates = 1; + num_rates <= LINK_TEST_NUM_STANDARD_RATES; + num_rates++) { + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, num_rates, + max_lane_count); + + update_link_caps_and_verify(&expected_table, link_caps, true); + } + } +} + +/* + * TEST: Update shrink and expand + * ------------------------------ + * Verify that removing or adding supported rates/lane counts updates + * the configuration table accordingly. + */ +static void disable_configs_and_verify(struct kunit *test, + struct intel_dp_link_caps *link_caps, + struct test_config_table *expected_table, + const struct link_config_set *config_set) +{ + struct test_ctx *ctx = test->priv; + const struct intel_dp_link_caps_test_ops *ops = + ctx->link_caps_ops; + int i; + + for (i = 0; i < config_set->size; i++) { + KUNIT_ASSERT_FALSE(test, has_config(&expected_table->disabled_configs, + &config_set->entries[i])); + add_config(test, &expected_table->disabled_configs, &config_set->entries[i]); + + KUNIT_ASSERT_TRUE(test, ops->disable_config(link_caps, &config_set->entries[i])); + + verify_link_caps(expected_table, link_caps); + } +} + +static void disable_configs_for_shrink_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct link_config_set config_set = {}; + struct intel_dp_link_config max_config; + + /* + * When configs shrink disable the config with the + * second-highest rate, lane params, so the disabled config + * stays around after the configs got shrunk. + */ + KUNIT_ASSERT_GE(test, expected_table->rates.size, 2); + KUNIT_ASSERT_GE(test, expected_table->max_lane_count, 2); + + max_config.rate = expected_table->rates.entries[expected_table->rates.size - 2]; + max_config.lane_count = expected_table->max_lane_count >> 1; + + add_config(test, &config_set, &max_config); + disable_configs_and_verify(test, link_caps, expected_table, + &config_set); +} + +static void disable_configs_for_expand_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct link_config_set config_set = {}; + struct intel_dp_link_config max_config; + + KUNIT_ASSERT_GE(test, expected_table->rates.size, 1); + + max_config.rate = expected_table->rates.entries[expected_table->rates.size - 1]; + max_config.lane_count = expected_table->max_lane_count; + + add_config(test, &config_set, &max_config); + disable_configs_and_verify(test, link_caps, expected_table, + &config_set); +} + +static void get_nth_rate_lane_config(const struct test_config_table *expected_table, int n, + struct intel_dp_link_config *config) +{ + int num_lane_configs = LINK_TEST_NUM_LANE_CONFIGS(expected_table->max_lane_count); + int rate_idx = n / num_lane_configs; + int lane_count_exp = n % num_lane_configs; + + config->rate = expected_table->rates.entries[rate_idx]; + config->lane_count = 1 << lane_count_exp; +} + +static void test_update_rates_shrink(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size > 1) { + if (disable_configs) + disable_configs_for_shrink_and_verify(&expected_table, link_caps); + + expected_table.rates.size--; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_rates_shrink(struct kunit *test) +{ + test_update_rates_shrink(test, false); +} + +static void intel_dp_link_caps_test_update_rates_shrink_disable(struct kunit *test) +{ + test_update_rates_shrink(test, true); +} + +static void test_update_rates_expand(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, 1, LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size < LINK_TEST_NUM_STANDARD_RATES) { + if (disable_configs) + disable_configs_for_expand_and_verify(&expected_table, link_caps); + + expected_table.rates.size++; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_rates_expand(struct kunit *test) +{ + test_update_rates_expand(test, false); +} + +static void intel_dp_link_caps_test_update_rates_expand_disable(struct kunit *test) +{ + test_update_rates_expand(test, true); +} + +static void test_update_lanes_shrink(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.max_lane_count > 1) { + if (disable_configs) + disable_configs_for_shrink_and_verify(&expected_table, link_caps); + + expected_table.max_lane_count >>= 1; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_lanes_shrink(struct kunit *test) +{ + test_update_lanes_shrink(test, false); +} + +static void intel_dp_link_caps_test_update_lanes_shrink_disable(struct kunit *test) +{ + test_update_lanes_shrink(test, true); +} + +static void test_update_lanes_expand(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, 1); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.max_lane_count < LINK_TEST_MAX_LANE_COUNT) { + if (disable_configs) + disable_configs_for_expand_and_verify(&expected_table, link_caps); + + expected_table.max_lane_count <<= 1; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_lanes_expand(struct kunit *test) +{ + test_update_lanes_expand(test, false); +} + +static void intel_dp_link_caps_test_update_lanes_expand_disable(struct kunit *test) +{ + test_update_lanes_expand(test, true); +} + +static void disable_random_configs_and_verify(struct test_config_table *expected_table, + struct intel_dp_link_caps *link_caps) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + struct link_config_set config_set = {}; + u32 disabled_config_mask; + int num_configs; + int i; + + num_configs = get_num_configs(expected_table->rates.size, + expected_table->max_lane_count); + disabled_config_mask = prandom_u32_state(&ctx->rnd) & + GENMASK_U32(num_configs - 1, 0); + + for (i = 0; i < num_configs; i++) { + struct intel_dp_link_config config; + + /* At least one config must remain enabled. */ + if (expected_table->disabled_configs.size + + config_set.size + 1 >= num_configs) + break; + + if (!(BIT(i) & disabled_config_mask)) + continue; + + get_nth_rate_lane_config(expected_table, i, &config); + /* Don't disable a config twice. */ + if (has_config(&expected_table->disabled_configs, &config)) + continue; + + add_config(test, &config_set, &config); + } + + disable_configs_and_verify(test, link_caps, expected_table, + &config_set); +} + +static void get_params_shrink_step(struct test_ctx *ctx, + int num_rates, int max_lane_count, + int *rates_step, int *lanes_step) +{ + int shrink_mask; + + *rates_step = 0; + *lanes_step = 0; + + if (num_rates == 1) + shrink_mask = BIT(0); /* shrink only lanes */ + else if (max_lane_count == 1) + shrink_mask = BIT(1); /* shrink only rates */ + else + shrink_mask = rand_in_range(ctx, + BIT(0), + BIT(0) | BIT(1)); /* shrink one or both params */ + + if (shrink_mask & BIT(1)) + *rates_step = rand_in_range(ctx, 1, num_rates - 1); + + if (shrink_mask & BIT(0)) + *lanes_step = rand_in_range(ctx, 1, ilog2(max_lane_count)); +} + +static void get_params_expand_step(struct test_ctx *ctx, + int max_num_rates, int num_rates, + int max_supported_lane_count, int max_lane_count, + int *rates_step, int *lanes_step) +{ + int expand_mask; + + *rates_step = 0; + *lanes_step = 0; + + if (num_rates == max_num_rates) + expand_mask = BIT(0); /* expand only lanes */ + else if (max_lane_count == max_supported_lane_count) + expand_mask = BIT(1); /* expand only rates */ + else + expand_mask = rand_in_range(ctx, + BIT(0), + BIT(0) | BIT(1)); /* expand one or both params */ + + if (expand_mask & BIT(1)) + *rates_step = rand_in_range(ctx, 1, max_num_rates - num_rates); + + if (expand_mask & BIT(0)) + *lanes_step = rand_in_range(ctx, 1, ilog2(max_supported_lane_count / + max_lane_count)); +} + +static void test_update_params_shrink_random(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size > 1 || expected_table.max_lane_count > 1) { + int rates_step; + int lanes_step; + + if (disable_configs) + disable_random_configs_and_verify(&expected_table, link_caps); + + get_params_shrink_step(ctx, + expected_table.rates.size, + expected_table.max_lane_count, + &rates_step, &lanes_step); + + expected_table.rates.size -= rates_step; + expected_table.max_lane_count >>= lanes_step; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_params_shrink_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_shrink_random(test, false); +} + +static void intel_dp_link_caps_test_update_params_shrink_disable_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_shrink_random(test, true); +} + +static void test_update_params_expand_random(struct kunit *test, bool disable_configs) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, 1, 1); + + update_link_caps_and_verify(&expected_table, link_caps, true); + + while (expected_table.rates.size < LINK_TEST_NUM_STANDARD_RATES || + expected_table.max_lane_count < LINK_TEST_MAX_LANE_COUNT) { + int rates_step; + int lanes_step; + + if (disable_configs) + disable_random_configs_and_verify(&expected_table, link_caps); + + get_params_expand_step(ctx, + LINK_TEST_NUM_STANDARD_RATES, + expected_table.rates.size, + LINK_TEST_MAX_LANE_COUNT, + expected_table.max_lane_count, + &rates_step, &lanes_step); + + expected_table.rates.size += rates_step; + expected_table.max_lane_count <<= lanes_step; + + update_link_caps_and_verify(&expected_table, link_caps, false); + } +} + +static void intel_dp_link_caps_test_update_params_expand_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_expand_random(test, false); +} + +static void intel_dp_link_caps_test_update_params_expand_disable_random(struct kunit *test) +{ + int i; + + for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++) + test_update_params_expand_random(test, true); +} + +/* + * TEST: Fallback sequence + * ----------------------- + * Verify the eDP fallback logic to set the maximum supported configuration + * as a preference. + * + * For DP SST and MST verify fallback selection from the connector's + * maximum configuration and iteration of the resulting allowed + * configurations. + */ +static void intel_dp_link_test_fallback_for_edp(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + struct intel_digital_port *dig_port = &ctx->dev.dig_port; + const struct intel_dp_link_training_test_ops *lt_ops = + ctx->link_training_ops; + const struct intel_dp_link_caps_test_ops *lc_ops = + ctx->link_caps_ops; + struct intel_dp_link_config min_config = { + .rate = expected_table.rates.entries[0], + .lane_count = 1, + }; + struct intel_dp_link_config max_config = { + .rate = expected_table.rates.entries[expected_table.rates.size - 1], + .lane_count = LINK_TEST_MAX_LANE_COUNT, + }; + struct intel_dp_link_caps_order order; + struct intel_dp_link_config iter_config; + struct intel_dp_link_caps_iter iter; + int fallback_err; + + dig_port->base.type = INTEL_OUTPUT_EDP; + ctx->dev.dig_port.dp.use_max_params = false; + + update_link_caps_and_verify(&expected_table, link_caps, true); + + order = lc_ops->connector_compute_order(&ctx->dev.connector); + + lc_ops->iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) + break; + lc_ops->iter_end(&iter); + + KUNIT_EXPECT_FALSE(test, ctx->dev.dig_port.dp.use_max_params); + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, &min_config)); + + ctx->dev.crtc_state.output_types = BIT(dig_port->base.type); + ctx->dev.crtc_state.port_clock = min_config.rate; + ctx->dev.crtc_state.lane_count = min_config.lane_count; + + fallback_err = lt_ops->get_fallback_values(&ctx->dev.dig_port.dp, &ctx->dev.crtc_state); + KUNIT_EXPECT_EQ(test, fallback_err, 0); + + /* The fallback should've changed the order. */ + order = lc_ops->connector_compute_order(&ctx->dev.connector); + + lc_ops->iter_start(&iter, link_caps, order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) + break; + lc_ops->iter_end(&iter); + + KUNIT_EXPECT_TRUE(test, ctx->dev.dig_port.dp.use_max_params); + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, &max_config)); +} + +static bool test_fallback_from_target(struct test_config_table *expected_table, + enum intel_output_type output_type, + const struct intel_dp_link_config *expected_target_config, + const struct intel_dp_link_config *expected_fallback_config) +{ + struct kunit *test = expected_table->test; + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct intel_dp_link_config iter_config; + const struct intel_dp_link_training_test_ops *lt_ops = + ctx->link_training_ops; + const struct intel_dp_link_caps_test_ops *lc_ops = + ctx->link_caps_ops; + /* Modify default order direction for max config lookup. */ + struct intel_dp_link_caps_order fallback_order = + lc_ops->connector_fallback_order(ctx->dev.connector.mst.dp); + struct intel_dp_link_caps_iter iter; + int expected_fallback_err = 0; + int fallback_err; + + /* Get the max connector config, optionally filtered to the max_rate limit. */ + lc_ops->iter_start(&iter, link_caps, fallback_order, INTEL_DP_LINK_CAPS_FILTER_ALL); + for_each_dp_link_config(&iter, &iter_config) + break; + lc_ops->iter_end(&iter); + + KUNIT_EXPECT_TRUE(test, link_configs_match(&iter_config, + expected_target_config)); + KUNIT_EXPECT_FALSE(test, link_configs_match(&iter_config, + &INTEL_DP_LINK_CONFIG_NULL)); + + ctx->dev.crtc_state.output_types = BIT(output_type); + ctx->dev.crtc_state.port_clock = expected_target_config->rate; + ctx->dev.crtc_state.lane_count = expected_target_config->lane_count; + + if (link_configs_match(expected_fallback_config, &INTEL_DP_LINK_CONFIG_NULL)) + expected_fallback_err = -1; + + fallback_err = lt_ops->get_fallback_values(&ctx->dev.dig_port.dp, &ctx->dev.crtc_state); + KUNIT_EXPECT_EQ(test, fallback_err, expected_fallback_err); + + if (!fallback_err) { + /* + * NOTE: This test does not verify any implied fallback + * target selection. + * + * The current driver behavior may still select a fallback + * configuration indirectly via max_limits, but that is an + * implementation artifact rather than part of the intended + * fallback API behavior, and is therefore not verified here. + * + * Instead, the effect of the fallback logic is verified by + * checking that the failed target configuration is disabled. + * Selecting the next target configuration from the remaining + * allowed configurations belongs to the modeset link target + * selection logic. + */ + add_config(test, &expected_table->disabled_configs, + expected_target_config); + } + + verify_link_caps(expected_table, link_caps); + + return !fallback_err; +} + +static const struct link_config_set * +get_target_configs_for_output_type(struct kunit *test, + enum intel_output_type output_type) +{ + switch (output_type) { + case INTEL_OUTPUT_DDI: + case INTEL_OUTPUT_DP: + case INTEL_OUTPUT_EDP: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE]; + case INTEL_OUTPUT_DP_MST: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_BW]; + default: + KUNIT_FAIL_AND_ABORT(test, "Missing output type: %d", output_type); + } +} + +static const struct link_config_set * +get_fallback_configs_for_output_type(struct kunit *test, + enum intel_output_type output_type) +{ + switch (output_type) { + case INTEL_OUTPUT_DDI: + case INTEL_OUTPUT_DP: + case INTEL_OUTPUT_EDP: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE]; + case INTEL_OUTPUT_DP_MST: + return &standard_dp_link_configs[INTEL_DP_LINK_CAPS_ORDER_KEY_BW]; + default: + KUNIT_FAIL_AND_ABORT(test, "Missing output type: %d", output_type); + } +} + +static void assert_config_is_supported(const struct test_config_table *expected_table, + const struct intel_dp_link_config *config) +{ + struct kunit *test = expected_table->test; + + KUNIT_ASSERT_TRUE(test, has_rate(&expected_table->rates, config->rate)); + KUNIT_ASSERT_LE(test, config->lane_count, expected_table->max_lane_count); +} + +static bool get_fallback_config(const struct test_config_table *expected_table, + enum intel_output_type output_type, + const struct intel_dp_link_config *target_config, + struct intel_dp_link_config *fallback_config) +{ + struct kunit *test = expected_table->test; + const struct link_config_set *config_set = + get_fallback_configs_for_output_type(test, output_type); + int i; + + i = lookup_config(config_set, target_config); + KUNIT_ASSERT_GE(test, i, 0); + + for (i--; i >= 0; i--) { + const struct intel_dp_link_config *config = + &config_set->entries[i]; + + assert_config_is_supported(expected_table, config); + *fallback_config = *config; + + return true; + } + + return false; +} + +static bool get_target_config(const struct test_config_table *expected_table, + enum intel_output_type output_type, + struct intel_dp_link_config *target) +{ + struct kunit *test = expected_table->test; + const struct link_config_set *config_set = + get_target_configs_for_output_type(test, output_type); + int i; + + for (i = config_set->size - 1; i >= 0; i--) { + const struct intel_dp_link_config *config = + &config_set->entries[i]; + + assert_config_is_supported(expected_table, config); + *target = *config; + + return true; + } + + return false; +} + +static void test_fallback_seq(struct kunit *test, + enum intel_output_type output_type) +{ + struct test_ctx *ctx = test->priv; + struct intel_dp_link_caps *link_caps = ctx->dev.dig_port.dp.link.caps; + struct test_config_table expected_table = + INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, + LINK_TEST_MAX_LANE_COUNT); + struct intel_digital_port *dig_port = &ctx->dev.dig_port; + struct intel_dp_link_config fallback_config = {}; + struct intel_dp_link_config target_config; + int fallback_count = 0; + bool target_found; + + dig_port->base.type = output_type; + ctx->dev.dig_port.dp.use_max_params = false; + + update_link_caps_and_verify(&expected_table, link_caps, true); + + /* Get the initial target config. */ + target_found = get_target_config(&expected_table, output_type, + &target_config); + KUNIT_ASSERT_TRUE(test, target_found); + + for (;;) { + /* Also test the case where no fallback is available. */ + if (!get_fallback_config(&expected_table, output_type, + &target_config, &fallback_config)) + fallback_config = INTEL_DP_LINK_CONFIG_NULL; + + if (!test_fallback_from_target(&expected_table, output_type, + &target_config, &fallback_config)) + break; + + /* Simply select the fallback config as the next target. */ + target_config = fallback_config; + + fallback_count++; + KUNIT_ASSERT_LT(test, fallback_count, LINK_TEST_MAX_CONFIGS); + } +} + +static void intel_dp_link_test_fallback_for_sst(struct kunit *test) +{ + test_fallback_seq(test, INTEL_OUTPUT_DP); +} + +static void intel_dp_link_test_fallback_for_mst(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + + ctx->dev.connector.mst.dp = &ctx->dev.dig_port.dp; + + test_fallback_seq(test, INTEL_OUTPUT_DP_MST); +} + +static struct kunit_case intel_dp_link_test_cases[] = { + KUNIT_CASE(intel_dp_link_caps_test_baseline), + + KUNIT_CASE(intel_dp_link_caps_test_update_reset), + + KUNIT_CASE(intel_dp_link_caps_test_update_rates_shrink), + KUNIT_CASE(intel_dp_link_caps_test_update_rates_shrink_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_rates_expand), + KUNIT_CASE(intel_dp_link_caps_test_update_rates_expand_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_shrink), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_shrink_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_expand), + KUNIT_CASE(intel_dp_link_caps_test_update_lanes_expand_disable), + KUNIT_CASE(intel_dp_link_caps_test_update_params_shrink_random), + KUNIT_CASE(intel_dp_link_caps_test_update_params_shrink_disable_random), + KUNIT_CASE(intel_dp_link_caps_test_update_params_expand_random), + KUNIT_CASE(intel_dp_link_caps_test_update_params_expand_disable_random), + + KUNIT_CASE(intel_dp_link_test_fallback_for_edp), + KUNIT_CASE(intel_dp_link_test_fallback_for_sst), + KUNIT_CASE(intel_dp_link_test_fallback_for_mst), + + {} +}; + +static struct test_ctx test_ctx; + +static int intel_dp_link_test_init(struct kunit *test) +{ + struct intel_digital_port *dig_port; + struct intel_encoder *encoder; + struct intel_dp *intel_dp; + + /* Reset the dev state for each test. */ + memset(&test_ctx.dev, 0, sizeof(test_ctx.dev)); + + test_ctx.dev.generic_device.drm.dev = &test_ctx.dev.device; + + test_ctx.dev.display.drm = &test_ctx.dev.generic_device.drm; + test_ctx.dev.generic_device.display = &test_ctx.dev.display; + + encoder = &test_ctx.dev.dig_port.base; + encoder->base.dev = &test_ctx.dev.generic_device.drm; + + dig_port = &test_ctx.dev.dig_port; + dig_port->base.type = INTEL_OUTPUT_DP; + + test_ctx.dev.connector.encoder = encoder; + + intel_dp = &dig_port->dp; + intel_dp->attached_connector = &test_ctx.dev.connector; + + intel_dp->link.caps = test_ctx.link_caps_ops->init(intel_dp); + + test->priv = &test_ctx; + + return 0; +} + +static void intel_dp_link_test_exit(struct kunit *test) +{ + struct test_ctx *ctx = test->priv; + + ctx->link_caps_ops->cleanup(ctx->dev.dig_port.dp.link.caps); +} + +static int intel_dp_link_test_suite_init(struct kunit_suite *test_suite) +{ +#ifdef I915 + test_ctx.link_caps_ops = &i915_display_dp_link_caps_test_ops; + test_ctx.link_training_ops = &i915_display_dp_link_training_test_ops; +#else + test_ctx.link_caps_ops = &intel_display_dp_link_caps_test_ops; + test_ctx.link_training_ops = &intel_display_dp_link_training_test_ops; +#endif + prandom_seed_state(&test_ctx.rnd, 0); + + return 0; +} + +static struct kunit_suite intel_dp_link_test_suite = { + .name = "intel_dp_link", + .suite_init = intel_dp_link_test_suite_init, + .init = intel_dp_link_test_init, + .exit = intel_dp_link_test_exit, + .test_cases = intel_dp_link_test_cases, +}; + +kunit_test_suites(&intel_dp_link_test_suite); + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Intel DP link KUnit tests"); diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 45f2dcc5130e..ce6d20958320 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -638,7 +638,7 @@ static int i915_driver_register(struct drm_i915_private *dev_priv) intel_display_driver_register(display); - intel_display_power_enable(display); + intel_display_driver_runtime_pm_enable(display); intel_runtime_pm_enable(&dev_priv->runtime_pm); if (i915_switcheroo_register(dev_priv)) @@ -660,7 +660,7 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv) i915_switcheroo_unregister(dev_priv); intel_runtime_pm_disable(&dev_priv->runtime_pm); - intel_display_power_disable(display); + intel_display_driver_runtime_pm_disable(display); intel_display_driver_unregister(display); @@ -1511,24 +1511,32 @@ static int i915_pm_runtime_suspend(struct device *kdev) for_each_gt(gt, dev_priv, i) intel_gt_runtime_suspend(gt); + intel_display_driver_pm_runtime_suspend(display); + intel_irq_suspend(dev_priv); for_each_gt(gt, dev_priv, i) intel_uncore_suspend(gt->uncore); - intel_display_power_runtime_suspend(display); + intel_display_driver_pm_runtime_suspend_late(display); ret = vlv_suspend_complete(dev_priv); if (ret) { drm_err(&dev_priv->drm, "Runtime suspend failed, disabling it (%d)\n", ret); - intel_uncore_runtime_resume(&dev_priv->uncore); + + intel_display_driver_pm_runtime_resume_early(display); + + for_each_gt(gt, dev_priv, i) + intel_uncore_runtime_resume(gt->uncore); intel_irq_resume(dev_priv); for_each_gt(gt, dev_priv, i) intel_gt_runtime_resume(gt); + intel_display_driver_pm_runtime_resume(display); + enable_rpm_wakeref_asserts(rpm); return ret; @@ -1550,34 +1558,8 @@ static int i915_pm_runtime_suspend(struct device *kdev) if (root_pdev) pci_d3cold_disable(root_pdev); - /* - * FIXME: We really should find a document that references the arguments - * used below! - */ - if (IS_BROADWELL(dev_priv)) { - /* - * On Broadwell, if we use PCI_D1 the PCH DDI ports will stop - * being detected, and the call we do at i915_pm_runtime_resume() - * won't be able to restore them. Since PCI_D3hot matches the - * actual specification and appears to be working, use it. - */ - intel_opregion_notify_adapter(display, PCI_D3hot); - } else { - /* - * current versions of firmware which depend on this opregion - * notification have repurposed the D1 definition to mean - * "runtime suspended" vs. what you would normally expect (D3) - * to distinguish it from notifications that might be sent via - * the suspend path. - */ - intel_opregion_notify_adapter(display, PCI_D1); - } - assert_forcewakes_inactive(&dev_priv->uncore); - if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) - intel_hpd_poll_enable(display); - drm_dbg(&dev_priv->drm, "Device suspended\n"); return 0; } @@ -1600,8 +1582,6 @@ static int i915_pm_runtime_resume(struct device *kdev) drm_WARN_ON_ONCE(&dev_priv->drm, atomic_read(&rpm->wakeref_count)); disable_rpm_wakeref_asserts(rpm); - intel_opregion_notify_adapter(display, PCI_D0); - root_pdev = pcie_find_root_port(pdev); if (root_pdev) pci_d3cold_enable(root_pdev); @@ -1610,7 +1590,7 @@ static int i915_pm_runtime_resume(struct device *kdev) drm_dbg(&dev_priv->drm, "Unclaimed access during suspend, bios?\n"); - intel_display_power_runtime_resume(display); + intel_display_driver_pm_runtime_resume_early(display); ret = vlv_resume_prepare(dev_priv, true); @@ -1628,17 +1608,7 @@ static int i915_pm_runtime_resume(struct device *kdev) intel_pxp_runtime_resume(dev_priv->pxp); - /* - * On VLV/CHV display interrupts are part of the display - * power well, so hpd is reinitialized from there. For - * everyone else do it here. - */ - if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) { - intel_hpd_init(display); - intel_hpd_poll_disable(display); - } - - skl_watermark_ipc_update(display); + intel_display_driver_pm_runtime_resume(display); enable_rpm_wakeref_asserts(rpm); diff --git a/drivers/gpu/drm/i915/i915_initial_plane.c b/drivers/gpu/drm/i915/i915_initial_plane.c index 4902c0748664..98858a78e46a 100644 --- a/drivers/gpu/drm/i915/i915_initial_plane.c +++ b/drivers/gpu/drm/i915/i915_initial_plane.c @@ -58,23 +58,23 @@ initial_plane_phys(struct drm_i915_private *i915, if (intel_memory_type_is_local(mem->type) != is_local) { drm_err(&i915->drm, "Initial plane FB PTE unsuitable for %s\n", - mem->region.name); + mem->name); return false; } if (dma_addr < mem->region.start || dma_addr > mem->region.end) { drm_err(&i915->drm, "Initial plane programming using invalid range, dma_addr=%pa (%s [%pa-%pa])\n", - &dma_addr, mem->region.name, &mem->region.start, &mem->region.end); + &dma_addr, mem->name, &mem->region.start, &mem->region.end); return false; } - drm_dbg(&i915->drm, "Using dma_addr=%pa, based on initial plane programming\n", - &dma_addr); - *out_phys_base = dma_addr - mem->region.start; *out_mem = mem; + drm_dbg_kms(&i915->drm, "Initial plane dma_addr=%pa phys_base=%pa mem=%s\n", + &dma_addr, out_phys_base, mem->name); + return true; } @@ -120,7 +120,7 @@ initial_plane_vma(struct drm_i915_private *i915, I915_BO_PREALLOC); if (IS_ERR(obj)) { drm_dbg_kms(&i915->drm, "Failed to preallocate initial FB in %s\n", - mem->region.name); + mem->name); return NULL; } diff --git a/drivers/gpu/drm/xe/.gitignore b/drivers/gpu/drm/xe/.gitignore index 8778bf132674..6dad8a5a2135 100644 --- a/drivers/gpu/drm/xe/.gitignore +++ b/drivers/gpu/drm/xe/.gitignore @@ -2,3 +2,4 @@ *.hdrtest /generated /xe_gen_wa_oob +!.kunitconfig-display diff --git a/drivers/gpu/drm/xe/.kunitconfig-display b/drivers/gpu/drm/xe/.kunitconfig-display new file mode 100644 index 000000000000..17020aa4ded3 --- /dev/null +++ b/drivers/gpu/drm/xe/.kunitconfig-display @@ -0,0 +1,11 @@ +CONFIG_EXPERT=y +CONFIG_MODULES=y +CONFIG_KUNIT=y +CONFIG_PCI=y +CONFIG_DEBUG_FS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DRM=m +CONFIG_DRM_XE=m +CONFIG_DRM_XE_DISPLAY=y +CONFIG_DRM_XE_KUNIT_TEST=m diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 6d728f8c4c39..67ada1d6c2fb 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -374,6 +374,9 @@ xe-$(CONFIG_DRM_XE_DP_TUNNEL) += \ obj-$(CONFIG_DRM_XE) += xe.o obj-$(CONFIG_DRM_XE_KUNIT_TEST) += tests/ +ifeq ($(CONFIG_DRM_XE_DISPLAY),y) + obj-$(CONFIG_DRM_XE_KUNIT_TEST) += display/tests/ +endif # header test hdrtest_find_args := -not -path xe_rtp_helpers.h diff --git a/drivers/gpu/drm/xe/display/tests/Makefile b/drivers/gpu/drm/xe/display/tests/Makefile new file mode 100644 index 000000000000..c71686458072 --- /dev/null +++ b/drivers/gpu/drm/xe/display/tests/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +subdir-ccflags-$(CONFIG_DRM_XE_DISPLAY) += \ + -I$(srctree)/drivers/gpu/drm/i915/display/ + +# Rule to build display code shared with i915 +$(obj)/i915-display/tests/%.o: $(srctree)/drivers/gpu/drm/i915/display/tests/%.c FORCE + $(call cmd,force_checksrc) + $(call if_changed_rule,cc_o_c) + +obj-$(CONFIG_DRM_XE_KUNIT_TEST) += xe_display_test.o +xe_display_test-y = \ + i915-display/tests/intel_dp_link_test.o diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 92a4573db28a..8da4c457a4a0 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -158,7 +158,7 @@ void xe_display_register(struct xe_device *xe) return; intel_display_driver_register(display); - intel_display_power_enable(display); + intel_display_driver_runtime_pm_enable(display); } void xe_display_unregister(struct xe_device *xe) @@ -168,7 +168,7 @@ void xe_display_unregister(struct xe_device *xe) if (!xe->info.probe_display) return; - intel_display_power_disable(display); + intel_display_driver_runtime_pm_disable(display); intel_display_driver_unregister(display); } @@ -313,7 +313,7 @@ static void xe_display_enable_d3cold(struct xe_device *xe) * We do a lot of poking in a lot of registers, make sure they work * properly. */ - intel_display_power_disable(display); + intel_display_driver_runtime_pm_disable(display); intel_display_flush_cleanup_work(display); @@ -346,9 +346,10 @@ static void xe_display_disable_d3cold(struct xe_device *xe) intel_opregion_resume(display); - intel_display_power_enable(display); + intel_display_driver_runtime_pm_enable(display); } +/* before irq suspend */ void xe_display_pm_runtime_suspend(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -361,9 +362,10 @@ void xe_display_pm_runtime_suspend(struct xe_device *xe) return; } - intel_hpd_poll_enable(display); + intel_display_driver_pm_runtime_suspend(display); } +/* after irq suspend */ void xe_display_pm_runtime_suspend_late(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -371,17 +373,31 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe) if (!xe->info.probe_display) return; - if (xe->d3cold.allowed) + if (xe->d3cold.allowed) { xe_display_pm_suspend_late(xe); + /* Ensure the wakelock release work gets flushed */ + intel_dmc_wl_flush_release_work(display); + return; + } - /* - * If xe_display_pm_suspend_late() is not called, it is likely - * that we will be on dynamic DC states with DMC wakelock enabled. We - * need to flush the release work in that case. - */ - intel_dmc_wl_flush_release_work(display); + intel_display_driver_pm_runtime_suspend_late(display); +} + +/* before irq resume */ +void xe_display_pm_runtime_resume_early(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + if (xe->d3cold.allowed) + return; + + intel_display_driver_pm_runtime_resume_early(display); } +/* after irq resume */ void xe_display_pm_runtime_resume(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -394,9 +410,7 @@ void xe_display_pm_runtime_resume(struct xe_device *xe) return; } - intel_hpd_init(display); - intel_hpd_poll_disable(display); - skl_watermark_ipc_update(display); + intel_display_driver_pm_runtime_resume(display); } diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h index e5f9aed93206..0babb50bfc77 100644 --- a/drivers/gpu/drm/xe/display/xe_display.h +++ b/drivers/gpu/drm/xe/display/xe_display.h @@ -43,6 +43,7 @@ void xe_display_pm_resume_early(struct xe_device *xe); void xe_display_pm_resume(struct xe_device *xe); void xe_display_pm_runtime_suspend(struct xe_device *xe); void xe_display_pm_runtime_suspend_late(struct xe_device *xe); +void xe_display_pm_runtime_resume_early(struct xe_device *xe); void xe_display_pm_runtime_resume(struct xe_device *xe); #define XE_DISPLAY_DRIVER_FEATURES (DRIVER_MODESET | DRIVER_ATOMIC) @@ -80,6 +81,7 @@ static inline void xe_display_pm_resume_early(struct xe_device *xe) {} static inline void xe_display_pm_resume(struct xe_device *xe) {} static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {} static inline void xe_display_pm_runtime_suspend_late(struct xe_device *xe) {} +static inline void xe_display_pm_runtime_resume_early(struct xe_device *xe) {} static inline void xe_display_pm_runtime_resume(struct xe_device *xe) {} #endif /* CONFIG_DRM_XE_DISPLAY */ diff --git a/drivers/gpu/drm/xe/display/xe_display_bo.c b/drivers/gpu/drm/xe/display/xe_display_bo.c index 8953da0136dc..f5102cd0e95d 100644 --- a/drivers/gpu/drm/xe/display/xe_display_bo.c +++ b/drivers/gpu/drm/xe/display/xe_display_bo.c @@ -147,33 +147,12 @@ static struct drm_gem_object *xe_display_bo_fbdev_create(struct drm_device *drm, struct xe_device *xe = to_xe_device(drm); struct xe_bo *obj; - obj = ERR_PTR(-ENODEV); - - if (xe_display_bo_fbdev_prefer_stolen(xe, size)) { - obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), - size, - ttm_bo_type_kernel, - XE_BO_FLAG_FORCE_WC | - XE_BO_FLAG_STOLEN | - XE_BO_FLAG_GGTT, - false); - if (!IS_ERR(obj)) - drm_info(&xe->drm, "Allocated fbdev into stolen\n"); - else - drm_info(&xe->drm, "Allocated fbdev into stolen failed: %li\n", PTR_ERR(obj)); - } else { - drm_info(&xe->drm, "Allocating fbdev: Stolen memory not preferred.\n"); - } - - if (IS_ERR(obj)) { - obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), size, - ttm_bo_type_kernel, - XE_BO_FLAG_FORCE_WC | - XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) | - XE_BO_FLAG_GGTT, - false); - } - + obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), size, + ttm_bo_type_kernel, + XE_BO_FLAG_FORCE_WC | + XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) | + XE_BO_FLAG_GGTT, + false); if (IS_ERR(obj)) { drm_err(&xe->drm, "failed to allocate framebuffer (%pe)\n", obj); return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index 5f4a0cd8deca..73469ea5f333 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -164,31 +164,14 @@ static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj, dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8, XE_PAGE_SIZE); - if (IS_DGFX(xe)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_VRAM0 | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - else - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_STOLEN | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - if (IS_ERR(dpt)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_SYSTEM | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE | - XE_BO_FLAG_FORCE_WC, - pin_params->alignment, false); + dpt = xe_bo_create_pin_map_at_novm(xe, tile0, + dpt_size, ~0ull, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile0) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_PAGETABLE | + XE_BO_FLAG_FORCE_WC, + pin_params->alignment, false); if (IS_ERR(dpt)) return PTR_ERR(dpt); diff --git a/drivers/gpu/drm/xe/display/xe_initial_plane.c b/drivers/gpu/drm/xe/display/xe_initial_plane.c index 02b46cc3b6df..0f86b73036d0 100644 --- a/drivers/gpu/drm/xe/display/xe_initial_plane.c +++ b/drivers/gpu/drm/xe/display/xe_initial_plane.c @@ -19,8 +19,24 @@ #include "xe_fb_pin.h" #include "xe_ggtt.h" #include "xe_mmio.h" +#include "xe_ttm_stolen_mgr.h" #include "xe_vram_types.h" +static bool is_pte_local(u64 pte) +{ + return pte & XE_GGTT_PTE_DM; +} + +static bool has_lmembar(struct xe_device *xe) +{ + return GRAPHICS_VERx100(xe) >= 1270; +} + +static bool need_pte_local(struct xe_device *xe) +{ + return IS_DGFX(xe) || has_lmembar(xe); +} + static struct xe_bo * initial_plane_bo(struct xe_device *xe, struct intel_initial_plane_config *plane_config) @@ -37,16 +53,20 @@ initial_plane_bo(struct xe_device *xe, flags = XE_BO_FLAG_FORCE_WC | XE_BO_FLAG_GGTT; base = round_down(plane_config->base, page_size); + size = round_up(plane_config->base + plane_config->size, + page_size); + size -= base; + if (IS_DGFX(xe)) { u64 pte = xe_ggtt_read_pte(tile0->mem.ggtt, base); - if (!(pte & XE_GGTT_PTE_DM)) { - drm_err(&xe->drm, - "Initial plane programming missing DM bit\n"); + if (is_pte_local(pte) != need_pte_local(xe)) { + drm_err(&xe->drm, "Initial plane PTE has bad local memory bit\n"); return NULL; } phys_base = pte & ~(page_size - 1); + flags |= XE_BO_FLAG_VRAM0; /* @@ -60,14 +80,26 @@ initial_plane_bo(struct xe_device *xe, return NULL; } - drm_dbg(&xe->drm, - "Using phys_base=%pa, based on initial plane programming\n", - &phys_base); + drm_dbg_kms(&xe->drm, + "Using phys_base=%pa, based on initial plane programming\n", + &phys_base); } else { - struct ttm_resource_manager *stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN); + struct ttm_resource_manager *stolen; + u64 pte; + + stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN); + if (!stolen) { + drm_dbg_kms(&xe->drm, "No stolen for initial FB\n"); + return NULL; + } + + pte = xe_ggtt_read_pte(tile0->mem.ggtt, base); - if (!stolen) + if (is_pte_local(pte) != need_pte_local(xe)) { + drm_err(&xe->drm, "Initial plane PTE has bad local memory bit\n"); return NULL; + } + phys_base = base; flags |= XE_BO_FLAG_STOLEN; @@ -79,16 +111,12 @@ initial_plane_bo(struct xe_device *xe, } } - size = round_up(plane_config->base + plane_config->size, - page_size); - size -= base; - bo = xe_bo_create_pin_map_at_novm(xe, tile0, size, phys_base, ttm_bo_type_kernel, flags, 0, false); if (IS_ERR(bo)) { - drm_dbg(&xe->drm, - "Failed to create bo phys_base=%pa size %u with flags %x: %li\n", - &phys_base, size, flags, PTR_ERR(bo)); + drm_dbg_kms(&xe->drm, + "Failed to create bo phys_base=%pa size %u with flags %x: %li\n", + &phys_base, size, flags, PTR_ERR(bo)); return NULL; } diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c index 99562f691080..a5289a9df8d2 100644 --- a/drivers/gpu/drm/xe/xe_pm.c +++ b/drivers/gpu/drm/xe/xe_pm.c @@ -700,6 +700,8 @@ int xe_pm_runtime_resume(struct xe_device *xe) if (xe->d3cold.allowed) xe_sysctrl_pm_resume(xe); + xe_display_pm_runtime_resume_early(xe); + xe_irq_resume(xe); for_each_gt(gt, xe, id) { diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c index 5e9070739e65..60f39efb02d0 100644 --- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c @@ -61,7 +61,7 @@ static u32 get_wopcm_size(struct xe_device *xe) return wopcm_size; } -static u64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr) +static u64 detect_lmembar_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr) { struct xe_vram_region *tile_vram = xe_device_get_root_tile(xe)->mem.vram; resource_size_t tile_io_start = xe_vram_region_io_start(tile_vram); @@ -102,7 +102,7 @@ static u64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr) return ALIGN_DOWN(stolen_size, SZ_1M); } -static u32 detect_bar2_integrated(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr) +static u32 detect_lmembar_integrated(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr) { struct pci_dev *pdev = to_pci_dev(xe->drm.dev); struct xe_gt *media_gt = xe_device_get_root_tile(xe)->media_gt; @@ -212,9 +212,9 @@ int xe_ttm_stolen_mgr_init(struct xe_device *xe) if (IS_SRIOV_VF(xe)) stolen_size = 0; else if (IS_DGFX(xe)) - stolen_size = detect_bar2_dgfx(xe, mgr); + stolen_size = detect_lmembar_dgfx(xe, mgr); else if (GRAPHICS_VERx100(xe) >= 1270) - stolen_size = detect_bar2_integrated(xe, mgr); + stolen_size = detect_lmembar_integrated(xe, mgr); else stolen_size = detect_stolen(xe, mgr); @@ -262,9 +262,9 @@ u64 xe_ttm_stolen_io_offset(struct xe_bo *bo, u32 offset) return mgr->io_base + (bo->ttm.resource->start << PAGE_SHIFT) + offset; } -static int __xe_ttm_stolen_io_mem_reserve_bar2(struct xe_device *xe, - struct xe_ttm_stolen_mgr *mgr, - struct ttm_resource *mem) +static int __xe_ttm_stolen_io_mem_reserve_lmembar(struct xe_device *xe, + struct xe_ttm_stolen_mgr *mgr, + struct ttm_resource *mem) { if (!mgr->io_base) return -EIO; @@ -321,7 +321,7 @@ int xe_ttm_stolen_io_mem_reserve(struct xe_device *xe, struct ttm_resource *mem) if (xe_ttm_stolen_cpu_access_needs_ggtt(xe)) return __xe_ttm_stolen_io_mem_reserve_stolen(xe, mgr, mem); else - return __xe_ttm_stolen_io_mem_reserve_bar2(xe, mgr, mem); + return __xe_ttm_stolen_io_mem_reserve_lmembar(xe, mgr, mem); } u64 xe_ttm_stolen_gpu_offset(struct xe_device *xe) |
