summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-26 22:25:08 +0100
committerMark Brown <broonie@kernel.org>2026-07-26 22:25:08 +0100
commit943c160ac32335200c8548386d2273f6aac077c4 (patch)
tree79e7078ecfe40693fe2cf4f45b54b5c131da45d5 /drivers
parentefba40297ebc8914028f083cdfb5610dd349bd37 (diff)
parentf0cabcc880aba3f05ca59d735f6d166b7fceca80 (diff)
downloadlinux-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')
-rw-r--r--drivers/gpu/drm/display/drm_dp_tunnel.c116
-rw-r--r--drivers/gpu/drm/i915/.kunitconfig9
-rw-r--r--drivers/gpu/drm/i915/Kconfig.debug12
-rw-r--r--drivers/gpu/drm/i915/Makefile2
-rw-r--r--drivers/gpu/drm/i915/display/hsw_ips.c2
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_plane.c8
-rw-r--r--drivers/gpu/drm/i915/display/intel_audio.c55
-rw-r--r--drivers/gpu/drm/i915/display/intel_bw.c112
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c10
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c76
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_core.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_device.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.c71
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.h8
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_irq.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_regs.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_types.h10
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_wa.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_wa.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc.c10
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c405
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.h15
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_caps.c757
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_caps.h172
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.c222
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.h31
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c79
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_test.c107
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_test.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_tunnel.c53
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_tunnel.h17
-rw-r--r--drivers/gpu/drm/i915/display/intel_dram.c14
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbc.c21
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev.c35
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_initial_plane.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_setup.c30
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.c55
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.h5
-rw-r--r--drivers/gpu/drm/i915/display/intel_pmdemand.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.c16
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite.c22
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.c6
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c22
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.h9
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.c6
-rw-r--r--drivers/gpu/drm/i915/display/tests/Makefile7
-rw-r--r--drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c1408
-rw-r--r--drivers/gpu/drm/i915/i915_driver.c58
-rw-r--r--drivers/gpu/drm/i915/i915_initial_plane.c12
-rw-r--r--drivers/gpu/drm/xe/.gitignore1
-rw-r--r--drivers/gpu/drm/xe/.kunitconfig-display11
-rw-r--r--drivers/gpu/drm/xe/Makefile3
-rw-r--r--drivers/gpu/drm/xe/display/tests/Makefile13
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c44
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.h2
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_bo.c33
-rw-r--r--drivers/gpu/drm/xe/display/xe_fb_pin.c33
-rw-r--r--drivers/gpu/drm/xe/display/xe_initial_plane.c58
-rw-r--r--drivers/gpu/drm/xe/xe_pm.c2
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c16
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, &current_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)