summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-03 15:44:52 +0100
committerMark Brown <broonie@kernel.org>2026-07-03 15:44:52 +0100
commit10182b0e6181d81cbba867e2a098c42088285224 (patch)
tree9c674f76798bbd5484d6d7fcfd239e9bc33a2817
parent4dac529c9a2c0ed1286801ec62d23538035a3a1d (diff)
parent8e27f752037e72ccee9c4a7c4a6202ecf3daf603 (diff)
downloadlinux-next-10182b0e6181d81cbba867e2a098c42088285224.tar.gz
linux-next-10182b0e6181d81cbba867e2a098c42088285224.zip
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/i915/kernel.git
-rw-r--r--Documentation/gpu/intel-display/dp-link-training.rst8
-rw-r--r--Documentation/gpu/intel-display/index.rst1
-rw-r--r--drivers/gpu/drm/drm_modes.c23
-rw-r--r--drivers/gpu/drm/i915/Makefile5
-rw-r--r--drivers/gpu/drm/i915/display/g4x_dp.c15
-rw-r--r--drivers/gpu/drm/i915/display/g4x_hdmi.c4
-rw-r--r--drivers/gpu/drm/i915/display/icl_dsi.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_alpm.c15
-rw-r--r--drivers/gpu/drm/i915/display/intel_bios.c62
-rw-r--r--drivers/gpu/drm/i915/display/intel_bw.c168
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c54
-rw-r--r--drivers/gpu/drm/i915/display/intel_cmtg.c280
-rw-r--r--drivers/gpu/drm/i915/display/intel_cmtg.h17
-rw-r--r--drivers/gpu/drm/i915/display/intel_cmtg_regs.h24
-rw-r--r--drivers/gpu/drm/i915/display/intel_color.c117
-rw-r--r--drivers/gpu/drm/i915/display/intel_crt.c9
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy.c11
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c29
-rw-r--r--drivers/gpu/drm/i915/display/intel_de.c40
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c170
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.h7
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_core.h14
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_device.c14
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_device.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.c131
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.h9
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_irq.c19
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_irq.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_limits.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.c185
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.h40
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_well.c76
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_well.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_regs.h46
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_types.h57
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc.c15
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc_regs.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc_wl.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c496
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.h16
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_caps.c675
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_caps.h47
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.c1051
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.h15
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c8
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_test.c7
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_tunnel.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb.c32
-rw-r--r--drivers/gpu/drm/i915/display/intel_dvo.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_gmbus.c148
-rw-r--r--drivers/gpu/drm/i915/display/intel_gmbus.h22
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.c71
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_initial_plane.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_lpe_audio.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy.c20
-rw-r--r--drivers/gpu/drm/i915/display/intel_lvds.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_panel.c170
-rw-r--r--drivers/gpu/drm/i915/display/intel_panel.h6
-rw-r--r--drivers/gpu/drm/i915/display/intel_parent.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_parent.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.c52
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.h5
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.c267
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr_regs.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_sdvo.c7
-rw-r--r--drivers/gpu/drm/i915/display/intel_tv.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.c29
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.h4
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi.c5
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.h7
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_pages.c128
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_panic.c147
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_panic.h11
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gsc.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/edid.c14
-rw-r--r--drivers/gpu/drm/i915/i915_driver.c123
-rw-r--r--drivers/gpu/drm/i915/i915_panic.c35
-rw-r--r--drivers/gpu/drm/i915/i915_panic.h9
-rw-r--r--drivers/gpu/drm/xe/Makefile1
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c220
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.h10
-rw-r--r--drivers/gpu/drm/xe/display/xe_panic.c26
-rw-r--r--drivers/gpu/drm/xe/xe_device.c4
-rw-r--r--include/drm/drm_modes.h1
-rw-r--r--include/drm/intel/display_parent_interface.h4
-rw-r--r--include/drm/intel/mchbar_regs.h2
91 files changed, 3663 insertions, 1962 deletions
diff --git a/Documentation/gpu/intel-display/dp-link-training.rst b/Documentation/gpu/intel-display/dp-link-training.rst
new file mode 100644
index 000000000000..d0bde965021d
--- /dev/null
+++ b/Documentation/gpu/intel-display/dp-link-training.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: MIT
+.. Copyright © 2026 Intel Corporation
+
+DisplayPort Link Training
+=========================
+
+.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_training.c
+ :doc: DisplayPort link training
diff --git a/Documentation/gpu/intel-display/index.rst b/Documentation/gpu/intel-display/index.rst
index 01c3d1e576b7..6fa929d82c38 100644
--- a/Documentation/gpu/intel-display/index.rst
+++ b/Documentation/gpu/intel-display/index.rst
@@ -38,6 +38,7 @@ driver. The display driver isn't an independent driver in that sense.
fifo-underrun
frontbuffer
hotplug
+ dp-link-training
plane
psr
snps-phy
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 3f8e025fd6d9..e1eed13a8e94 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1469,6 +1469,25 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_mode_duplicate);
+static bool drm_mode_match_timings_vrr(const struct drm_display_mode *mode1,
+ const struct drm_display_mode *mode2)
+{
+ int mode1_vsync_start_offset = mode1->vtotal - mode1->vsync_start;
+ int mode1_vsync_end_offset = mode1->vtotal - mode1->vsync_end;
+ int mode2_vsync_start_offset = mode2->vtotal - mode2->vsync_start;
+ int mode2_vsync_end_offset = mode2->vtotal - mode2->vsync_end;
+
+ return mode1->hdisplay == mode2->hdisplay &&
+ mode1->hsync_start == mode2->hsync_start &&
+ mode1->hsync_end == mode2->hsync_end &&
+ mode1->htotal == mode2->htotal &&
+ mode1->hskew == mode2->hskew &&
+ mode1->vdisplay == mode2->vdisplay &&
+ mode1_vsync_start_offset == mode2_vsync_start_offset &&
+ mode1_vsync_end_offset == mode2_vsync_end_offset &&
+ mode1->vscan == mode2->vscan;
+}
+
static bool drm_mode_match_timings(const struct drm_display_mode *mode1,
const struct drm_display_mode *mode2)
{
@@ -1538,6 +1557,10 @@ bool drm_mode_match(const struct drm_display_mode *mode1,
if (!mode1 || !mode2)
return false;
+ if (match_flags & DRM_MODE_MATCH_TIMINGS_VRR &&
+ !drm_mode_match_timings_vrr(mode1, mode2))
+ return false;
+
if (match_flags & DRM_MODE_MATCH_TIMINGS &&
!drm_mode_match_timings(mode1, mode2))
return false;
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 07802a7f4ce5..c4de717505d7 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -83,8 +83,7 @@ i915-y += \
i915_fb_pin.o \
i915_hdcp_gsc.o \
i915_initial_plane.o \
- i915_overlay.o \
- i915_panic.o
+ i915_overlay.o
# "Graphics Technology" (aka we talk to the gpu)
gt-y += \
@@ -167,6 +166,7 @@ gem-y += \
gem/i915_gem_object.o \
gem/i915_gem_object_frontbuffer.o \
gem/i915_gem_pages.o \
+ gem/i915_gem_panic.o \
gem/i915_gem_phys.o \
gem/i915_gem_pm.o \
gem/i915_gem_region.o \
@@ -356,6 +356,7 @@ i915-y += \
display/intel_dp_aux.o \
display/intel_dp_aux_backlight.o \
display/intel_dp_hdcp.o \
+ display/intel_dp_link_caps.o \
display/intel_dp_link_training.o \
display/intel_dp_mst.o \
display/intel_dp_test.o \
diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c
index 5ff1cdf4581a..b867443ff227 100644
--- a/drivers/gpu/drm/i915/display/g4x_dp.c
+++ b/drivers/gpu/drm/i915/display/g4x_dp.c
@@ -1222,7 +1222,8 @@ static bool ilk_digital_port_connected(struct intel_encoder *encoder)
return intel_de_read(display, DEISR) & bit;
}
-static int g4x_dp_compute_config(struct intel_encoder *encoder,
+static int g4x_dp_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -1232,7 +1233,7 @@ static int g4x_dp_compute_config(struct intel_encoder *encoder,
if (HAS_PCH_SPLIT(display) && encoder->port != PORT_A)
crtc_state->has_pch_encoder = true;
- ret = intel_dp_compute_config(encoder, crtc_state, conn_state);
+ ret = intel_dp_compute_config(state, encoder, crtc_state, conn_state);
if (ret)
return ret;
@@ -1252,10 +1253,13 @@ static void g4x_dp_suspend_complete(struct intel_encoder *encoder)
static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
{
+ struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder));
+
intel_dp_encoder_flush_work(encoder);
drm_encoder_cleanup(encoder);
- kfree(enc_to_dig_port(to_intel_encoder(encoder)));
+ intel_dp_link_cleanup(&dig_port->dp);
+ kfree(dig_port);
}
static void intel_dp_encoder_reset(struct drm_encoder *encoder)
@@ -1350,6 +1354,9 @@ bool g4x_dp_init(struct intel_display *display,
intel_encoder->audio_enable = g4x_dp_audio_enable;
intel_encoder->audio_disable = g4x_dp_audio_disable;
+ if (intel_dp_link_init(&dig_port->dp) != 0)
+ goto err_dp_init;
+
if ((display->platform.ivybridge && port == PORT_A) ||
(HAS_PCH_CPT(display) && port != PORT_A)) {
dig_port->dp.set_link_train = cpt_set_link_train;
@@ -1419,6 +1426,8 @@ bool g4x_dp_init(struct intel_display *display,
return true;
err_init_connector:
+ intel_dp_link_cleanup(&dig_port->dp);
+err_dp_init:
drm_encoder_cleanup(encoder);
err_encoder_init:
kfree(intel_connector);
diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c
index acb36cab999c..4c33aa1d1d32 100644
--- a/drivers/gpu/drm/i915/display/g4x_hdmi.c
+++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c
@@ -126,12 +126,12 @@ static bool g4x_compute_has_hdmi_sink(struct intel_atomic_state *state,
return false;
}
-static int g4x_hdmi_compute_config(struct intel_encoder *encoder,
+static int g4x_hdmi_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
- struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
if (HAS_PCH_SPLIT(display))
diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index a549f1fac810..ea0cdb7822f3 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -1657,7 +1657,8 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder,
return 0;
}
-static int gen11_dsi_compute_config(struct intel_encoder *encoder,
+static int gen11_dsi_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -1671,7 +1672,7 @@ static int gen11_dsi_compute_config(struct intel_encoder *encoder,
pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- ret = intel_panel_compute_config(intel_connector, adjusted_mode);
+ ret = intel_panel_compute_config(state, pipe_config, intel_connector);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/display/intel_alpm.c b/drivers/gpu/drm/i915/display/intel_alpm.c
index c6963ea420cc..f1383764b702 100644
--- a/drivers/gpu/drm/i915/display/intel_alpm.c
+++ b/drivers/gpu/drm/i915/display/intel_alpm.c
@@ -291,7 +291,9 @@ void intel_alpm_lobf_compute_config_late(struct intel_dp *intel_dp,
if (!crtc_state->has_lobf)
return;
- if (!intel_alpm_lobf_is_window1_sufficient(crtc_state)) {
+ if (crtc_state->has_psr ||
+ !intel_vrr_is_fixed_rr(crtc_state) ||
+ !intel_alpm_lobf_is_window1_sufficient(crtc_state)) {
crtc_state->has_lobf = false;
return;
}
@@ -343,11 +345,7 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
if (!intel_dp->as_sdp_supported)
return;
- if (crtc_state->has_psr)
- return;
-
- if (!intel_vrr_always_use_vrr_tg(display) ||
- !intel_vrr_is_fixed_rr(crtc_state))
+ if (!intel_vrr_always_use_vrr_tg(display))
return;
if (!(intel_alpm_aux_wake_supported(intel_dp) ||
@@ -407,6 +405,11 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
if (crtc_state->disable_as_sdp_when_pr_active)
pr_alpm_ctl |= PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE;
+ if (intel_display_power_dc3co_allowed(display))
+ pr_alpm_ctl |= PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL;
+ else
+ pr_alpm_ctl &= ~PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL;
+
intel_de_write(display, PR_ALPM_CTL(display, cpu_transcoder),
pr_alpm_ctl);
}
diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index ded2ee497bbf..97cbae2e547e 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -2217,52 +2217,52 @@ static u8 translate_iboost(struct intel_display *display, u8 val)
static const u8 cnp_ddc_pin_map[] = {
[0] = 0, /* N/A */
- [GMBUS_PIN_1_BXT] = DDC_BUS_DDI_B,
- [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_C,
- [GMBUS_PIN_4_CNP] = DDC_BUS_DDI_D, /* sic */
- [GMBUS_PIN_3_BXT] = DDC_BUS_DDI_F, /* sic */
+ [GMBUS_PIN_1] = DDC_BUS_DDI_B,
+ [GMBUS_PIN_2] = DDC_BUS_DDI_C,
+ [GMBUS_PIN_4] = DDC_BUS_DDI_D, /* sic */
+ [GMBUS_PIN_3] = DDC_BUS_DDI_F, /* sic */
};
static const u8 icp_ddc_pin_map[] = {
- [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
- [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B,
- [GMBUS_PIN_3_BXT] = TGL_DDC_BUS_DDI_C,
- [GMBUS_PIN_9_TC1_ICP] = ICL_DDC_BUS_PORT_1,
- [GMBUS_PIN_10_TC2_ICP] = ICL_DDC_BUS_PORT_2,
- [GMBUS_PIN_11_TC3_ICP] = ICL_DDC_BUS_PORT_3,
- [GMBUS_PIN_12_TC4_ICP] = ICL_DDC_BUS_PORT_4,
- [GMBUS_PIN_13_TC5_TGP] = TGL_DDC_BUS_PORT_5,
- [GMBUS_PIN_14_TC6_TGP] = TGL_DDC_BUS_PORT_6,
+ [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
+ [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B,
+ [GMBUS_PIN_3] = TGL_DDC_BUS_DDI_C,
+ [GMBUS_PIN_9_TC1] = ICL_DDC_BUS_PORT_1,
+ [GMBUS_PIN_10_TC2] = ICL_DDC_BUS_PORT_2,
+ [GMBUS_PIN_11_TC3] = ICL_DDC_BUS_PORT_3,
+ [GMBUS_PIN_12_TC4] = ICL_DDC_BUS_PORT_4,
+ [GMBUS_PIN_13_TC5] = TGL_DDC_BUS_PORT_5,
+ [GMBUS_PIN_14_TC6] = TGL_DDC_BUS_PORT_6,
};
static const u8 rkl_pch_tgp_ddc_pin_map[] = {
- [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
- [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B,
- [GMBUS_PIN_9_TC1_ICP] = RKL_DDC_BUS_DDI_D,
- [GMBUS_PIN_10_TC2_ICP] = RKL_DDC_BUS_DDI_E,
+ [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
+ [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B,
+ [GMBUS_PIN_9_TC1] = RKL_DDC_BUS_DDI_D,
+ [GMBUS_PIN_10_TC2] = RKL_DDC_BUS_DDI_E,
};
static const u8 adls_ddc_pin_map[] = {
- [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
- [GMBUS_PIN_9_TC1_ICP] = ADLS_DDC_BUS_PORT_TC1,
- [GMBUS_PIN_10_TC2_ICP] = ADLS_DDC_BUS_PORT_TC2,
- [GMBUS_PIN_11_TC3_ICP] = ADLS_DDC_BUS_PORT_TC3,
- [GMBUS_PIN_12_TC4_ICP] = ADLS_DDC_BUS_PORT_TC4,
+ [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
+ [GMBUS_PIN_9_TC1] = ADLS_DDC_BUS_PORT_TC1,
+ [GMBUS_PIN_10_TC2] = ADLS_DDC_BUS_PORT_TC2,
+ [GMBUS_PIN_11_TC3] = ADLS_DDC_BUS_PORT_TC3,
+ [GMBUS_PIN_12_TC4] = ADLS_DDC_BUS_PORT_TC4,
};
static const u8 gen9bc_tgp_ddc_pin_map[] = {
- [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_B,
- [GMBUS_PIN_9_TC1_ICP] = DDC_BUS_DDI_C,
- [GMBUS_PIN_10_TC2_ICP] = DDC_BUS_DDI_D,
+ [GMBUS_PIN_2] = DDC_BUS_DDI_B,
+ [GMBUS_PIN_9_TC1] = DDC_BUS_DDI_C,
+ [GMBUS_PIN_10_TC2] = DDC_BUS_DDI_D,
};
static const u8 adlp_ddc_pin_map[] = {
- [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
- [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B,
- [GMBUS_PIN_9_TC1_ICP] = ADLP_DDC_BUS_PORT_TC1,
- [GMBUS_PIN_10_TC2_ICP] = ADLP_DDC_BUS_PORT_TC2,
- [GMBUS_PIN_11_TC3_ICP] = ADLP_DDC_BUS_PORT_TC3,
- [GMBUS_PIN_12_TC4_ICP] = ADLP_DDC_BUS_PORT_TC4,
+ [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
+ [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B,
+ [GMBUS_PIN_9_TC1] = ADLP_DDC_BUS_PORT_TC1,
+ [GMBUS_PIN_10_TC2] = ADLP_DDC_BUS_PORT_TC2,
+ [GMBUS_PIN_11_TC3] = ADLP_DDC_BUS_PORT_TC3,
+ [GMBUS_PIN_12_TC4] = ADLP_DDC_BUS_PORT_TC4,
};
static u8 map_ddc_pin(struct intel_display *display, u8 vbt_pin)
diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c
index dc5a5b639d87..41539fdfeac5 100644
--- a/drivers/gpu/drm/i915/display/intel_bw.c
+++ b/drivers/gpu/drm/i915/display/intel_bw.c
@@ -59,7 +59,7 @@ struct intel_psf_gv_point {
struct intel_qgv_info {
struct intel_qgv_point points[I915_NUM_QGV_POINTS];
struct intel_psf_gv_point psf_points[I915_NUM_PSF_GV_POINTS];
- u8 num_points;
+ u8 num_qgv_points;
u8 num_psf_points;
u8 t_bl;
u8 max_numchannels;
@@ -155,8 +155,8 @@ static int adls_pcode_read_psf_gv_point_info(struct intel_display *display,
static u16 icl_qgv_points_mask(struct intel_display *display)
{
- unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points;
- unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
+ unsigned int num_psf_gv_points = display->bw.num_psf_gv_points;
+ unsigned int num_qgv_points = display->bw.num_qgv_points;
u16 qgv_points = 0, psf_points = 0;
/*
@@ -252,7 +252,7 @@ static int icl_get_qgv_points(struct intel_display *display,
{
int i, ret;
- qi->num_points = dram_info->num_qgv_points;
+ qi->num_qgv_points = dram_info->num_qgv_points;
qi->num_psf_points = dram_info->num_psf_gv_points;
if (DISPLAY_VER(display) >= 14) {
@@ -324,10 +324,10 @@ static int icl_get_qgv_points(struct intel_display *display,
}
if (drm_WARN_ON(display->drm,
- qi->num_points > ARRAY_SIZE(qi->points)))
- qi->num_points = ARRAY_SIZE(qi->points);
+ qi->num_qgv_points > ARRAY_SIZE(qi->points)))
+ qi->num_qgv_points = ARRAY_SIZE(qi->points);
- for (i = 0; i < qi->num_points; i++) {
+ for (i = 0; i < qi->num_qgv_points; i++) {
struct intel_qgv_point *sp = &qi->points[i];
ret = intel_read_qgv_point_info(display, sp, i);
@@ -373,7 +373,7 @@ static int icl_sagv_max_dclk(const struct intel_qgv_info *qi)
u16 dclk = 0;
int i;
- for (i = 0; i < qi->num_points; i++)
+ for (i = 0; i < qi->num_qgv_points; i++)
dclk = max(dclk, qi->points[i].dclk);
return dclk;
@@ -536,6 +536,9 @@ static int icl_get_bw_info(struct intel_display *display,
ipqdepth = min(ipqdepthpch, display_bw_params->displayrtids / num_channels);
qi.deinterleave = DIV_ROUND_UP(num_channels, is_y_tile(display) ? 4 : 2);
+ display->bw.num_qgv_points = qi.num_qgv_points;
+ display->bw.num_psf_gv_points = qi.num_psf_points;
+
for (i = 0; i < num_groups; i++) {
struct intel_bw_info *bi = &display->bw.max[i];
int clpchgroup;
@@ -544,10 +547,7 @@ static int icl_get_bw_info(struct intel_display *display,
clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i;
bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1;
- bi->num_qgv_points = qi.num_points;
- bi->num_psf_gv_points = qi.num_psf_points;
-
- for (j = 0; j < qi.num_points; j++) {
+ for (j = 0; j < qi.num_qgv_points; j++) {
const struct intel_qgv_point *sp = &qi.points[j];
int ct, bw;
@@ -574,7 +574,7 @@ static int icl_get_bw_info(struct intel_display *display,
* SAGV point, but we can't send PCode commands to restrict it
* as it will fail and pointless anyway.
*/
- if (qi.num_points == 1)
+ if (qi.num_qgv_points == 1)
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
else
display->sagv.status = I915_SAGV_ENABLED;
@@ -629,16 +629,20 @@ static int tgl_get_bw_info(struct intel_display *display,
*/
clperchgroup = 4 * (8 / num_channels) * qi.deinterleave;
+ display->bw.num_qgv_points = qi.num_qgv_points;
+ display->bw.num_psf_gv_points = qi.num_psf_points;
+
+ display->bw.max[0].num_planes = U8_MAX;
+
for (i = 0; i < num_groups; i++) {
struct intel_bw_info *bi = &display->bw.max[i];
- struct intel_bw_info *bi_next;
int clpchgroup;
int j;
clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i;
if (i < num_groups - 1) {
- bi_next = &display->bw.max[i + 1];
+ struct intel_bw_info *bi_next = &display->bw.max[i + 1];
if (clpchgroup < clperchgroup)
bi_next->num_planes = (ipqdepth - clpchgroup) / clpchgroup;
@@ -646,10 +650,7 @@ static int tgl_get_bw_info(struct intel_display *display,
bi_next->num_planes = 0;
}
- bi->num_qgv_points = qi.num_points;
- bi->num_psf_gv_points = qi.num_psf_points;
-
- for (j = 0; j < qi.num_points; j++) {
+ for (j = 0; j < qi.num_qgv_points; j++) {
const struct intel_qgv_point *sp = &qi.points[j];
int ct, bw;
@@ -665,23 +666,27 @@ static int tgl_get_bw_info(struct intel_display *display,
bi->deratedbw[j] = min(maxdebw,
bw * (100 - soc_bw_params->derating) / 100);
- bi->peakbw[j] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk);
drm_dbg_kms(display->drm,
- "BW%d / QGV %d: num_planes=%d deratedbw=%u peakbw: %u\n",
- i, j, bi->num_planes, bi->deratedbw[j],
- bi->peakbw[j]);
+ "BW%d / QGV %d: num_planes=%d deratedbw=%u\n",
+ i, j, bi->num_planes, bi->deratedbw[j]);
}
+ }
- for (j = 0; j < qi.num_psf_points; j++) {
- const struct intel_psf_gv_point *sp = &qi.psf_points[j];
+ for (i = 0; i < qi.num_qgv_points; i++) {
+ const struct intel_qgv_point *sp = &qi.points[i];
- bi->psf_bw[j] = adl_calc_psf_bw(sp->clk);
+ display->bw.peakbw[i] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk);
- drm_dbg_kms(display->drm,
- "BW%d / PSF GV %d: num_planes=%d bw=%u\n",
- i, j, bi->num_planes, bi->psf_bw[j]);
- }
+ drm_dbg_kms(display->drm, "QGV %d: peakbw=%u\n", i, display->bw.peakbw[i]);
+ }
+
+ for (i = 0; i < qi.num_psf_points; i++) {
+ const struct intel_psf_gv_point *sp = &qi.psf_points[i];
+
+ display->bw.psf_bw[i] = adl_calc_psf_bw(sp->clk);
+
+ drm_dbg_kms(display->drm, "PSF GV %d: bw=%u\n", i, display->bw.psf_bw[i]);
}
/*
@@ -689,7 +694,7 @@ static int tgl_get_bw_info(struct intel_display *display,
* SAGV point, but we can't send PCode commands to restrict it
* as it will fail and pointless anyway.
*/
- if (qi.num_points == 1)
+ if (qi.num_qgv_points == 1)
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
else
display->sagv.status = I915_SAGV_ENABLED;
@@ -699,25 +704,20 @@ static int tgl_get_bw_info(struct intel_display *display,
static void dg2_get_bw_info(struct intel_display *display)
{
- unsigned int deratedbw = display->platform.dg2_g11 ? 38000 : 50000;
- int num_groups = ARRAY_SIZE(display->bw.max);
int i;
- /*
- * DG2 doesn't have SAGV or QGV points, just a constant max bandwidth
- * that doesn't depend on the number of planes enabled. So fill all the
- * plane group with constant bw information for uniformity with other
- * platforms. DG2-G10 platforms have a constant 50 GB/s bandwidth,
- * whereas DG2-G11 platforms have 38 GB/s.
- */
- for (i = 0; i < num_groups; i++) {
- struct intel_bw_info *bi = &display->bw.max[i];
+ display->bw.num_qgv_points = 1;
- bi->num_planes = 1;
- /* Need only one dummy QGV point per group */
- bi->num_qgv_points = 1;
- bi->deratedbw[0] = deratedbw;
- }
+ display->bw.max[0].num_planes = U8_MAX;
+ display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000;
+
+ drm_dbg_kms(display->drm,
+ "QGV 0: deratedbw=%u\n",
+ display->bw.max[0].deratedbw[0]);
+
+ /* Bandwidth does not depend on # of planes; set all groups the same */
+ for (i = 1; i < ARRAY_SIZE(display->bw.max); i++)
+ display->bw.max[i] = display->bw.max[0];
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
}
@@ -741,31 +741,32 @@ static int xe2_hpd_get_bw_info(struct intel_display *display,
peakbw = tgl_peakbw(num_channels, qi.channel_width, icl_sagv_max_dclk(&qi));
maxdebw = min(soc_bw_params->deprogbwlimit * 1000, peakbw * DEPROGBWPCLIMIT / 100);
- for (i = 0; i < qi.num_points; i++) {
+ display->bw.num_qgv_points = qi.num_qgv_points;
+
+ display->bw.max[0].num_planes = U8_MAX;
+
+ for (i = 0; i < qi.num_qgv_points; i++) {
const struct intel_qgv_point *sp = &qi.points[i];
int bw = tgl_peakbw(num_channels, qi.channel_width, sp->dclk);
display->bw.max[0].deratedbw[i] =
min(maxdebw, (100 - soc_bw_params->derating) * bw / 100);
- display->bw.max[0].peakbw[i] = bw;
- drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw: %u\n",
- i, display->bw.max[0].deratedbw[i],
- display->bw.max[0].peakbw[i]);
+ display->bw.peakbw[i] = bw;
+
+ drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw=%u\n",
+ i, display->bw.max[0].deratedbw[i], display->bw.peakbw[i]);
}
/* Bandwidth does not depend on # of planes; set all groups the same */
- display->bw.max[0].num_planes = 1;
- display->bw.max[0].num_qgv_points = qi.num_points;
for (i = 1; i < ARRAY_SIZE(display->bw.max); i++)
- memcpy(&display->bw.max[i], &display->bw.max[0],
- sizeof(display->bw.max[0]));
+ display->bw.max[i] = display->bw.max[0];
/*
* Xe2_HPD should always have exactly two QGV points representing
* battery and plugged-in operation.
*/
- drm_WARN_ON(display->drm, qi.num_points != 2);
+ drm_WARN_ON(display->drm, qi.num_qgv_points != 2);
display->sagv.status = I915_SAGV_ENABLED;
return 0;
@@ -776,6 +777,9 @@ static unsigned int icl_max_bw_index(struct intel_display *display,
{
int i;
+ if (qgv_point >= display->bw.num_qgv_points)
+ return UINT_MAX;
+
/*
* Let's return max bw for 0 planes
*/
@@ -785,13 +789,6 @@ static unsigned int icl_max_bw_index(struct intel_display *display,
const struct intel_bw_info *bi =
&display->bw.max[i];
- /*
- * Pcode will not expose all QGV points when
- * SAGV is forced to off/min/med/max.
- */
- if (qgv_point >= bi->num_qgv_points)
- return UINT_MAX;
-
if (num_planes >= bi->num_planes)
return i;
}
@@ -804,31 +801,24 @@ static unsigned int tgl_max_bw_index(struct intel_display *display,
{
int i;
+ if (qgv_point >= display->bw.num_qgv_points)
+ return UINT_MAX;
+
for (i = ARRAY_SIZE(display->bw.max) - 1; i >= 0; i--) {
const struct intel_bw_info *bi =
&display->bw.max[i];
- /*
- * Pcode will not expose all QGV points when
- * SAGV is forced to off/min/med/max.
- */
- if (qgv_point >= bi->num_qgv_points)
- return UINT_MAX;
-
if (num_planes <= bi->num_planes)
return i;
}
- return 0;
+ return UINT_MAX;
}
static unsigned int adl_psf_bw(struct intel_display *display,
int psf_gv_point)
{
- const struct intel_bw_info *bi =
- &display->bw.max[0];
-
- return bi->psf_bw[psf_gv_point];
+ return display->bw.psf_bw[psf_gv_point];
}
static unsigned int icl_qgv_bw(struct intel_display *display,
@@ -950,7 +940,7 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state)
static unsigned int icl_max_bw_qgv_point_mask(struct intel_display *display,
int num_active_planes)
{
- unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
+ unsigned int num_qgv_points = display->bw.num_qgv_points;
unsigned int max_bw_point = 0;
unsigned int max_bw = 0;
int i;
@@ -986,7 +976,7 @@ static u16 icl_prepare_qgv_points_mask(struct intel_display *display,
static unsigned int icl_max_bw_psf_gv_point_mask(struct intel_display *display)
{
- unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points;
+ unsigned int num_psf_gv_points = display->bw.num_psf_gv_points;
unsigned int max_bw_point_mask = 0;
unsigned int max_bw = 0;
int i;
@@ -1091,7 +1081,7 @@ static int mtl_find_qgv_points(struct intel_display *display,
struct intel_bw_state *new_bw_state)
{
unsigned int best_rate = UINT_MAX;
- unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
+ unsigned int num_qgv_points = display->bw.num_qgv_points;
unsigned int qgv_peak_bw = 0;
int i;
int ret;
@@ -1116,21 +1106,15 @@ static int mtl_find_qgv_points(struct intel_display *display,
* offered per plane group
*/
for (i = 0; i < num_qgv_points; i++) {
- unsigned int bw_index =
- tgl_max_bw_index(display, num_active_planes, i);
- unsigned int max_data_rate;
-
- if (bw_index >= ARRAY_SIZE(display->bw.max))
- continue;
-
- max_data_rate = display->bw.max[bw_index].deratedbw[i];
+ unsigned int max_data_rate =
+ icl_qgv_bw(display, num_active_planes, i);
if (max_data_rate < data_rate)
continue;
- if (max_data_rate - data_rate < best_rate) {
- best_rate = max_data_rate - data_rate;
- qgv_peak_bw = display->bw.max[bw_index].peakbw[i];
+ if (max_data_rate < best_rate) {
+ best_rate = max_data_rate;
+ qgv_peak_bw = display->bw.peakbw[i];
}
drm_dbg_kms(display->drm, "QGV point %d: max bw %d required %d qgv_peak_bw: %d\n",
@@ -1162,8 +1146,8 @@ static int icl_find_qgv_points(struct intel_display *display,
const struct intel_bw_state *old_bw_state,
struct intel_bw_state *new_bw_state)
{
- unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points;
- unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
+ unsigned int num_psf_gv_points = display->bw.num_psf_gv_points;
+ unsigned int num_qgv_points = display->bw.num_qgv_points;
u16 psf_points = 0;
u16 qgv_points = 0;
int i;
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index 7bc9b956554b..d3c5e3438d19 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -1229,23 +1229,28 @@ static void skl_set_cdclk(struct intel_display *display,
static void skl_sanitize_cdclk(struct intel_display *display)
{
- u32 cdctl, expected;
+ u32 cdctl, expected, swf18;
/*
* check if the pre-os initialized the display
* There is SWF18 scratchpad register defined which is set by the
* pre-os which can be used by the OS drivers to check the status
*/
- if ((intel_de_read(display, SWF_ILK(0x18)) & 0x00FFFFFF) == 0)
+ swf18 = intel_de_read(display, SWF_ILK(0x18));
+ if ((swf18 & 0x00FFFFFF) == 0) {
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK due to SWF18 0x%x\n", swf18);
goto sanitize;
+ }
intel_update_cdclk(display);
intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK");
/* Is PLL enabled and locked ? */
if (display->cdclk.hw.vco == 0 ||
- display->cdclk.hw.cdclk == display->cdclk.hw.bypass)
+ display->cdclk.hw.cdclk == display->cdclk.hw.bypass) {
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n");
goto sanitize;
+ }
/* DPLL okay; verify the cdclock
*
@@ -1261,8 +1266,11 @@ static void skl_sanitize_cdclk(struct intel_display *display)
cdctl &= ~CDCLK_FREQ_DECIMAL_MASK;
cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK;
- if (cdctl != expected)
+ if (cdctl != expected) {
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x)\n",
+ intel_de_read(display, CDCLK_CTL), expected);
goto sanitize;
+ }
drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n",
intel_de_read(display, CDCLK_CTL), expected);
@@ -1274,8 +1282,6 @@ static void skl_sanitize_cdclk(struct intel_display *display)
return;
sanitize:
- drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n");
-
/* force cdclk programming */
display->cdclk.hw.cdclk = 0;
/* force full PLL disable + enable */
@@ -1938,14 +1944,17 @@ static void adlp_cdclk_pll_crawl(struct intel_display *display, int vco)
display->cdclk.hw.vco = vco;
}
+static u32 bxt_cdclk_cd2x_pipe_mask(struct intel_display *display)
+{
+ if (DISPLAY_VER(display) >= 11)
+ return ICL_CDCLK_CD2X_PIPE_MASK;
+ else
+ return BXT_CDCLK_CD2X_PIPE_MASK;
+}
+
static u32 bxt_cdclk_cd2x_pipe(struct intel_display *display, enum pipe pipe)
{
- if (DISPLAY_VER(display) >= 12) {
- if (pipe == INVALID_PIPE)
- return TGL_CDCLK_CD2X_PIPE_NONE;
- else
- return TGL_CDCLK_CD2X_PIPE(pipe);
- } else if (DISPLAY_VER(display) >= 11) {
+ if (DISPLAY_VER(display) >= 11) {
if (pipe == INVALID_PIPE)
return ICL_CDCLK_CD2X_PIPE_NONE;
else
@@ -2340,18 +2349,24 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK");
if (display->cdclk.hw.vco == 0 ||
- display->cdclk.hw.cdclk == display->cdclk.hw.bypass)
+ display->cdclk.hw.cdclk == display->cdclk.hw.bypass) {
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n");
goto sanitize;
+ }
/* Make sure this is a legal cdclk value for the platform */
cdclk = bxt_calc_cdclk(display, display->cdclk.hw.cdclk);
- if (cdclk != display->cdclk.hw.cdclk)
+ if (cdclk != display->cdclk.hw.cdclk) {
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad CDCLK frequency\n");
goto sanitize;
+ }
/* Make sure the VCO is correct for the cdclk */
vco = bxt_calc_cdclk_pll_vco(display, cdclk);
- if (vco != display->cdclk.hw.vco)
+ if (vco != display->cdclk.hw.vco) {
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad VCO frequency\n");
goto sanitize;
+ }
/*
* Some BIOS versions leave an incorrect decimal frequency value and
@@ -2366,7 +2381,7 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
* dividers both syncing to an active pipe, or asynchronously
* (PIPE_NONE).
*/
- cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
+ cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display);
cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
if (cdctl != expected) {
@@ -2375,8 +2390,11 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK;
}
- if (cdctl != expected)
+ if (cdctl != expected) {
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x\n",
+ intel_de_read(display, CDCLK_CTL), expected);
goto sanitize;
+ }
drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n",
intel_de_read(display, CDCLK_CTL), expected);
@@ -2388,8 +2406,6 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
return;
sanitize:
- drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n");
-
/* force cdclk programming */
display->cdclk.hw.cdclk = 0;
diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c
index e1fdc6fe9762..c8e0f90af910 100644
--- a/drivers/gpu/drm/i915/display/intel_cmtg.c
+++ b/drivers/gpu/drm/i915/display/intel_cmtg.c
@@ -4,7 +4,6 @@
*/
#include <linux/string_choices.h>
-#include <linux/types.h>
#include <drm/drm_device.h>
#include <drm/drm_print.h>
@@ -13,9 +12,14 @@
#include "intel_cmtg_regs.h"
#include "intel_crtc.h"
#include "intel_de.h"
+#include "intel_display.h"
#include "intel_display_device.h"
+#include "intel_display_irq.h"
#include "intel_display_power.h"
#include "intel_display_regs.h"
+#include "intel_display_types.h"
+#include "intel_vrr.h"
+#include "intel_vrr_regs.h"
/**
* DOC: Common Primary Timing Generator (CMTG)
@@ -81,6 +85,18 @@ static void intel_cmtg_dump_config(struct intel_display *display,
str_yes_no(cmtg_config->trans_b_secondary));
}
+static inline enum transcoder to_cmtg_transcoder(enum transcoder cpu_transcoder)
+{
+ switch (cpu_transcoder) {
+ case TRANSCODER_A:
+ return TRANSCODER_CMTG0;
+ case TRANSCODER_B:
+ return TRANSCODER_CMTG1;
+ default:
+ return INVALID_TRANSCODER;
+ }
+}
+
static bool intel_cmtg_transcoder_is_secondary(struct intel_display *display,
enum transcoder trans)
{
@@ -103,11 +119,11 @@ static void intel_cmtg_get_config(struct intel_display *display,
{
u32 val;
- val = intel_de_read(display, TRANS_CMTG_CTL_A);
+ val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_A));
cmtg_config->cmtg_a_enable = val & CMTG_ENABLE;
if (intel_cmtg_has_cmtg_b(display)) {
- val = intel_de_read(display, TRANS_CMTG_CTL_B);
+ val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_B));
cmtg_config->cmtg_b_enable = val & CMTG_ENABLE;
}
@@ -124,8 +140,8 @@ static bool intel_cmtg_disable_requires_modeset(struct intel_display *display,
return cmtg_config->trans_a_secondary || cmtg_config->trans_b_secondary;
}
-static void intel_cmtg_disable(struct intel_display *display,
- struct intel_cmtg_config *cmtg_config)
+static void intel_cmtg_disable_all(struct intel_display *display,
+ struct intel_cmtg_config *cmtg_config)
{
u32 clk_sel_clr = 0;
u32 clk_sel_set = 0;
@@ -140,14 +156,14 @@ static void intel_cmtg_disable(struct intel_display *display,
if (cmtg_config->cmtg_a_enable) {
drm_dbg_kms(display->drm, "Disabling CMTG A\n");
- intel_de_rmw(display, TRANS_CMTG_CTL_A, CMTG_ENABLE, 0);
+ intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_A), CMTG_ENABLE, 0);
clk_sel_clr |= CMTG_CLK_SEL_A_MASK;
clk_sel_set |= CMTG_CLK_SEL_A_DISABLED;
}
if (cmtg_config->cmtg_b_enable) {
drm_dbg_kms(display->drm, "Disabling CMTG B\n");
- intel_de_rmw(display, TRANS_CMTG_CTL_B, CMTG_ENABLE, 0);
+ intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_B), CMTG_ENABLE, 0);
clk_sel_clr |= CMTG_CLK_SEL_B_MASK;
clk_sel_set |= CMTG_CLK_SEL_B_DISABLED;
}
@@ -156,6 +172,57 @@ static void intel_cmtg_disable(struct intel_display *display,
intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set);
}
+void intel_cmtg_disable(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+ enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
+ u32 clk_sel_clr = 0, interrupt_mask = 0;
+
+ if (!crtc->cmtg.enabled)
+ return;
+
+ if (drm_WARN_ON(display->drm, cmtg_transcoder == INVALID_TRANSCODER))
+ return;
+
+ crtc->cmtg.enabled = false;
+ intel_de_rmw(display, TRANS_VRR_CTL(display, cmtg_transcoder),
+ VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN, 0);
+
+ /*
+ * Use cpu_transcoder for:
+ * 1. Exclusive CMTG registers that do not use the standard transcoder offset
+ * (e.g., TRANS_CMTG_CTL, CMTG_CLK_SEL).
+ * 2. Registers shared between the eDP and CMTG transcoders.
+ * (e.g., TRANS_DDI_FUNC_CTL2).
+ */
+
+ intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder),
+ CMTG_SECONDARY_MODE, 0);
+ intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), CMTG_HW_GB_ENABLE, 0);
+
+ intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_ENABLE, 0);
+
+ if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_STATE, 50)) {
+ drm_WARN(display->drm, 1, "CMTG: %s disable timeout\n",
+ transcoder_name(cpu_transcoder));
+ return;
+ }
+
+ clk_sel_clr = cpu_transcoder == TRANSCODER_A ? CMTG_CLK_SEL_A_MASK : CMTG_CLK_SEL_B_MASK;
+ intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, 0);
+
+ drm_dbg_kms(display->drm, "CMTG: %s disabled\n", transcoder_name(cpu_transcoder));
+
+ if (cpu_transcoder == TRANSCODER_A)
+ interrupt_mask = CMTG_VBLANK_A;
+ else if (cpu_transcoder == TRANSCODER_B)
+ interrupt_mask = CMTG_VBLANK_B;
+
+ intel_display_irq_port_interrupt_mask(display, interrupt_mask, true);
+}
+
/*
* Read out CMTG configuration and, on platforms that allow disabling it without
* a modeset, do it.
@@ -183,5 +250,202 @@ void intel_cmtg_sanitize(struct intel_display *display)
if (intel_cmtg_disable_requires_modeset(display, &cmtg_config))
return;
- intel_cmtg_disable(display, &cmtg_config);
+ intel_cmtg_disable_all(display, &cmtg_config);
+}
+
+bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+
+ if ((cpu_transcoder == TRANSCODER_A || cpu_transcoder == TRANSCODER_B) &&
+ DISPLAY_VER(display) == 35 && intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
+ return true;
+
+ return false;
+}
+
+void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+ u32 clk_sel_clr = 0;
+ u32 clk_sel_set = 0;
+
+ if (!intel_cmtg_is_allowed(crtc_state))
+ return;
+
+ if (cpu_transcoder == TRANSCODER_A) {
+ clk_sel_clr = CMTG_CLK_SEL_A_MASK;
+ clk_sel_set = CMTG_CLK_SELECT_PHYA_ENABLE;
+ } else if (cpu_transcoder == TRANSCODER_B) {
+ clk_sel_clr = CMTG_CLK_SEL_B_MASK;
+ clk_sel_set = CMTG_CLK_SELECT_PHYB_ENABLE;
+ }
+
+ if (clk_sel_set)
+ intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set);
+}
+
+void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type)
+{
+ enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
+
+ if (cmtg_transcoder == INVALID_TRANSCODER)
+ return;
+
+ if (!intel_cmtg_is_allowed(crtc_state))
+ return;
+
+ if (type == LRR)
+ intel_set_transcoder_timings_lrr(crtc_state, cmtg_transcoder);
+ else
+ intel_set_transcoder_timings(crtc_state, cmtg_transcoder);
+}
+
+void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state)
+{
+ enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
+
+ if (!intel_cmtg_is_allowed(crtc_state))
+ return;
+
+ intel_vrr_set_fixed_rr_timings(crtc_state, cmtg_transcoder);
+}
+
+void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
+ u32 vrr_ctl;
+
+ if (!intel_cmtg_is_allowed(crtc_state))
+ return;
+
+ vrr_ctl = VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN |
+ XELPD_VRR_CTL_VRR_GUARDBAND(crtc_state->vrr.guardband);
+
+ /* TODO: The code below may need to be revisited once CMRR is enabled */
+ if (crtc_state->cmrr.enable)
+ vrr_ctl |= VRR_CTL_CMRR_ENABLE;
+
+ intel_de_write(display, TRANS_VRR_CTL(display, cmtg_transcoder), vrr_ctl);
+}
+
+void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
+ const struct intel_link_m_n *m_n = &crtc_state->dp_m_n;
+
+ if (!intel_cmtg_is_allowed(crtc_state))
+ return;
+
+ intel_de_write(display, PIPE_LINK_M1(display, cmtg_transcoder), m_n->link_m);
+ intel_de_write(display, PIPE_LINK_N1(display, cmtg_transcoder), m_n->link_n);
+}
+
+static void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+ u32 cmtg_ctl;
+
+ cmtg_ctl = CMTG_SYNC_TO_PORT | CMTG_ENABLE;
+
+ intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), 0, cmtg_ctl);
+ if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder),
+ CMTG_SYNC_TO_PORT, 50)) {
+ drm_WARN(display->drm, 1, "CMTG: %s enable timeout\n",
+ transcoder_name(cpu_transcoder));
+ }
+}
+
+static void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+ u32 interrupt_mask = 0;
+
+ intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE);
+ intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), 0, CMTG_HW_GB_ENABLE);
+ crtc->cmtg.enabled = true;
+ drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder));
+
+ /*
+ * TODO: Currently cmtg is enabled along with eDP transcoder so cmtg
+ * interrupt is not enabled through IER, need to do some fine
+ * tuning in future.
+ */
+
+ if (cpu_transcoder == TRANSCODER_A)
+ interrupt_mask = CMTG_VBLANK_A;
+ else if (cpu_transcoder == TRANSCODER_B)
+ interrupt_mask = CMTG_VBLANK_B;
+
+ intel_display_irq_port_interrupt_mask(display, interrupt_mask, false);
+}
+
+/* Bspec: 75253 */
+#define DC3CO_ENTRY_LATENCY_US 55
+#define DC3CO_EXIT_LATENCY_US 40
+
+static void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+ u32 breakeven_gb;
+ u32 dc5_exit_latency;
+ u32 line_time_us = 75; /* Max default initialization value */
+ u32 val;
+
+ if (crtc_state->linetime)
+ line_time_us = DIV_ROUND_UP(crtc_state->linetime, 8);
+
+ /* Break Even Guardband - DC3co Entry Latency / linetime */
+ breakeven_gb = DIV_ROUND_UP(DC3CO_ENTRY_LATENCY_US, line_time_us);
+
+ /* DC5 Exit Latency - DC3co Exit Latency / linetime */
+ dc5_exit_latency = DIV_ROUND_UP(DC3CO_EXIT_LATENCY_US, line_time_us);
+
+ val = REG_FIELD_PREP(CMTG_HW_GB_BREAKEVEN_MASK, breakeven_gb) |
+ REG_FIELD_PREP(CMTG_HW_GB_DC5_EXIT_LATENCY_MASK, dc5_exit_latency) |
+ REG_FIELD_PREP(CMTG_HW_GB_UP_LW_BG_DIFF_MASK, 1);
+
+ intel_de_write(display, CMTG_HW_GB(cpu_transcoder), val);
+}
+
+static void intel_cmtg_restore(const struct intel_crtc_state *crtc_state)
+{
+ intel_cmtg_set_clk_select(crtc_state);
+ intel_cmtg_set_timings(crtc_state, MODESET);
+ intel_cmtg_set_vrr_timings(crtc_state);
+ intel_cmtg_set_vrr_ctl(crtc_state);
+ intel_cmtg_set_m_n(crtc_state);
+}
+
+void intel_cmtg_program(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_crtc *crtc;
+ struct intel_crtc_state *new_crtc_state;
+ bool dc3co_to_dc6 = intel_display_power_get_and_reset_dc3co_to_dc6(display);
+
+ for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) {
+ bool modeset = intel_crtc_needs_modeset(new_crtc_state);
+
+ if (!intel_cmtg_is_allowed(new_crtc_state))
+ continue;
+
+ if ((modeset || dc3co_to_dc6) &&
+ new_crtc_state->hw.active && !crtc->cmtg.enabled) {
+ if (dc3co_to_dc6)
+ intel_cmtg_restore(new_crtc_state);
+
+ intel_cmtg_enable_sync(new_crtc_state);
+ intel_cmtg_set_hwgb(new_crtc_state);
+ intel_cmtg_enable_ddi(new_crtc_state);
+ }
+ }
}
diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h
index ba62199adaa2..a08cb2dcee67 100644
--- a/drivers/gpu/drm/i915/display/intel_cmtg.h
+++ b/drivers/gpu/drm/i915/display/intel_cmtg.h
@@ -6,8 +6,25 @@
#ifndef __INTEL_CMTG_H__
#define __INTEL_CMTG_H__
+#include <linux/types.h>
+
+struct intel_atomic_state;
struct intel_display;
+struct intel_crtc_state;
+
+enum set_timing_type {
+ MODESET = 0,
+ LRR
+};
+void intel_cmtg_disable(const struct intel_crtc_state *crtc_state);
+void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state);
+void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state);
+void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state);
+void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type);
+void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state);
void intel_cmtg_sanitize(struct intel_display *display);
+bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state);
+void intel_cmtg_program(struct intel_atomic_state *state);
#endif /* __INTEL_CMTG_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h
index 945a35578284..18dcb665df04 100644
--- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h
+++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h
@@ -10,12 +10,32 @@
#define CMTG_CLK_SEL _MMIO(0x46160)
#define CMTG_CLK_SEL_A_MASK REG_GENMASK(31, 29)
+#define CMTG_CLK_SELECT_PHYA_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0x4)
#define CMTG_CLK_SEL_A_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0)
#define CMTG_CLK_SEL_B_MASK REG_GENMASK(15, 13)
+#define CMTG_CLK_SELECT_PHYB_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0x6)
#define CMTG_CLK_SEL_B_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0)
-#define TRANS_CMTG_CTL_A _MMIO(0x6fa88)
-#define TRANS_CMTG_CTL_B _MMIO(0x6fb88)
+#define _TRANS_CMTG_CTL_A 0x6fa88
+#define _TRANS_CMTG_CTL_B 0x6fb88
+#define TRANS_CMTG_CTL(trans) _MMIO_TRANS((trans), \
+ _TRANS_CMTG_CTL_A, _TRANS_CMTG_CTL_B)
#define CMTG_ENABLE REG_BIT(31)
+#define CMTG_SYNC_TO_PORT REG_BIT(29)
+#define CMTG_STATE REG_BIT(23)
+
+#define _CMTG_HW_GB_A 0x6fa8c
+#define _CMTG_HW_GB_B 0x6fb8c
+#define CMTG_HW_GB(trans) _MMIO_TRANS((trans), \
+ _CMTG_HW_GB_A, _CMTG_HW_GB_B)
+#define CMTG_HW_GB_BREAKEVEN_MASK REG_GENMASK(11, 0)
+#define CMTG_HW_GB_DC5_EXIT_LATENCY_MASK REG_GENMASK(27, 16)
+#define CMTG_HW_GB_UP_LW_BG_DIFF_MASK REG_GENMASK(31, 28)
+
+#define _CMTG_SCANLINE_GB1_A 0x456A0
+#define _CMTG_SCANLINE_GB1_B 0x456C0
+#define CMTG_SCANLINE_GB1(trans) _MMIO_TRANS((trans), \
+ _CMTG_SCANLINE_GB1_A, _CMTG_SCANLINE_GB1_B)
+#define CMTG_HW_GB_ENABLE REG_BIT(31)
#endif /* __INTEL_CMTG_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c
index 7ef870cd9a16..87ced9f6ff40 100644
--- a/drivers/gpu/drm/i915/display/intel_color.c
+++ b/drivers/gpu/drm/i915/display/intel_color.c
@@ -3967,48 +3967,34 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb,
enum pipe pipe = to_intel_plane(state->plane)->pipe;
enum plane_id plane = to_intel_plane(state->plane)->id;
const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data;
- u32 i, lut_size;
+ int i, lut_size = 128;
+ u32 lut_val;
- if (icl_is_hdr_plane(display, plane)) {
- lut_size = 128;
+ if (!icl_is_hdr_plane(display, plane))
+ return;
- intel_de_write_dsb(display, dsb,
- PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
- PLANE_PAL_PREC_AUTO_INCREMENT);
+ intel_de_write_dsb(display, dsb,
+ PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
+ PLANE_PAL_PREC_AUTO_INCREMENT);
+ for (i = 0; i < lut_size + 3; i++) {
if (pre_csc_lut) {
- for (i = 0; i < lut_size; i++) {
- u32 lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24);
-
- intel_de_write_dsb(display, dsb,
- PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
- lut_val);
- }
-
- /* Program the max register to clamp values > 1.0. */
- /* TODO: Restrict to 0x7ffffff */
- do {
- intel_de_write_dsb(display, dsb,
- PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
- (1 << 24));
- } while (i++ < 130);
+ if (i < lut_size)
+ lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24);
+ /* else duplicate last lut_val */
} else {
- for (i = 0; i < lut_size; i++) {
- u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);
-
- intel_de_write_dsb(display, dsb,
- PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
- }
-
- do {
- intel_de_write_dsb(display, dsb,
- PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
- 1 << 24);
- } while (i++ < 130);
+ if (i < lut_size)
+ lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1);
+ else
+ lut_val = 1 << 24;
}
- intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
+ intel_de_write_dsb(display, dsb,
+ PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ lut_val);
}
+
+ intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
}
static void
@@ -4020,51 +4006,38 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb,
enum pipe pipe = to_intel_plane(state->plane)->pipe;
enum plane_id plane = to_intel_plane(state->plane)->id;
const struct drm_color_lut32 *post_csc_lut = plane_state->hw.gamma_lut->data;
- u32 i, lut_size, lut_val;
-
- if (icl_is_hdr_plane(display, plane)) {
- intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
- PLANE_PAL_PREC_AUTO_INCREMENT);
- /* TODO: Add macro */
- intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0),
- PLANE_PAL_PREC_AUTO_INCREMENT);
+ int i, lut_size = 32;
+ u32 lut_val;
+
+ if (!icl_is_hdr_plane(display, plane))
+ return;
+
+ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
+ PLANE_PAL_PREC_AUTO_INCREMENT);
+ /* TODO: Add macro */
+ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0),
+ PLANE_PAL_PREC_AUTO_INCREMENT);
+
+ for (i = 0; i < lut_size + 3; i++) {
if (post_csc_lut) {
- lut_size = 32;
- for (i = 0; i < lut_size; i++) {
+ if (i < lut_size)
lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24);
-
- intel_de_write_dsb(display, dsb,
- PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
- lut_val);
- }
-
- /* Segment 2 */
- do {
- intel_de_write_dsb(display, dsb,
- PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
- (1 << 24));
- } while (i++ < 34);
+ /* else clamp to the last LUT value to prevent step discontinuity */
} else {
- /*TODO: Add for segment 0 */
- lut_size = 32;
- for (i = 0; i < lut_size; i++) {
- u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);
-
- intel_de_write_dsb(display, dsb,
- PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
- }
-
- do {
- intel_de_write_dsb(display, dsb,
- PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
- 1 << 24);
- } while (i++ < 34);
+ if (i < lut_size)
+ lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1);
+ else
+ lut_val = 1 << 24;
}
- intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
intel_de_write_dsb(display, dsb,
- PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0);
+ PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ lut_val);
}
+
+ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
+ intel_de_write_dsb(display, dsb,
+ PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0);
}
static void
diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c
index 243e332bef57..5b8968197fbc 100644
--- a/drivers/gpu/drm/i915/display/intel_crt.c
+++ b/drivers/gpu/drm/i915/display/intel_crt.c
@@ -397,7 +397,8 @@ intel_crt_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
-static int intel_crt_compute_config(struct intel_encoder *encoder,
+static int intel_crt_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -413,7 +414,8 @@ static int intel_crt_compute_config(struct intel_encoder *encoder,
return 0;
}
-static int pch_crt_compute_config(struct intel_encoder *encoder,
+static int pch_crt_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -432,7 +434,8 @@ static int pch_crt_compute_config(struct intel_encoder *encoder,
return 0;
}
-static int hsw_crt_compute_config(struct intel_encoder *encoder,
+static int hsw_crt_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c
index 52347668f27d..88384dea868b 100644
--- a/drivers/gpu/drm/i915/display/intel_cursor.c
+++ b/drivers/gpu/drm/i915/display/intel_cursor.c
@@ -876,7 +876,7 @@ intel_legacy_cursor_update(struct drm_plane *_plane,
new_plane_state->uapi.crtc_w = crtc_w;
new_plane_state->uapi.crtc_h = crtc_h;
- intel_plane_copy_uapi_to_hw_state(new_plane_state, new_plane_state, crtc);
+ intel_plane_copy_uapi_to_hw_state(NULL, new_plane_state, new_plane_state, crtc);
ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state,
old_plane_state, new_plane_state);
diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c
index 24a51ab21b55..452062417ce9 100644
--- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c
@@ -9,6 +9,7 @@
#include <drm/drm_print.h>
#include "intel_alpm.h"
+#include "intel_cmtg.h"
#include "intel_cx0_phy.h"
#include "intel_cx0_phy_regs.h"
#include "intel_display_regs.h"
@@ -3418,10 +3419,20 @@ void intel_mtl_pll_enable(struct intel_encoder *encoder,
void intel_mtl_pll_enable_clock(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state)
{
+ struct intel_display *display = to_intel_display(encoder);
struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
if (intel_tc_port_in_tbt_alt_mode(dig_port))
intel_mtl_tbt_pll_enable_clock(encoder, crtc_state->port_clock);
+
+ /*
+ * CMTG can be enabled only when the transcoder and port are compatible
+ * (transcoder A with port A, transcoder B with port B).
+ */
+ if (HAS_LT_PHY(display) &&
+ ((crtc_state->cpu_transcoder == TRANSCODER_A && encoder->port == PORT_A) ||
+ (crtc_state->cpu_transcoder == TRANSCODER_B && encoder->port == PORT_B)))
+ intel_cmtg_set_clk_select(crtc_state);
}
/*
diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h
index 1428e7a5a318..95c20eb4b4b7 100644
--- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h
+++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h
@@ -60,7 +60,6 @@ void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
u8 lane_mask, u8 state);
-int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock);
void intel_cx0_setup_powerdown(struct intel_encoder *encoder);
bool intel_cx0_is_hdmi_frl(u32 clock);
u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr);
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index 6296635c4e79..2b7eb010511b 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -3501,6 +3501,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state,
}
intel_ddi_buf_enable(encoder, buf_ctl);
+
+ intel_hdmi_poll_for_scrambling_enable(crtc_state, connector);
}
static void intel_ddi_enable(struct intel_atomic_state *state,
@@ -4483,7 +4485,8 @@ intel_ddi_compute_output_type(struct intel_encoder *encoder,
}
}
-static int intel_ddi_compute_config(struct intel_encoder *encoder,
+static int intel_ddi_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -4501,7 +4504,7 @@ static int intel_ddi_compute_config(struct intel_encoder *encoder,
ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state);
} else {
- ret = intel_dp_compute_config(encoder, pipe_config, conn_state);
+ ret = intel_dp_compute_config(state, encoder, pipe_config, conn_state);
}
if (ret)
@@ -4606,7 +4609,8 @@ intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state,
return transcoders;
}
-static int intel_ddi_compute_config_late(struct intel_encoder *encoder,
+static int intel_ddi_compute_config_late(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -4659,6 +4663,7 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
drm_encoder_cleanup(encoder);
kfree(dig_port->hdcp.port_data.streams);
+ intel_dp_link_cleanup(&dig_port->dp);
kfree(dig_port);
}
@@ -4696,11 +4701,16 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
struct intel_display *display = to_intel_display(dig_port);
struct intel_connector *connector;
enum port port = dig_port->base.port;
+ int err;
connector = intel_connector_alloc();
if (!connector)
return -ENOMEM;
+ err = intel_dp_link_init(&dig_port->dp);
+ if (err)
+ goto err_dp_init;
+
dig_port->dp.output_reg = DDI_BUF_CTL(port);
if (DISPLAY_VER(display) >= 14)
dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain;
@@ -4713,8 +4723,9 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
dig_port->dp.preemph_max = intel_ddi_dp_preemph_max;
if (!intel_dp_init_connector(dig_port, connector)) {
- kfree(connector);
- return -EINVAL;
+ err = -EINVAL;
+
+ goto err_init_connector;
}
if (dig_port->base.type == INTEL_OUTPUT_EDP) {
@@ -4730,6 +4741,13 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
}
return 0;
+
+err_init_connector:
+ intel_dp_link_cleanup(&dig_port->dp);
+err_dp_init:
+ kfree(connector);
+
+ return err;
}
static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port)
@@ -4738,6 +4756,7 @@ static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port)
struct intel_connector *connector = intel_dp->attached_connector;
intel_dp_cleanup_connector(dig_port, connector);
+ intel_dp_link_cleanup(intel_dp);
kfree(connector);
}
diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c
index 6daee9e82503..a7417905192d 100644
--- a/drivers/gpu/drm/i915/display/intel_de.c
+++ b/drivers/gpu/drm/i915/display/intel_de.c
@@ -9,11 +9,11 @@
#include "intel_de.h"
-static int __intel_de_wait_for_register(struct intel_display *display,
- intel_reg_t reg, u32 mask, u32 value,
- unsigned int timeout_us,
- u32 (*read)(struct intel_display *display, intel_reg_t reg),
- u32 *out_val, bool is_atomic)
+static int intel_de_wait_for_register(struct intel_display *display,
+ intel_reg_t reg, u32 mask, u32 value,
+ unsigned int timeout_us,
+ u32 (*read)(struct intel_display *display, intel_reg_t reg),
+ u32 *out_val, bool is_atomic)
{
const ktime_t end = ktime_add_us(ktime_get_raw(), timeout_us);
int wait_max = 1000;
@@ -60,28 +60,6 @@ static int __intel_de_wait_for_register(struct intel_display *display,
return ret;
}
-static int intel_de_wait_for_register(struct intel_display *display,
- intel_reg_t reg, u32 mask, u32 value,
- unsigned int fast_timeout_us,
- unsigned int slow_timeout_us,
- u32 (*read)(struct intel_display *display, intel_reg_t reg),
- u32 *out_value, bool is_atomic)
-{
- int ret = -EINVAL;
-
- if (fast_timeout_us)
- ret = __intel_de_wait_for_register(display, reg, mask, value,
- fast_timeout_us, read,
- out_value, is_atomic);
-
- if (ret && slow_timeout_us)
- ret = __intel_de_wait_for_register(display, reg, mask, value,
- slow_timeout_us, read,
- out_value, is_atomic);
-
- return ret;
-}
-
int intel_de_wait_us(struct intel_display *display, intel_reg_t reg,
u32 mask, u32 value, unsigned int timeout_us,
u32 *out_value)
@@ -91,7 +69,7 @@ int intel_de_wait_us(struct intel_display *display, intel_reg_t reg,
intel_dmc_wl_get(display, reg);
ret = intel_de_wait_for_register(display, reg, mask, value,
- timeout_us, 0,
+ timeout_us,
intel_de_read,
out_value, false);
@@ -109,7 +87,7 @@ int intel_de_wait_ms(struct intel_display *display, intel_reg_t reg,
intel_dmc_wl_get(display, reg);
ret = intel_de_wait_for_register(display, reg, mask, value,
- 2, timeout_ms * 1000,
+ timeout_ms * 1000,
intel_de_read,
out_value, false);
@@ -123,7 +101,7 @@ int intel_de_wait_fw_ms(struct intel_display *display, intel_reg_t reg,
u32 *out_value)
{
return intel_de_wait_for_register(display, reg, mask, value,
- 2, timeout_ms * 1000,
+ timeout_ms * 1000,
intel_de_read_fw,
out_value, false);
}
@@ -133,7 +111,7 @@ int intel_de_wait_fw_us_atomic(struct intel_display *display, intel_reg_t reg,
u32 *out_value)
{
return intel_de_wait_for_register(display, reg, mask, value,
- timeout_us, 0,
+ timeout_us,
intel_de_read_fw,
out_value, true);
}
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 8e269b71f18e..90c05ad08f86 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -60,6 +60,7 @@
#include "intel_bw.h"
#include "intel_cdclk.h"
#include "intel_clock_gating.h"
+#include "intel_cmtg.h"
#include "intel_color.h"
#include "intel_crt.h"
#include "intel_crtc.h"
@@ -132,7 +133,6 @@
#include "vlv_dsi_pll.h"
#include "vlv_dsi_regs.h"
-static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state);
static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state);
static void hsw_set_transconf(const struct intel_crtc_state *crtc_state);
static void bdw_set_pipe_misc(struct intel_dsb *dsb,
@@ -1504,7 +1504,7 @@ static void ilk_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta
&crtc_state->dp_m2_n2);
}
- intel_set_transcoder_timings(crtc_state);
+ intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder);
ilk_set_pipeconf(crtc_state);
}
@@ -1635,7 +1635,9 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta
&crtc_state->dp_m2_n2);
}
- intel_set_transcoder_timings(crtc_state);
+ intel_cmtg_set_m_n(crtc_state);
+ intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder);
+ intel_cmtg_set_timings(crtc_state, MODESET);
if (cpu_transcoder != TRANSCODER_EDP)
intel_de_write(display, TRANS_MULT(display, cpu_transcoder),
@@ -1788,6 +1790,10 @@ static void hsw_crtc_disable(struct intel_atomic_state *state,
intel_atomic_get_old_crtc_state(state, crtc);
struct intel_crtc *pipe_crtc;
+ if (crtc->cmtg.enabled && intel_cmtg_is_allowed(old_crtc_state)) {
+ intel_cmtg_set_clk_select(old_crtc_state);
+ intel_cmtg_disable(old_crtc_state);
+ }
/*
* FIXME collapse everything to one hook.
* Need care with mst->ddi interactions.
@@ -2048,7 +2054,7 @@ static void i9xx_configure_cpu_transcoder(const struct intel_crtc_state *crtc_st
&crtc_state->dp_m2_n2);
}
- intel_set_transcoder_timings(crtc_state);
+ intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder);
i9xx_set_pipeconf(crtc_state);
}
@@ -2664,17 +2670,17 @@ transcoder_has_vrr(const struct intel_crtc_state *crtc_state)
return HAS_VRR(display) && !transcoder_is_dsi(cpu_transcoder);
}
-static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
+void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state,
+ enum transcoder transcoder)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
enum pipe pipe = crtc->pipe;
- enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end;
int vsyncshift = 0;
- drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder));
+ drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder));
/* We need to be careful not to changed the adjusted mode, for otherwise
* the hw state checker will get angry at the mismatch. */
@@ -2703,7 +2709,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
*/
if (DISPLAY_VER(display) >= 13) {
intel_de_write(display,
- TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
+ TRANS_SET_CONTEXT_LATENCY(display, transcoder),
crtc_state->set_context_latency);
/*
@@ -2718,35 +2724,26 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
if (DISPLAY_VER(display) >= 4 && DISPLAY_VER(display) < 35)
intel_de_write(display,
- TRANS_VSYNCSHIFT(display, cpu_transcoder),
+ TRANS_VSYNCSHIFT(display, transcoder),
vsyncshift);
- intel_de_write(display, TRANS_HTOTAL(display, cpu_transcoder),
+ intel_de_write(display, TRANS_HTOTAL(display, transcoder),
HACTIVE(adjusted_mode->crtc_hdisplay - 1) |
HTOTAL(adjusted_mode->crtc_htotal - 1));
- intel_de_write(display, TRANS_HBLANK(display, cpu_transcoder),
+ intel_de_write(display, TRANS_HBLANK(display, transcoder),
HBLANK_START(adjusted_mode->crtc_hblank_start - 1) |
HBLANK_END(adjusted_mode->crtc_hblank_end - 1));
- intel_de_write(display, TRANS_HSYNC(display, cpu_transcoder),
+ intel_de_write(display, TRANS_HSYNC(display, transcoder),
HSYNC_START(adjusted_mode->crtc_hsync_start - 1) |
HSYNC_END(adjusted_mode->crtc_hsync_end - 1));
- /*
- * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal
- * bits are not required. Since the support for these bits is going to
- * be deprecated in upcoming platforms, avoid writing these bits for the
- * platforms that do not use legacy Timing Generator.
- */
- if (intel_vrr_always_use_vrr_tg(display))
- crtc_vtotal = 1;
-
- intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VTOTAL(display, transcoder),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
- intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VBLANK(display, transcoder),
VBLANK_START(crtc_vblank_start - 1) |
VBLANK_END(crtc_vblank_end - 1));
- intel_de_write(display, TRANS_VSYNC(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VSYNC(display, transcoder),
VSYNC_START(adjusted_mode->crtc_vsync_start - 1) |
VSYNC_END(adjusted_mode->crtc_vsync_end - 1));
@@ -2754,13 +2751,15 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
* programmed with the VTOTAL_EDP value. Same for VTOTAL_C. This is
* documented on the DDI_FUNC_CTL register description, EDP Input Select
* bits. */
- if (display->platform.haswell && cpu_transcoder == TRANSCODER_EDP &&
+ if (display->platform.haswell && transcoder == TRANSCODER_EDP &&
(pipe == PIPE_B || pipe == PIPE_C))
intel_de_write(display, TRANS_VTOTAL(display, pipe),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
- if (DISPLAY_VER(display) >= 30) {
+ if (DISPLAY_VER(display) >= 30 &&
+ transcoder != TRANSCODER_CMTG0 &&
+ transcoder != TRANSCODER_CMTG1) {
/*
* Address issues for resolutions with high refresh rate that
* have small Hblank, specifically where Hblank is smaller than
@@ -2769,19 +2768,19 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
* followed by BE which DPRX devices are unable to handle.
* https://groups.vesa.org/wg/DP/document/20494
*/
- intel_de_write(display, DP_MIN_HBLANK_CTL(cpu_transcoder),
+ intel_de_write(display, DP_MIN_HBLANK_CTL(transcoder),
crtc_state->min_hblank);
}
}
-static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state)
+void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state,
+ enum transcoder transcoder)
{
struct intel_display *display = to_intel_display(crtc_state);
- enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end;
- drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder));
+ drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder));
crtc_vdisplay = adjusted_mode->crtc_vdisplay;
crtc_vtotal = adjusted_mode->crtc_vtotal;
@@ -2796,7 +2795,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc
if (DISPLAY_VER(display) >= 13) {
intel_de_write(display,
- TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
+ TRANS_SET_CONTEXT_LATENCY(display, transcoder),
crtc_state->set_context_latency);
/*
@@ -2813,28 +2812,27 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc
* The hardware actually ignores TRANS_VBLANK.VBLANK_END in DP mode.
* But let's write it anyway to keep the state checker happy.
*/
- intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VBLANK(display, transcoder),
VBLANK_START(crtc_vblank_start - 1) |
VBLANK_END(crtc_vblank_end - 1));
+
/*
- * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal
- * bits are not required. Since the support for these bits is going to
- * be deprecated in upcoming platforms, avoid writing these bits for the
- * platforms that do not use legacy Timing Generator.
+ * DP doesn't have vertical sync, so TRANS_VSYNC only affects
+ * the position of the vsync interrupt (and does so even when
+ * using the VRR timing generator!). Thus updating TRANS_VSYNC
+ * here seems fine even if it isn't double buffered.
*/
- if (intel_vrr_always_use_vrr_tg(display))
- crtc_vtotal = 1;
+ intel_de_write(display, TRANS_VSYNC(display, transcoder),
+ VSYNC_START(adjusted_mode->crtc_vsync_start - 1) |
+ VSYNC_END(adjusted_mode->crtc_vsync_end - 1));
/*
* The double buffer latch point for TRANS_VTOTAL
* is the transcoder's undelayed vblank.
*/
- intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VTOTAL(display, transcoder),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
-
- intel_vrr_set_fixed_rr_timings(crtc_state);
- intel_vrr_transcoder_enable(crtc_state);
}
static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state)
@@ -4783,7 +4781,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
if (connector_state->crtc != &crtc->base)
continue;
- ret = encoder->compute_config(encoder, crtc_state,
+ ret = encoder->compute_config(state, encoder, crtc_state,
connector_state);
if (ret == -EDEADLK)
return ret;
@@ -4843,7 +4841,7 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state,
!encoder->compute_config_late)
continue;
- ret = encoder->compute_config_late(encoder, crtc_state,
+ ret = encoder->compute_config_late(state, encoder, crtc_state,
conn_state);
if (ret)
return ret;
@@ -5189,9 +5187,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_I(name.crtc_vdisplay); \
if (!fastset || !allow_vblank_delay_fastset(current_config)) \
PIPE_CONF_CHECK_I(name.crtc_vblank_start); \
- PIPE_CONF_CHECK_I(name.crtc_vsync_start); \
- PIPE_CONF_CHECK_I(name.crtc_vsync_end); \
if (!fastset || !pipe_config->update_lrr) { \
+ PIPE_CONF_CHECK_I(name.crtc_vsync_start); \
+ PIPE_CONF_CHECK_I(name.crtc_vsync_end); \
PIPE_CONF_CHECK_I(name.crtc_vtotal); \
PIPE_CONF_CHECK_I(name.crtc_vblank_end); \
} \
@@ -5658,35 +5656,43 @@ int intel_modeset_all_pipes_late(struct intel_atomic_state *state,
return 0;
}
+int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state,
+ u8 pipe_mask,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_crtc *crtc;
+
+ state->base.acquire_ctx = ctx;
+ state->internal = true;
+
+ for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) {
+ struct intel_crtc_state *crtc_state =
+ intel_atomic_get_crtc_state(&state->base, crtc);
+
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ crtc_state->uapi.connectors_changed = true;
+ }
+
+ return drm_atomic_commit(&state->base);
+}
+
int intel_modeset_commit_pipes(struct intel_display *display,
u8 pipe_mask,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_atomic_commit *state;
- struct intel_crtc *crtc;
int ret;
state = drm_atomic_commit_alloc(display->drm);
if (!state)
return -ENOMEM;
- state->acquire_ctx = ctx;
- to_intel_atomic_state(state)->internal = true;
-
- for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) {
- struct intel_crtc_state *crtc_state =
- intel_atomic_get_crtc_state(state, crtc);
-
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- goto out;
- }
+ ret = intel_modeset_commit_pipes_for_atomic_state(to_intel_atomic_state(state),
+ pipe_mask, ctx);
- crtc_state->uapi.connectors_changed = true;
- }
-
- ret = drm_atomic_commit(state);
-out:
drm_atomic_commit_put(state);
return ret;
@@ -5805,6 +5811,8 @@ static bool lrr_params_changed(const struct intel_crtc_state *old_crtc_state,
return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start ||
old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end ||
+ old_adjusted_mode->crtc_vsync_start != new_adjusted_mode->crtc_vsync_start ||
+ old_adjusted_mode->crtc_vsync_end != new_adjusted_mode->crtc_vsync_end ||
old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal ||
old_crtc_state->set_context_latency != new_crtc_state->set_context_latency;
}
@@ -6667,12 +6675,19 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state,
display->platform.broadwell || display->platform.haswell)
hsw_set_linetime_wm(new_crtc_state);
- if (new_crtc_state->update_m_n)
+ if (new_crtc_state->update_m_n) {
intel_cpu_transcoder_set_m1_n1(crtc, new_crtc_state->cpu_transcoder,
&new_crtc_state->dp_m_n);
+ intel_cmtg_set_m_n(new_crtc_state);
+ }
- if (new_crtc_state->update_lrr)
- intel_set_transcoder_timings_lrr(new_crtc_state);
+ if (new_crtc_state->update_lrr) {
+ intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder);
+ intel_cmtg_set_timings(new_crtc_state, LRR);
+ intel_vrr_set_fixed_rr_timings(new_crtc_state, new_crtc_state->cpu_transcoder);
+ intel_cmtg_set_vrr_timings(new_crtc_state);
+ intel_vrr_transcoder_enable(new_crtc_state);
+ }
}
static void commit_pipe_pre_planes(struct intel_atomic_state *state,
@@ -6870,6 +6885,12 @@ static void intel_update_crtc(struct intel_atomic_state *state,
if (intel_crtc_needs_fastset(new_crtc_state) &&
old_crtc_state->inherited)
intel_crtc_arm_fifo_underrun(crtc, new_crtc_state);
+
+ if (crtc->cmtg.enabled && intel_crtc_vrr_enabling(state, crtc) &&
+ intel_cmtg_is_allowed(new_crtc_state)) {
+ intel_cmtg_set_clk_select(new_crtc_state);
+ intel_cmtg_disable(new_crtc_state);
+ }
}
static void intel_old_crtc_state_disables(struct intel_atomic_state *state,
@@ -7428,6 +7449,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
struct intel_crtc *crtc;
struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {};
struct ref_tracker *wakeref = NULL;
+ int power_async_delay;
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state)
intel_atomic_dsb_prepare(state, crtc);
@@ -7536,9 +7558,14 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
/* Now enable the clocks, plane, pipe, and connectors that we set up. */
display->modeset.funcs->commit_modeset_enables(state);
+ intel_display_power_dc3co_compute(state);
+
/* FIXME probably need to sequence this properly */
intel_program_dpkgc_latency(state);
+ if (intel_display_power_dc3co_allowed(display))
+ intel_cmtg_program(state);
+
intel_wait_for_vblank_workers(state);
/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
@@ -7632,11 +7659,12 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
*/
intel_uncore_arm_unclaimed_mmio_detection(uncore);
}
- /*
- * Delay re-enabling DC states by 17 ms to avoid the off->on->off
- * toggling overhead at and above 60 FPS.
- */
- intel_display_power_put_async_delay(display, POWER_DOMAIN_DC_OFF, wakeref, 17);
+
+ power_async_delay = intel_display_power_select_target_dc_state(state);
+
+ intel_display_power_put_async_delay(display,
+ POWER_DOMAIN_DC_OFF, wakeref, power_async_delay);
+
intel_display_rpm_put(display, state->wakeref);
/*
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 1963dbc80221..57ea4f2edf2a 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -424,6 +424,10 @@ void intel_set_m_n(struct intel_display *display,
const struct intel_link_m_n *m_n,
intel_reg_t data_m_reg, intel_reg_t data_n_reg,
intel_reg_t link_m_reg, intel_reg_t link_n_reg);
+void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state,
+ enum transcoder transcoder);
+void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state,
+ enum transcoder transcoder);
void intel_get_m_n(struct intel_display *display,
struct intel_link_m_n *m_n,
intel_reg_t data_m_reg, intel_reg_t data_n_reg,
@@ -474,6 +478,9 @@ int intel_modeset_pipes_in_mask_early(struct intel_atomic_state *state,
const char *reason, u8 pipe_mask);
int intel_modeset_all_pipes_late(struct intel_atomic_state *state,
const char *reason);
+int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state,
+ u8 pipe_mask,
+ struct drm_modeset_acquire_ctx *ctx);
int intel_modeset_commit_pipes(struct intel_display *display,
u8 pipe_mask,
struct drm_modeset_acquire_ctx *ctx);
diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h
index 09ce25a6d4b1..17f7d3abdb9c 100644
--- a/drivers/gpu/drm/i915/display/intel_display_core.h
+++ b/drivers/gpu/drm/i915/display/intel_display_core.h
@@ -325,14 +325,14 @@ struct intel_display {
struct intel_bw_info {
/* for each QGV point */
unsigned int deratedbw[I915_NUM_QGV_POINTS];
- /* for each PSF GV point */
- unsigned int psf_bw[I915_NUM_PSF_GV_POINTS];
- /* Peak BW for each QGV point */
- unsigned int peakbw[I915_NUM_QGV_POINTS];
- u8 num_qgv_points;
- u8 num_psf_gv_points;
u8 num_planes;
} max[6];
+ /* for each PSF GV point */
+ unsigned int psf_bw[I915_NUM_PSF_GV_POINTS];
+ /* Peak BW for each QGV point */
+ unsigned int peakbw[I915_NUM_QGV_POINTS];
+ u8 num_qgv_points;
+ u8 num_psf_gv_points;
} bw;
struct {
@@ -538,6 +538,8 @@ struct intel_display {
struct {
struct i915_power_domains domains;
+ /* DC3CO state */
+ struct intel_dc3co_state dc3co;
/* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */
u32 chv_phy_control;
diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
index 08004c1ba03f..3f02868ef105 100644
--- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c
+++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
@@ -32,6 +32,7 @@
#include "intel_display_types.h"
#include "intel_dmc.h"
#include "intel_dp.h"
+#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
@@ -1342,6 +1343,7 @@ void intel_connector_debugfs_add(struct intel_connector *connector)
intel_psr_connector_debugfs_add(connector);
intel_alpm_lobf_debugfs_add(connector);
intel_dp_link_training_debugfs_add(connector);
+ intel_dp_link_caps_debugfs_add(connector);
intel_link_bw_connector_debugfs_add(connector);
if (DISPLAY_VER(display) >= 11 &&
diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c
index 69a9f782935c..f17fc2c68472 100644
--- a/drivers/gpu/drm/i915/display/intel_display_device.c
+++ b/drivers/gpu/drm/i915/display/intel_display_device.c
@@ -101,6 +101,8 @@ static const struct intel_display_device_info no_display = {};
#define TRANSCODER_EDP_OFFSET 0x6f000
#define TRANSCODER_DSI0_OFFSET 0x6b000
#define TRANSCODER_DSI1_OFFSET 0x6b800
+#define TRANSCODER_CMTG0_OFFSET 0x6F000
+#define TRANSCODER_CMTG1_OFFSET 0x6F100
#define CURSOR_A_OFFSET 0x70080
#define CURSOR_B_OFFSET 0x700c0
@@ -1352,6 +1354,18 @@ static const struct intel_display_device_info xe2_lpd_display = {
BIT(INTEL_FBC_A) | BIT(INTEL_FBC_B) |
BIT(INTEL_FBC_C) | BIT(INTEL_FBC_D),
.__runtime_defaults.has_dbuf_overlap_detection = true,
+ .trans_offsets = {
+ [TRANSCODER_A] = TRANSCODER_A_OFFSET,
+ [TRANSCODER_B] = TRANSCODER_B_OFFSET,
+ [TRANSCODER_C] = TRANSCODER_C_OFFSET,
+ [TRANSCODER_D] = TRANSCODER_D_OFFSET,
+ [TRANSCODER_CMTG0] = TRANSCODER_CMTG0_OFFSET,
+ [TRANSCODER_CMTG1] = TRANSCODER_CMTG1_OFFSET,
+ },
+ .__runtime_defaults.cpu_transcoder_mask =
+ BIT(TRANSCODER_A) | BIT(TRANSCODER_B) |
+ BIT(TRANSCODER_C) | BIT(TRANSCODER_D) |
+ BIT(TRANSCODER_CMTG0) | BIT(TRANSCODER_CMTG1),
};
static const struct intel_display_device_info wcl_display = {
diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h
index 12e5a522a299..f77b3da2cff5 100644
--- a/drivers/gpu/drm/i915/display/intel_display_device.h
+++ b/drivers/gpu/drm/i915/display/intel_display_device.h
@@ -159,6 +159,7 @@ struct intel_display_platforms {
#define HAS_CUR_FBC(__display) (!HAS_GMCH(__display) && IS_DISPLAY_VER(__display, 7, 13))
#define HAS_D12_PLANE_MINIMIZATION(__display) ((__display)->platform.rocketlake || (__display)->platform.alderlake_s)
#define HAS_DBUF_OVERLAP_DETECTION(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dbuf_overlap_detection)
+#define HAS_DC3CO(__display) (DISPLAY_VER(__display) >= 35)
#define HAS_DDI(__display) (DISPLAY_INFO(__display)->has_ddi)
#define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0)
#define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc)
@@ -292,7 +293,7 @@ struct intel_display_runtime_info {
u32 rawclk_freq;
u8 pipe_mask;
- u8 cpu_transcoder_mask;
+ u16 cpu_transcoder_mask;
u16 port_mask;
u8 num_sprites[I915_MAX_PIPES];
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
index d0729936f681..bb5301b90231 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
@@ -43,6 +43,8 @@
#include "intel_dp_tunnel.h"
#include "intel_dpll.h"
#include "intel_dpll_mgr.h"
+#include "intel_dram.h"
+#include "intel_encoder.h"
#include "intel_fb.h"
#include "intel_fbc.h"
#include "intel_fbdev.h"
@@ -65,6 +67,8 @@
#include "intel_wm.h"
#include "skl_watermark.h"
+static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown);
+
bool intel_display_driver_probe_defer(struct pci_dev *pdev)
{
struct drm_privacy_screen *privacy_screen;
@@ -200,11 +204,23 @@ int intel_display_driver_probe_noirq(struct intel_display *display)
{
int ret;
+ intel_opregion_setup(display);
+
+ /*
+ * Fill the dram structure to get the system dram info. This will be
+ * used for memory latency calculation.
+ */
+ ret = intel_dram_detect(display);
+ if (ret)
+ goto cleanup_opregion;
+
+ intel_bw_init_hw(display);
+
if (HAS_DISPLAY(display)) {
ret = drm_vblank_init(display->drm,
INTEL_NUM_PIPES(display));
if (ret)
- return ret;
+ goto cleanup_opregion;
}
intel_bios_init(display);
@@ -303,6 +319,8 @@ cleanup_pw_domain_dmc:
intel_display_power_driver_remove(display);
cleanup_bios:
intel_bios_driver_remove(display);
+cleanup_opregion:
+ intel_opregion_cleanup(display);
return ret;
}
@@ -604,6 +622,8 @@ void intel_display_driver_remove_noirq(struct intel_display *display)
if (!HAS_DISPLAY(display))
return;
+ intel_hpd_cancel_work(display);
+
intel_display_driver_suspend_access(display);
/*
@@ -644,6 +664,8 @@ void intel_display_driver_remove_nogem(struct intel_display *display)
intel_display_power_driver_remove(display);
intel_bios_driver_remove(display);
+
+ intel_opregion_cleanup(display);
}
void intel_display_driver_unregister(struct intel_display *display)
@@ -674,34 +696,102 @@ void intel_display_driver_unregister(struct intel_display *display)
intel_vga_unregister(display);
}
+void intel_display_driver_shutdown(struct intel_display *display)
+{
+ if (!HAS_DISPLAY(display))
+ return;
+
+ __intel_display_driver_pm_suspend(display, true);
+
+ intel_encoder_shutdown_all(display);
+}
+
+void intel_display_driver_shutdown_late(struct intel_display *display)
+{
+ if (!HAS_DISPLAY(display))
+ return;
+
+ /*
+ * The only requirement is to reboot with display DC states disabled,
+ * for now leaving all display power wells in the INIT power domain
+ * enabled.
+ */
+ intel_display_power_driver_remove(display);
+}
+
/*
* turn all crtc's off, but do not adjust state
* This has to be paired with a call to intel_modeset_setup_hw_state.
*/
-int intel_display_driver_suspend(struct intel_display *display)
+static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown)
{
- struct drm_atomic_commit *state;
- int ret;
+ int ret = 0;
if (!HAS_DISPLAY(display))
return 0;
- state = drm_atomic_helper_suspend(display->drm);
- ret = PTR_ERR_OR_ZERO(state);
- if (ret)
- drm_err(display->drm, "Suspending crtc's failed with %i\n",
- ret);
- else
- display->restore.modeset_state = state;
+ /*
+ * We do a lot of poking in a lot of registers, make sure they work
+ * properly.
+ */
+ intel_display_power_disable(display);
+
+ drm_client_dev_suspend(display->drm);
+
+ drm_kms_helper_poll_disable(display->drm);
+ intel_display_driver_disable_user_access(display);
+
+ if (shutdown) {
+ drm_atomic_helper_shutdown(display->drm);
+ } else {
+ struct drm_atomic_commit *state;
+
+ state = drm_atomic_helper_suspend(display->drm);
+ ret = PTR_ERR_OR_ZERO(state);
+ if (ret)
+ drm_err(display->drm, "Suspending crtc's failed with %i\n",
+ ret);
+ else
+ display->restore.modeset_state = state;
+ }
/* ensure all DPT VMAs have been unpinned for intel_dpt_suspend() */
flush_workqueue(display->wq.cleanup);
intel_dp_mst_suspend(display);
+ intel_encoder_block_all_hpds(display);
+
+ intel_hpd_cancel_work(display);
+
+ intel_display_driver_suspend_access(display);
+
+ intel_encoder_suspend_all(display);
+
return ret;
}
+int intel_display_driver_pm_suspend(struct intel_display *display)
+{
+ return __intel_display_driver_pm_suspend(display, false);
+}
+
+void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle)
+{
+ if (!HAS_DISPLAY(display))
+ return;
+
+ intel_display_power_suspend_late(display, s2idle);
+}
+
+void intel_display_driver_pm_resume_early(struct intel_display *display)
+{
+ if (!HAS_DISPLAY(display))
+ return;
+
+ intel_display_power_resume_early(display);
+}
+
int
__intel_display_driver_resume(struct intel_display *display,
struct drm_atomic_commit *state,
@@ -741,7 +831,7 @@ __intel_display_driver_resume(struct intel_display *display,
return ret;
}
-void intel_display_driver_resume(struct intel_display *display)
+void intel_display_driver_pm_resume(struct intel_display *display)
{
struct drm_atomic_commit *state = display->restore.modeset_state;
struct drm_modeset_acquire_ctx ctx;
@@ -750,6 +840,12 @@ void intel_display_driver_resume(struct intel_display *display)
if (!HAS_DISPLAY(display))
return;
+ intel_display_driver_resume_access(display);
+
+ intel_hpd_init(display);
+
+ intel_encoder_unblock_all_hpds(display);
+
/* MST sideband requires HPD interrupts enabled */
intel_dp_mst_resume(display);
@@ -779,4 +875,15 @@ void intel_display_driver_resume(struct intel_display *display)
"Restoring old state failed with %i\n", ret);
if (state)
drm_atomic_commit_put(state);
+
+ intel_display_driver_enable_user_access(display);
+ drm_kms_helper_poll_enable(display->drm);
+
+ intel_hpd_poll_disable(display);
+
+ intel_opregion_resume(display);
+
+ drm_client_dev_resume(display->drm);
+
+ intel_display_power_enable(display);
}
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h
index 5270c26a32e0..7eca3d17dd82 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.h
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.h
@@ -24,8 +24,13 @@ void intel_display_driver_remove(struct intel_display *display);
void intel_display_driver_remove_noirq(struct intel_display *display);
void intel_display_driver_remove_nogem(struct intel_display *display);
void intel_display_driver_unregister(struct intel_display *display);
-int intel_display_driver_suspend(struct intel_display *display);
-void intel_display_driver_resume(struct intel_display *display);
+void intel_display_driver_shutdown(struct intel_display *display);
+void intel_display_driver_shutdown_late(struct intel_display *display);
+
+int intel_display_driver_pm_suspend(struct intel_display *display);
+void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle);
+void intel_display_driver_pm_resume_early(struct intel_display *display);
+void intel_display_driver_pm_resume(struct intel_display *display);
/* interface for intel_display_reset.c */
int __intel_display_driver_resume(struct intel_display *display,
diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c
index 4a821b0674fd..bcb0ee22fb56 100644
--- a/drivers/gpu/drm/i915/display/intel_display_irq.c
+++ b/drivers/gpu/drm/i915/display/intel_display_irq.c
@@ -1469,6 +1469,18 @@ static void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl)
found = true;
}
+ if (DISPLAY_VER(display) == 35) {
+ if (iir & CMTG_VBLANK_A) {
+ intel_handle_vblank(display, PIPE_A);
+ found = true;
+ }
+
+ if (iir & CMTG_VBLANK_B) {
+ intel_handle_vblank(display, PIPE_B);
+ found = true;
+ }
+ }
+
if (DISPLAY_VER(display) >= 11) {
u32 te_trigger = iir & (DSI0_TE | DSI1_TE);
@@ -2666,3 +2678,10 @@ void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *s
drm_printf(p, "DERRMR: 0x%08x\n", snapshot->derrmr);
drm_printf(p, "ERR_INT: 0x%08x\n", snapshot->err_int);
}
+
+void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask)
+{
+ spin_lock_irq(&display->irq.lock);
+ bdw_update_port_irq(display, bits, mask ? 0 : bits);
+ spin_unlock_irq(&display->irq.lock);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.h b/drivers/gpu/drm/i915/display/intel_display_irq.h
index a1227cee885a..84446bf53401 100644
--- a/drivers/gpu/drm/i915/display/intel_display_irq.h
+++ b/drivers/gpu/drm/i915/display/intel_display_irq.h
@@ -82,4 +82,6 @@ void i915gm_irq_cstate_wa(struct intel_display *display, bool enable);
struct intel_display_irq_snapshot *intel_display_irq_snapshot_capture(struct intel_display *display);
void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *snapshot, struct drm_printer *p);
+void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask);
+
#endif /* __INTEL_DISPLAY_IRQ_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_display_limits.h b/drivers/gpu/drm/i915/display/intel_display_limits.h
index 453f7b720815..ea89473c177f 100644
--- a/drivers/gpu/drm/i915/display/intel_display_limits.h
+++ b/drivers/gpu/drm/i915/display/intel_display_limits.h
@@ -45,6 +45,8 @@ enum transcoder {
TRANSCODER_DSI_1,
TRANSCODER_DSI_A = TRANSCODER_DSI_0, /* legacy DSI */
TRANSCODER_DSI_C = TRANSCODER_DSI_1, /* legacy DSI */
+ TRANSCODER_CMTG0,
+ TRANSCODER_CMTG1,
I915_MAX_TRANSCODERS
};
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
index 2e51dfcd5dce..8e312f5b9f6d 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -14,7 +14,9 @@
#include "intel_cdclk.h"
#include "intel_clock_gating.h"
#include "intel_combo_phy.h"
+#include "intel_crtc.h"
#include "intel_de.h"
+#include "intel_display.h"
#include "intel_display_power.h"
#include "intel_display_power_map.h"
#include "intel_display_power_well.h"
@@ -30,6 +32,8 @@
#include "intel_pch_refclk.h"
#include "intel_pmdemand.h"
#include "intel_pps_regs.h"
+#include "intel_psr.h"
+#include "intel_psr_regs.h"
#include "intel_snps_phy.h"
#include "skl_watermark.h"
#include "skl_watermark_regs.h"
@@ -267,7 +271,7 @@ sanitize_target_dc_state(struct intel_display *display,
static const u32 states[] = {
DC_STATE_EN_UPTO_DC6,
DC_STATE_EN_UPTO_DC5,
- DC_STATE_EN_DC3CO,
+ DC_STATE_EN_UPTO_DC3CO,
DC_STATE_DISABLE,
};
int i;
@@ -285,6 +289,19 @@ sanitize_target_dc_state(struct intel_display *display,
return target_dc_state;
}
+bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display)
+{
+ struct i915_power_domains *power_domains = &display->power.domains;
+ bool ret;
+
+ mutex_lock(&power_domains->lock);
+ ret = power_domains->dc3co_to_dc6;
+ power_domains->dc3co_to_dc6 = false;
+ mutex_unlock(&power_domains->lock);
+
+ return ret;
+}
+
/**
* intel_display_power_set_target_dc_state - Set target dc state.
* @display: display device
@@ -300,6 +317,7 @@ void intel_display_power_set_target_dc_state(struct intel_display *display,
struct i915_power_well *power_well;
bool dc_off_enabled;
struct i915_power_domains *power_domains = &display->power.domains;
+ u32 old_target_dc_state;
mutex_lock(&power_domains->lock);
power_well = lookup_power_well(display, SKL_DISP_DC_OFF);
@@ -320,8 +338,17 @@ void intel_display_power_set_target_dc_state(struct intel_display *display,
if (!dc_off_enabled)
intel_power_well_enable(display, power_well);
+ old_target_dc_state = power_domains->target_dc_state;
power_domains->target_dc_state = state;
+ /*
+ * CMTG must be restored explicitly after DC6 exit. The dc3co_to_dc6
+ * flag helps CMTG determine whether restoration is required.
+ */
+ if (old_target_dc_state == DC_STATE_EN_UPTO_DC3CO &&
+ power_domains->target_dc_state == DC_STATE_EN_UPTO_DC6)
+ power_domains->dc3co_to_dc6 = true;
+
if (!dc_off_enabled)
intel_power_well_disable(display, power_well);
@@ -358,6 +385,144 @@ unlock:
return current_dc_state;
}
+bool intel_display_power_dc3co_supported(struct intel_display *display)
+{
+ struct i915_power_domains *power_domains = &display->power.domains;
+
+ if (!HAS_DC3CO(display))
+ return false;
+
+ return (power_domains->allowed_dc_mask & DC_STATE_EN_UPTO_DC3CO) == DC_STATE_EN_UPTO_DC3CO;
+}
+
+bool intel_display_power_dc3co_allowed(struct intel_display *display)
+{
+ struct intel_dc3co_state *dc3co = &display->power.dc3co;
+ bool allowed;
+
+ if (!intel_display_power_dc3co_supported(display))
+ return false;
+
+ mutex_lock(&dc3co->lock);
+ allowed = dc3co->allowed;
+ mutex_unlock(&dc3co->lock);
+
+ return allowed;
+}
+
+void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger)
+{
+ struct intel_dc3co_state *dc3co = &display->power.dc3co;
+
+ if (!intel_display_power_dc3co_supported(display))
+ return;
+
+ mutex_lock(&dc3co->lock);
+ dc3co->trigger = trigger;
+ dc3co->allowed = !!trigger;
+ mutex_unlock(&dc3co->lock);
+}
+
+static bool intel_dc3co_port_pipe_compatible(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
+ enum port port = dig_port->base.port;
+ int num_pipes = intel_crtc_num_joined_pipes(crtc_state);
+
+ /* Need to follow 1:1 mapping because of CMTG restriction */
+ if (DISPLAY_VER(to_intel_display(crtc_state)) == 35)
+ return num_pipes == 1 &&
+ ((pipe == PIPE_A && port == PORT_A) ||
+ (pipe == PIPE_B && port == PORT_B));
+ else
+ return num_pipes == 1 && pipe <= PIPE_B && port <= PORT_B;
+}
+
+void intel_display_power_dc3co_compute(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_crtc *crtc;
+ struct intel_crtc_state *crtc_state;
+ struct intel_encoder *encoder;
+ struct intel_dp *intel_dp;
+ u8 active_pipes = 0;
+ enum pipe pipe;
+ u32 trigger = DC3CO_TRIGGER_NONE;
+
+ if (!intel_display_power_dc3co_supported(display))
+ return;
+
+ for_each_intel_crtc(display, crtc)
+ active_pipes |= crtc->active ? BIT(crtc->pipe) : 0;
+
+ active_pipes = intel_calc_active_pipes(state, active_pipes);
+
+ if (hweight8(active_pipes) != 1)
+ goto done;
+
+ pipe = ffs(active_pipes) - 1;
+ crtc = intel_crtc_for_pipe(display, pipe);
+
+ crtc_state = to_intel_crtc_state(crtc->base.state);
+
+ for_each_intel_encoder_mask(display->drm, encoder,
+ crtc_state->uapi.encoder_mask) {
+ if (encoder->type != INTEL_OUTPUT_EDP)
+ goto done;
+
+ intel_dp = enc_to_intel_dp(encoder);
+
+ if (!intel_dc3co_port_pipe_compatible(intel_dp, crtc_state))
+ goto done;
+
+ if (intel_psr2_in_deep_sleep(intel_dp))
+ goto done;
+ }
+
+ if (crtc_state->has_lobf)
+ trigger |= DC3CO_TRIGGER_LOBF;
+ if (crtc_state->has_panel_replay && intel_dp->as_sdp_supported)
+ trigger |= DC3CO_TRIGGER_PANEL_REPLAY;
+ if (crtc_state->has_sel_update)
+ trigger |= DC3CO_TRIGGER_PSR2;
+
+done:
+ intel_display_power_dc3co_update(display, trigger);
+}
+
+/*
+ * Select the target DC state for this commit and return the async-put delay
+ * to use when releasing the DC_OFF reference.
+ *
+ * Picks DC_STATE_EN_UPTO_DC3CO when DC3CO can be enabled
+ * otherwise falls back to default DC state of DC_STATE_EN_UPTO_DC6.
+ * The chosen target is programmed via intel_display_power_set_target_dc_state().
+ *
+ * Returns the async-put delay (in ms) to use when releasing the DC_OFF
+ * reference: DC3CO_PUT_ASYNC_DELAY_MS when DC3CO was selected, otherwise
+ * DC6_PUT_ASYNC_DELAY_MS.
+ */
+int intel_display_power_select_target_dc_state(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ u32 target_dc_state;
+
+ if (!intel_display_power_dc3co_supported(display))
+ return DC6_PUT_ASYNC_DELAY_MS;
+
+ if (intel_display_power_dc3co_allowed(display))
+ target_dc_state = DC_STATE_EN_UPTO_DC3CO;
+ else
+ target_dc_state = DC_STATE_EN_UPTO_DC6;
+
+ intel_display_power_set_target_dc_state(display, target_dc_state);
+
+ return target_dc_state == DC_STATE_EN_UPTO_DC3CO ?
+ DC3CO_PUT_ASYNC_DELAY_MS : DC6_PUT_ASYNC_DELAY_MS;
+}
+
static void __async_put_domains_mask(struct i915_power_domains *power_domains,
struct intel_power_domain_mask *mask)
{
@@ -956,7 +1121,9 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc)
if (!HAS_DISPLAY(display))
return 0;
- if (DISPLAY_VER(display) >= 20)
+ if (DISPLAY_VER(display) >= 35)
+ max_dc = 4;
+ else if (DISPLAY_VER(display) >= 20)
max_dc = 2;
else if (display->platform.dg2)
max_dc = 1;
@@ -999,10 +1166,10 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc)
switch (requested_dc) {
case 4:
- mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6;
+ mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6;
break;
case 3:
- mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC5;
+ mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC5;
break;
case 2:
mask |= DC_STATE_EN_UPTO_DC6;
@@ -1037,6 +1204,7 @@ int intel_display_power_init(struct intel_display *display)
sanitize_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
mutex_init(&power_domains->lock);
+ mutex_init(&display->power.dc3co.lock);
INIT_DELAYED_WORK(&power_domains->async_put_work,
intel_display_power_put_async_work);
@@ -1634,9 +1802,12 @@ static void tgl_bw_buddy_init(struct intel_display *display)
if (table[config].page_mask == 0) {
drm_dbg_kms(display->drm,
"Unknown memory configuration; disabling address buddy logic.\n");
- for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask))
- intel_de_write(display, BW_BUDDY_CTL(i),
- BW_BUDDY_DISABLE);
+
+ if (DISPLAY_VER(display) < 20) {
+ for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask))
+ intel_de_write(display, BW_BUDDY_CTL(i),
+ BW_BUDDY_DISABLE);
+ }
} else {
for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) {
intel_de_write(display, BW_BUDDY_PAGE_MASK(i),
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h
index 56dc89eed3f8..546af67b680b 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.h
+++ b/drivers/gpu/drm/i915/display/intel_display_power.h
@@ -9,9 +9,12 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include "intel_display_limits.h"
+
enum aux_ch;
enum port;
struct i915_power_well;
+struct intel_atomic_state;
struct intel_display;
struct intel_encoder;
struct ref_tracker;
@@ -131,6 +134,36 @@ struct intel_power_domain_mask {
DECLARE_BITMAP(bits, POWER_DOMAIN_NUM);
};
+/*
+ * DC3CO enabling triggers (bitmask).
+ * DC3CO may be enabled when at least one of these triggers is active.
+ * Additional constraints may still apply.
+ */
+#define DC3CO_TRIGGER_NONE (0)
+#define DC3CO_TRIGGER_PSR2 BIT(0)
+#define DC3CO_TRIGGER_LOBF BIT(1)
+#define DC3CO_TRIGGER_PANEL_REPLAY BIT(2)
+#define DC3CO_TRIGGER_ALL (DC3CO_TRIGGER_PSR2 | \
+ DC3CO_TRIGGER_LOBF | \
+ DC3CO_TRIGGER_PANEL_REPLAY)
+
+/*
+ * Delay to re-enable DC5/DC6 states by 17 ms to avoid the off->on->off
+ * toggling overhead at and above 60 FPS.
+ */
+#define DC6_PUT_ASYNC_DELAY_MS 17
+/*
+ * Use minimal re-enable delay to allow DC3CO entry on
+ * the next idle frame.
+ */
+#define DC3CO_PUT_ASYNC_DELAY_MS 1
+
+struct intel_dc3co_state {
+ struct mutex lock; /* protects allowed and trigger fields */
+ bool allowed; /* DC3CO compute result */
+ u32 trigger; /* Bitmask of active DC3CO triggers */
+};
+
struct i915_power_domains {
/*
* Power wells needed for initialization at driver init and suspend
@@ -138,6 +171,7 @@ struct i915_power_domains {
*/
bool initializing;
bool display_core_suspended;
+ bool dc3co_to_dc6;
int power_well_count;
u32 dc_state;
@@ -179,9 +213,15 @@ void intel_display_power_sanitize_state(struct intel_display *display);
void intel_display_power_suspend_late(struct intel_display *display, bool s2idle);
void intel_display_power_resume_early(struct intel_display *display);
+bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display);
void intel_display_power_set_target_dc_state(struct intel_display *display,
u32 state);
u32 intel_display_power_get_current_dc_state(struct intel_display *display);
+bool intel_display_power_dc3co_supported(struct intel_display *display);
+void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger);
+bool intel_display_power_dc3co_allowed(struct intel_display *display);
+void intel_display_power_dc3co_compute(struct intel_atomic_state *state);
+int intel_display_power_select_target_dc_state(struct intel_atomic_state *state);
void intel_display_power_runtime_suspend(struct intel_display *display);
void intel_display_power_runtime_resume(struct intel_display *display);
diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c
index 04bd0dde5bed..02cb4d800e23 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power_well.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c
@@ -726,12 +726,28 @@ static void assert_can_disable_dc9(struct intel_display *display)
*/
}
+static u32 dc_state_ro_mask(struct intel_display *display)
+{
+ if (DISPLAY_VER(display) >= 20)
+ return DC_STATE_EN_CSR_MASK_CMTG_1 | DC_STATE_EN_CSR_MASK_CMTG_0;
+ else if (DISPLAY_VER(display) >= 13 && !display->platform.dg2)
+ return DC_STATE_EN_CSR_MASK_CMTG_0;
+
+ return 0;
+}
+
static void gen9_write_dc_state(struct intel_display *display,
u32 state)
{
int rewrites = 0;
int rereads = 0;
u32 v;
+ /*
+ * Mask out RO status bits from read-back comparison.
+ * HW may set these bits independently, so exclude them
+ * to prevent the verify loop from retrying due to RO bits mismatch.
+ */
+ u32 ro_mask = dc_state_ro_mask(display);
intel_de_write(display, DC_STATE_EN, state);
@@ -743,7 +759,7 @@ static void gen9_write_dc_state(struct intel_display *display,
do {
v = intel_de_read(display, DC_STATE_EN);
- if (v != state) {
+ if ((v & ~ro_mask) != (state & ~ro_mask)) {
intel_de_write(display, DC_STATE_EN, state);
rewrites++;
rereads = 0;
@@ -753,10 +769,10 @@ static void gen9_write_dc_state(struct intel_display *display,
} while (rewrites < 100);
- if (v != state)
+ if ((v & ~ro_mask) != (state & ~ro_mask))
drm_err(display->drm,
- "Writing dc state to 0x%x failed, now 0x%x\n",
- state, v);
+ "Writing dc state to 0x%x failed, now 0x%x (ro_mask=0x%x)\n",
+ state, v, ro_mask);
/* Most of the times we need one retry, avoid spam */
if (rewrites > 1)
@@ -772,7 +788,7 @@ static u32 gen9_dc_mask(struct intel_display *display)
mask = DC_STATE_EN_UPTO_DC5;
if (DISPLAY_VER(display) >= 12)
- mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6
+ mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6
| DC_STATE_EN_DC9;
else if (DISPLAY_VER(display) == 11)
mask |= DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9;
@@ -866,21 +882,30 @@ void gen9_set_dc_state(struct intel_display *display, u32 state)
power_domains->dc_state = val & mask;
}
-static void tgl_enable_dc3co(struct intel_display *display)
+void xe3lpd_enable_dc_count(struct intel_display *display)
{
- drm_dbg_kms(display->drm, "Enabling DC3CO\n");
- gen9_set_dc_state(display, DC_STATE_EN_DC3CO);
+ if (DISPLAY_VER(display) < 35)
+ return;
+
+ intel_de_write(display, DC_COUNT_EN, DC_COUNT_EN_COUNTER_ENABLE);
}
-static void tgl_disable_dc3co(struct intel_display *display)
+static void assert_can_enable_dc3co(struct intel_display *display)
{
- drm_dbg_kms(display->drm, "Disabling DC3CO\n");
- intel_de_rmw(display, DC_STATE_EN, DC_STATE_DC3CO_STATUS, 0);
- gen9_set_dc_state(display, DC_STATE_DISABLE);
- /*
- * Delay of 200us DC3CO Exit time B.Spec 49196
- */
- usleep_range(200, 210);
+ drm_WARN_ONCE(display->drm,
+ (intel_de_read(display, DC_STATE_EN) &
+ DC_STATE_EN_UPTO_DC3CO),
+ "DC3CO already programmed to be enabled.\n");
+
+ assert_main_dmc_loaded(display);
+}
+
+static void xe3lpd_enable_dc3co(struct intel_display *display)
+{
+ assert_can_enable_dc3co(display);
+ drm_dbg_kms(display->drm, "Enabling DC3CO\n");
+ intel_dmc_wl_enable(display, DC_STATE_EN_UPTO_DC3CO);
+ gen9_set_dc_state(display, DC_STATE_EN_UPTO_DC3CO);
}
static void assert_can_enable_dc5(struct intel_display *display)
@@ -1039,8 +1064,8 @@ static void bxt_verify_dpio_phy_power_wells(struct intel_display *display)
static bool gen9_dc_off_power_well_enabled(struct intel_display *display,
struct i915_power_well *power_well)
{
- return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_DC3CO) == 0 &&
- (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0);
+ return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO) == 0 &&
+ (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK) == 0);
}
static void gen9_assert_dbuf_enabled(struct intel_display *display)
@@ -1061,11 +1086,6 @@ void gen9_disable_dc_states(struct intel_display *display)
struct intel_cdclk_config cdclk_config = {};
u32 old_state = power_domains->dc_state;
- if (power_domains->target_dc_state == DC_STATE_EN_DC3CO) {
- tgl_disable_dc3co(display);
- return;
- }
-
if (HAS_DISPLAY(display)) {
intel_dmc_wl_get_noreg(display);
gen9_set_dc_state(display, DC_STATE_DISABLE);
@@ -1076,9 +1096,13 @@ void gen9_disable_dc_states(struct intel_display *display)
}
if (old_state == DC_STATE_EN_UPTO_DC5 ||
- old_state == DC_STATE_EN_UPTO_DC6)
+ old_state == DC_STATE_EN_UPTO_DC6 ||
+ old_state == DC_STATE_EN_UPTO_DC3CO)
intel_dmc_wl_disable(display);
+ if (old_state == DC_STATE_EN_UPTO_DC3CO)
+ return;
+
intel_cdclk_get_cdclk(display, &cdclk_config);
/* Can't read out voltage_level so can't use intel_cdclk_changed() */
drm_WARN_ON(display->drm,
@@ -1114,8 +1138,8 @@ static void gen9_dc_off_power_well_disable(struct intel_display *display,
return;
switch (power_domains->target_dc_state) {
- case DC_STATE_EN_DC3CO:
- tgl_enable_dc3co(display);
+ case DC_STATE_EN_UPTO_DC3CO:
+ xe3lpd_enable_dc3co(display);
break;
case DC_STATE_EN_UPTO_DC6:
skl_enable_dc6(display);
diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.h b/drivers/gpu/drm/i915/display/intel_display_power_well.h
index 8f5524da2d06..0ce64b894436 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power_well.h
+++ b/drivers/gpu/drm/i915/display/intel_display_power_well.h
@@ -159,6 +159,7 @@ void gen9_set_dc_state(struct intel_display *display, u32 state);
void gen9_disable_dc_states(struct intel_display *display);
void bxt_enable_dc9(struct intel_display *display);
void bxt_disable_dc9(struct intel_display *display);
+void xe3lpd_enable_dc_count(struct intel_display *display);
extern const struct i915_power_well_ops i9xx_always_on_power_well_ops;
extern const struct i915_power_well_ops chv_pipe_power_well_ops;
diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h
index 4321f8b529da..39e50423132f 100644
--- a/drivers/gpu/drm/i915/display/intel_display_regs.h
+++ b/drivers/gpu/drm/i915/display/intel_display_regs.h
@@ -1458,6 +1458,8 @@
#define GEN9_AUX_CHANNEL_B (1 << 25)
#define DSI1_TE (1 << 24)
#define DSI0_TE (1 << 23)
+#define CMTG_VBLANK_B (1 << 17)
+#define CMTG_VBLANK_A (1 << 14)
#define GEN8_DE_PORT_HOTPLUG(hpd_pin) REG_BIT(3 + _HPD_PIN_DDI(hpd_pin))
#define BXT_DE_PORT_HOTPLUG_MASK (GEN8_DE_PORT_HOTPLUG(HPD_PORT_A) | \
GEN8_DE_PORT_HOTPLUG(HPD_PORT_B) | \
@@ -2767,28 +2769,28 @@ enum skl_power_gate {
*/
/* CDCLK_CTL */
#define CDCLK_CTL _MMIO(0x46000)
-#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26)
+#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) /* skl */
#define CDCLK_FREQ_450_432 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 0)
-#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1)
+#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1)
#define CDCLK_FREQ_337_308 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 2)
#define CDCLK_FREQ_675_617 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 3)
-#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25)
+#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) /* lnl+ */
#define MDCLK_SOURCE_SEL_CD2XCLK REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 0)
#define MDCLK_SOURCE_SEL_CDCLK_PLL REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 1)
-#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22)
+#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) /* bxt+ */
#define BXT_CDCLK_CD2X_DIV_SEL_1 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 0)
#define BXT_CDCLK_CD2X_DIV_SEL_1_5 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 1)
#define BXT_CDCLK_CD2X_DIV_SEL_2 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 2)
#define BXT_CDCLK_CD2X_DIV_SEL_4 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 3)
-#define BXT_CDCLK_CD2X_PIPE(pipe) ((pipe) << 20)
-#define CDCLK_DIVMUX_CD_OVERRIDE (1 << 19)
-#define BXT_CDCLK_CD2X_PIPE_NONE BXT_CDCLK_CD2X_PIPE(3)
-#define ICL_CDCLK_CD2X_PIPE(pipe) (_PICK(pipe, 0, 2, 6) << 19)
-#define ICL_CDCLK_CD2X_PIPE_NONE (7 << 19)
-#define TGL_CDCLK_CD2X_PIPE(pipe) BXT_CDCLK_CD2X_PIPE(pipe)
-#define TGL_CDCLK_CD2X_PIPE_NONE ICL_CDCLK_CD2X_PIPE_NONE
-#define BXT_CDCLK_SSA_PRECHARGE_ENABLE (1 << 16)
-#define CDCLK_FREQ_DECIMAL_MASK (0x7ff)
+#define BXT_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 20) /* bxt/glk */
+#define BXT_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, (pipe))
+#define BXT_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, 3)
+#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl+ */
+#define ICL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1)
+#define ICL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, 7)
+#define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) /* pre-icl */
+#define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) /* bxt/glk */
+#define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) /* pre-lnl */
/* CDCLK_SQUASH_CTL */
#define CDCLK_SQUASH_CTL _MMIO(0x46008)
@@ -3070,19 +3072,29 @@ enum skl_power_gate {
/* GEN9 DC */
#define DC_STATE_EN _MMIO(0x45504)
#define DC_STATE_DISABLE 0
-#define DC_STATE_EN_DC3CO REG_BIT(30)
#define DC_STATE_DC3CO_STATUS REG_BIT(29)
#define HOLD_PHY_CLKREQ_PG1_LATCH REG_BIT(21)
#define HOLD_PHY_PG1_LATCH REG_BIT(20)
-#define DC_STATE_EN_UPTO_DC5 (1 << 0)
#define DC_STATE_EN_DC9 (1 << 3)
-#define DC_STATE_EN_UPTO_DC6 (2 << 0)
-#define DC_STATE_EN_UPTO_DC5_DC6_MASK 0x3
+#define DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK REG_GENMASK(1, 0)
+#define DC_STATE_EN_DISABLE REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 0)
+#define DC_STATE_EN_UPTO_DC5 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 1)
+#define DC_STATE_EN_UPTO_DC6 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 2)
+#define DC_STATE_EN_UPTO_DC3CO REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 3)
+/* display version 20+ */
+#define DC_STATE_EN_CSR_MASK_CMTG_1 REG_BIT(11)
+/* display version 13+, except dg2 */
+#define DC_STATE_EN_CSR_MASK_CMTG_0 REG_BIT(10)
#define DC_STATE_DEBUG _MMIO(0x45520)
#define DC_STATE_DEBUG_MASK_CORES (1 << 0)
#define DC_STATE_DEBUG_MASK_MEMORY_UP (1 << 1)
+#define DC_COUNT_EN _MMIO(0x457B4)
+#define DC_COUNT_EN_COUNTER_ENABLE REG_BIT(31)
+
+#define DC_STATE_DC3CO_RESIDENCY _MMIO(0x457B8)
+
#define D_COMP_BDW _MMIO(0x138144)
/* Pipe WM_LINETIME - watermark line time */
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index c21e0c0ef0b1..c048da7d6fea 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -58,6 +58,8 @@ struct cec_notifier;
struct drm_printer;
struct intel_connector;
struct intel_ddi_buf_trans;
+struct intel_dp_link_caps;
+struct intel_dp_link_training;
struct intel_fbc;
struct intel_global_objs_state;
struct intel_hdcp_shim;
@@ -150,7 +152,6 @@ struct intel_framebuffer {
unsigned int min_alignment;
unsigned int vtd_guard;
- unsigned int (*panic_tiling)(unsigned int x, unsigned int y, unsigned int width);
struct intel_panic *panic;
};
@@ -177,10 +178,12 @@ struct intel_encoder {
enum intel_output_type (*compute_output_type)(struct intel_encoder *,
struct intel_crtc_state *,
struct drm_connector_state *);
- int (*compute_config)(struct intel_encoder *,
+ int (*compute_config)(struct intel_atomic_state *,
+ struct intel_encoder *,
struct intel_crtc_state *,
struct drm_connector_state *);
- int (*compute_config_late)(struct intel_encoder *,
+ int (*compute_config_late)(struct intel_atomic_state *,
+ struct intel_encoder *,
struct intel_crtc_state *,
struct drm_connector_state *);
void (*pre_pll_enable)(struct intel_atomic_state *,
@@ -1185,7 +1188,6 @@ struct intel_crtc_state {
bool pkg_c_latency_used;
/* Only used for state verification. */
enum intel_panel_replay_dsc_support panel_replay_dsc_support;
- u32 dc3co_exitline;
u16 su_y_granularity;
u8 active_non_psr_pipes;
u8 entry_setup_frames;
@@ -1573,6 +1575,10 @@ struct intel_crtc {
#endif
bool vblank_psr_notify;
+
+ struct {
+ bool enabled;
+ } cmtg;
};
struct intel_plane_error {
@@ -1772,14 +1778,16 @@ struct intel_psr {
ktime_t last_exit;
bool sink_not_reliable;
bool irq_aux_error;
+ /* DC3CO allowed used to control PSR configuration */
+ bool dc3co_allowed;
+ /* DC3CO disable work */
+ struct delayed_work dc3co_work;
u16 su_w_granularity;
u16 su_y_granularity;
bool source_panel_replay_support;
bool sink_panel_replay_support;
bool panel_replay_enabled;
- u32 dc3co_exitline;
u32 dc3co_exit_delay;
- struct delayed_work dc3co_work;
u8 entry_setup_frames;
u8 io_wake_lines;
@@ -1795,6 +1803,14 @@ struct intel_psr {
struct ref_tracker *vblank_wakeref;
};
+struct intel_dp_link_config {
+ int rate;
+ int lane_count;
+};
+
+#define INTEL_DP_LINK_CONFIG_NULL \
+ ((struct intel_dp_link_config){})
+
struct intel_dp {
intel_reg_t output_reg;
u32 DP;
@@ -1820,29 +1836,9 @@ struct intel_dp {
bool use_rate_select;
/* Max sink lane count as reported by DP_MAX_LANE_COUNT */
int max_sink_lane_count;
- /* intersection of source and sink rates */
- int num_common_rates;
- int common_rates[DP_MAX_SUPPORTED_RATES];
- int max_common_lane_count;
struct {
/* TODO: move the rest of link specific fields to here */
bool active;
- /* common rate,lane_count configs in bw order */
- int num_configs;
-#define INTEL_DP_MAX_LANE_COUNT 4
-#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1)
-#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
-#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS)
-#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \
- INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
- struct intel_dp_link_config {
- u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS;
- u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS;
- } configs[INTEL_DP_MAX_LINK_CONFIGS];
- /* Max lane count for the current link */
- int max_lane_count;
- /* Max rate for the current link */
- int max_rate;
/*
* Link parameters for which the MST topology was probed.
* Tracking these ensures that the MST path resources are
@@ -1851,13 +1847,8 @@ struct intel_dp {
*/
int mst_probed_lane_count;
int mst_probed_rate;
- int force_lane_count;
- int force_rate;
- bool retrain_disabled;
- /* Sequential link training failures after a passing LT */
- int seq_train_failures;
- int force_train_failure;
- bool force_retrain;
+ struct intel_dp_link_training *training;
+ struct intel_dp_link_caps *caps;
} link;
bool reset_link_params;
int mso_link_count;
diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c
index 481fb65b7110..11f5dbf91e68 100644
--- a/drivers/gpu/drm/i915/display/intel_dmc.c
+++ b/drivers/gpu/drm/i915/display/intel_dmc.c
@@ -941,6 +941,8 @@ void intel_dmc_load_program(struct intel_display *display)
gen9_set_dc_state_debugmask(display);
+ xe3lpd_enable_dc_count(display);
+
pipedmc_clock_gating_wa(display, false);
}
@@ -1647,19 +1649,20 @@ static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused)
DMC_VERSION_MINOR(dmc->version));
if (DISPLAY_VER(display) >= 12) {
- intel_reg_t dc3co_reg;
+ if (DISPLAY_VER(display) >= 35) {
+ dc5_reg = DG1_DMC_DEBUG_DC5_COUNT;
+ seq_printf(m, "DC3CO count: %d\n",
+ intel_de_read(display, XE3P_DMC_DC3CO_COUNT));
- if (display->platform.dgfx || DISPLAY_VER(display) >= 14) {
- dc3co_reg = DG1_DMC_DEBUG3;
+ seq_printf(m, "DC3CO residency: %d\n",
+ intel_de_read(display, DC_STATE_DC3CO_RESIDENCY));
+ } else if (display->platform.dgfx || DISPLAY_VER(display) >= 14) {
dc5_reg = DG1_DMC_DEBUG_DC5_COUNT;
} else {
- dc3co_reg = TGL_DMC_DEBUG3;
dc5_reg = TGL_DMC_DEBUG_DC5_COUNT;
dc6_reg = TGL_DMC_DEBUG_DC6_COUNT;
}
- seq_printf(m, "DC3CO count: %d\n",
- intel_de_read(display, dc3co_reg));
} else {
dc5_reg = display->platform.broxton ? BXT_DMC_DC3_DC5_COUNT :
SKL_DMC_DC3_DC5_COUNT;
diff --git a/drivers/gpu/drm/i915/display/intel_dmc_regs.h b/drivers/gpu/drm/i915/display/intel_dmc_regs.h
index 38e342b45af0..6b7978fb8986 100644
--- a/drivers/gpu/drm/i915/display/intel_dmc_regs.h
+++ b/drivers/gpu/drm/i915/display/intel_dmc_regs.h
@@ -531,6 +531,8 @@ enum pipedmc_event_id {
#define TGL_DMC_DEBUG3 _MMIO(0x101090)
#define DG1_DMC_DEBUG3 _MMIO(0x13415c)
+#define XE3P_DMC_DC3CO_COUNT _MMIO(0x8f05c)
+
#define DMC_WAKELOCK_CFG _MMIO(0x8F1B0)
#define DMC_WAKELOCK_CFG_ENABLE REG_BIT(31)
#define DMC_WAKELOCK1_CTL _MMIO(0x8F140)
diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c
index b007343721e1..ab4e0e9573df 100644
--- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c
+++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c
@@ -267,7 +267,7 @@ static bool intel_dmc_wl_check_range(struct intel_display *display,
* the DMC and requires a DC exit for proper access.
*/
switch (dc_state) {
- case DC_STATE_EN_DC3CO:
+ case DC_STATE_EN_UPTO_DC3CO:
ranges = xe3lpd_dc3co_dmc_ranges;
break;
case DC_STATE_EN_UPTO_DC5:
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index c81fc00a334c..6e3fa6662cbe 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -33,7 +33,6 @@
#include <linux/notifier.h>
#include <linux/seq_buf.h>
#include <linux/slab.h>
-#include <linux/sort.h>
#include <linux/string_helpers.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
@@ -71,6 +70,7 @@
#include "intel_dp.h"
#include "intel_dp_aux.h"
#include "intel_dp_hdcp.h"
+#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
@@ -313,7 +313,7 @@ static void intel_dp_set_max_sink_lane_count(struct intel_dp *intel_dp)
}
/* Get length of rates array potentially limited by max_rate. */
-static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate)
+int intel_dp_rate_limit_len(const int *rates, int len, int max_rate)
{
int i;
@@ -326,31 +326,6 @@ static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate)
return 0;
}
-/* Get length of common rates array potentially limited by max_rate. */
-static int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp,
- int max_rate)
-{
- return intel_dp_rate_limit_len(intel_dp->common_rates,
- intel_dp->num_common_rates, max_rate);
-}
-
-int intel_dp_common_rate(struct intel_dp *intel_dp, int index)
-{
- struct intel_display *display = to_intel_display(intel_dp);
-
- if (drm_WARN_ON(display->drm,
- index < 0 || index >= intel_dp->num_common_rates))
- return 162000;
-
- return intel_dp->common_rates[index];
-}
-
-/* Theoretical max between source and sink */
-int intel_dp_max_common_rate(struct intel_dp *intel_dp)
-{
- return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1);
-}
-
int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port)
{
int vbt_max_lanes = intel_bios_dp_max_lane_count(dig_port->base.devdata);
@@ -364,43 +339,35 @@ int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port)
/*
* Theoretical max between source and sink.
- * Return %true if the max common lane count changed.
*/
-static bool intel_dp_set_max_common_lane_count(struct intel_dp *intel_dp)
+static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp)
{
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
int source_max = intel_dp_max_source_lane_count(dig_port);
int sink_max = intel_dp->max_sink_lane_count;
int lane_max = intel_tc_port_max_lane_count(dig_port);
int lttpr_max = drm_dp_lttpr_max_lane_count(intel_dp->lttpr_common_caps);
- int old_max_common_lane_count = intel_dp->max_common_lane_count;
if (lttpr_max)
sink_max = min(sink_max, lttpr_max);
- intel_dp->max_common_lane_count = min3(source_max, sink_max, lane_max);
-
- return intel_dp->max_common_lane_count != old_max_common_lane_count;
-}
-
-int intel_dp_max_common_lane_count(struct intel_dp *intel_dp)
-{
- return intel_dp->max_common_lane_count;
-}
-
-static int forced_lane_count(struct intel_dp *intel_dp)
-{
- return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp));
+ 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;
- if (intel_dp->link.force_lane_count)
- lane_count = forced_lane_count(intel_dp);
+ intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits);
+ intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
+
+ if (forced_params.lane_count)
+ lane_count = forced_params.lane_count;
else
- lane_count = intel_dp->link.max_lane_count;
+ lane_count = max_link_limits.lane_count;
switch (lane_count) {
case 1:
@@ -415,8 +382,12 @@ int intel_dp_max_lane_count(struct intel_dp *intel_dp)
static int intel_dp_min_lane_count(struct intel_dp *intel_dp)
{
- if (intel_dp->link.force_lane_count)
- return forced_lane_count(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;
}
@@ -698,174 +669,44 @@ int intel_dp_rate_index(const int *rates, int len, int rate)
return -1;
}
-static int intel_dp_link_config_rate(struct intel_dp *intel_dp,
- const struct intel_dp_link_config *lc)
-{
- return intel_dp_common_rate(intel_dp, lc->link_rate_idx);
-}
-
-static int intel_dp_link_config_lane_count(const struct intel_dp_link_config *lc)
-{
- return 1 << lc->lane_count_exp;
-}
-
-static int intel_dp_link_config_bw(struct intel_dp *intel_dp,
- const struct intel_dp_link_config *lc)
-{
- return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc),
- intel_dp_link_config_lane_count(lc));
-}
-
-static int link_config_cmp_by_bw(const void *a, const void *b, const void *p)
-{
- struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */
- const struct intel_dp_link_config *lc_a = a;
- const struct intel_dp_link_config *lc_b = b;
- int bw_a = intel_dp_link_config_bw(intel_dp, lc_a);
- int bw_b = intel_dp_link_config_bw(intel_dp, lc_b);
-
- if (bw_a != bw_b)
- return bw_a - bw_b;
-
- return intel_dp_link_config_rate(intel_dp, lc_a) -
- intel_dp_link_config_rate(intel_dp, lc_b);
-}
-
-static void intel_dp_link_config_init(struct intel_dp *intel_dp)
-{
- struct intel_display *display = to_intel_display(intel_dp);
- struct intel_dp_link_config *lc;
- int num_common_lane_configs;
- int i;
- int j;
-
- if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp))))
- return;
-
- num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1;
-
- if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs >
- ARRAY_SIZE(intel_dp->link.configs)))
- return;
-
- intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs;
-
- lc = &intel_dp->link.configs[0];
- for (i = 0; i < intel_dp->num_common_rates; i++) {
- for (j = 0; j < num_common_lane_configs; j++) {
- lc->lane_count_exp = j;
- lc->link_rate_idx = i;
-
- lc++;
- }
- }
-
- sort_r(intel_dp->link.configs, intel_dp->link.num_configs,
- sizeof(intel_dp->link.configs[0]),
- link_config_cmp_by_bw, NULL,
- intel_dp);
-}
-
-void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count)
-{
- struct intel_display *display = to_intel_display(intel_dp);
- const struct intel_dp_link_config *lc;
-
- if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs))
- idx = 0;
-
- lc = &intel_dp->link.configs[idx];
-
- *link_rate = intel_dp_link_config_rate(intel_dp, lc);
- *lane_count = intel_dp_link_config_lane_count(lc);
-}
-
-int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count)
-{
- int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates,
- link_rate);
- int lane_count_exp = ilog2(lane_count);
- int i;
-
- for (i = 0; i < intel_dp->link.num_configs; i++) {
- const struct intel_dp_link_config *lc = &intel_dp->link.configs[i];
-
- if (lc->lane_count_exp == lane_count_exp &&
- lc->link_rate_idx == link_rate_idx)
- return i;
- }
-
- return -1;
-}
-
-/* Return %true if the common rates changed. */
-static bool intel_dp_set_common_rates(struct intel_dp *intel_dp)
+static void intel_dp_get_common_rates(struct intel_dp *intel_dp,
+ int common_rates[DP_MAX_SUPPORTED_RATES],
+ int *num_common_rates)
{
struct intel_display *display = to_intel_display(intel_dp);
- int num_old_common_rates = intel_dp->num_common_rates;
- int old_common_rates[DP_MAX_SUPPORTED_RATES];
drm_WARN_ON(display->drm,
!intel_dp->num_source_rates || !intel_dp->num_sink_rates);
- /* TODO: Add a struct containing both rates and number of rates. */
- static_assert(__same_type(old_common_rates[0], intel_dp->common_rates[0]) &&
- sizeof(old_common_rates) == sizeof(intel_dp->common_rates));
- memcpy(old_common_rates, intel_dp->common_rates,
- num_old_common_rates * sizeof(old_common_rates[0]));
-
- intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates,
- intel_dp->num_source_rates,
- intel_dp->sink_rates,
- intel_dp->num_sink_rates,
- intel_dp->common_rates);
+ *num_common_rates = intersect_rates(intel_dp->source_rates,
+ intel_dp->num_source_rates,
+ intel_dp->sink_rates,
+ intel_dp->num_sink_rates,
+ common_rates);
/* Paranoia, there should always be something in common. */
- if (drm_WARN_ON(display->drm, intel_dp->num_common_rates == 0)) {
- intel_dp->common_rates[0] = 162000;
- intel_dp->num_common_rates = 1;
+ if (drm_WARN_ON(display->drm, *num_common_rates == 0)) {
+ common_rates[0] = 162000;
+ *num_common_rates = 1;
}
-
- return num_old_common_rates != intel_dp->num_common_rates ||
- memcmp(old_common_rates, intel_dp->common_rates,
- num_old_common_rates * sizeof(old_common_rates[0]));
}
/* Return %true if any common link param changed. */
static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp)
{
+ int num_common_rates;
+ int common_rates[DP_MAX_SUPPORTED_RATES];
bool params_changed = false;
- if (intel_dp_set_common_rates(intel_dp))
+ 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)))
params_changed = true;
- if (intel_dp_set_max_common_lane_count(intel_dp))
- params_changed = true;
-
- intel_dp_link_config_init(intel_dp);
-
return params_changed;
}
-bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
- u8 lane_count)
-{
- /*
- * FIXME: we need to synchronize the current link parameters with
- * hardware readout. Currently fast link training doesn't work on
- * boot-up.
- */
- if (link_rate == 0 ||
- link_rate > intel_dp->link.max_rate)
- return false;
-
- if (lane_count == 0 ||
- lane_count > intel_dp_max_lane_count(intel_dp))
- return false;
-
- return true;
-}
-
u32 intel_dp_mode_to_fec_clock(u32 mode_clock)
{
return div_u64(mul_u32_u32(mode_clock, DP_DSC_FEC_OVERHEAD_FACTOR),
@@ -1693,41 +1534,40 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp)
seq_buf_print_array(&s, intel_dp->sink_rates, intel_dp->num_sink_rates);
drm_dbg_kms(display->drm, "sink rates: %s\n", seq_buf_str(&s));
- seq_buf_clear(&s);
- seq_buf_print_array(&s, intel_dp->common_rates, intel_dp->num_common_rates);
- drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s));
-}
-
-static int forced_link_rate(struct intel_dp *intel_dp)
-{
- int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate);
-
- if (len == 0)
- return intel_dp_common_rate(intel_dp, 0);
-
- return intel_dp_common_rate(intel_dp, len - 1);
+ 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;
- if (intel_dp->link.force_rate)
- return forced_link_rate(intel_dp);
+ intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
- len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate);
+ if (forced_params.rate)
+ return forced_params.rate;
- return intel_dp_common_rate(intel_dp, len - 1);
+ 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)
{
- if (intel_dp->link.force_rate)
- return forced_link_rate(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);
- return intel_dp_common_rate(intel_dp, 0);
+ 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)
@@ -1911,6 +1751,7 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp,
const struct drm_connector_state *conn_state,
const struct link_config_limits *limits)
{
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state);
int link_rate, link_avail;
@@ -1920,8 +1761,8 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp,
int link_bpp_x16 =
intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp);
- for (i = 0; i < intel_dp->num_common_rates; i++) {
- link_rate = intel_dp_common_rate(intel_dp, i);
+ 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;
@@ -2145,12 +1986,13 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp,
const struct link_config_limits *limits,
int dsc_bpp_x16)
{
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
int link_rate, lane_count;
int i;
- for (i = 0; i < intel_dp->num_common_rates; i++) {
- link_rate = intel_dp_common_rate(intel_dp, i);
+ 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;
@@ -3685,19 +3527,19 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state,
}
int
-intel_dp_compute_config(struct intel_encoder *encoder,
+intel_dp_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
- struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state);
struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct intel_connector *connector = intel_dp->attached_connector;
int ret = 0, link_bpp_x16;
if (intel_dp_is_edp(intel_dp)) {
- ret = intel_panel_compute_config(connector, adjusted_mode);
+ ret = intel_panel_compute_config(state, pipe_config, connector);
if (ret)
return ret;
}
@@ -3810,12 +3652,10 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp,
void intel_dp_reset_link_params(struct intel_dp *intel_dp)
{
- intel_dp->link.max_lane_count = intel_dp_max_common_lane_count(intel_dp);
- intel_dp->link.max_rate = intel_dp_max_common_rate(intel_dp);
+ intel_dp_link_caps_reset(intel_dp->link.caps);
intel_dp->link.mst_probed_lane_count = 0;
intel_dp->link.mst_probed_rate = 0;
- intel_dp->link.retrain_disabled = false;
- intel_dp->link.seq_train_failures = 0;
+ intel_dp_link_training_reset(intel_dp->link.training);
}
/* Enable backlight PWM and backlight PP control. */
@@ -5715,32 +5555,6 @@ void intel_read_dp_sdp(struct intel_encoder *encoder,
}
}
-static bool intel_dp_link_ok(struct intel_dp *intel_dp,
- u8 link_status[DP_LINK_STATUS_SIZE])
-{
- struct intel_display *display = to_intel_display(intel_dp);
- struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
- bool uhbr = intel_dp->link_rate >= 1000000;
- bool ok;
-
- if (uhbr)
- ok = drm_dp_128b132b_lane_channel_eq_done(link_status,
- intel_dp->lane_count);
- else
- ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count);
-
- if (ok)
- return true;
-
- intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status);
- drm_dbg_kms(display->drm,
- "[ENCODER:%d:%s] %s link not ok, retraining\n",
- encoder->base.base.id, encoder->base.name,
- uhbr ? "128b/132b" : "8b/10b");
-
- return false;
-}
-
static void
intel_dp_mst_hpd_irq(struct intel_dp *intel_dp, u8 *esi, u8 *ack)
{
@@ -5774,7 +5588,7 @@ static bool
intel_dp_check_mst_status(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
- bool force_retrain = intel_dp->link.force_retrain;
+ bool force_retrain = intel_dp_link_training_get_force_retrain(intel_dp->link.training);
bool reprobe_needed = false;
for (;;) {
@@ -5847,78 +5661,6 @@ intel_dp_handle_hdmi_link_status_change(struct intel_dp *intel_dp)
}
}
-static int
-intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE])
-{
- int err;
-
- memset(link_status, 0, DP_LINK_STATUS_SIZE);
-
- if (intel_dp_mst_active_streams(intel_dp) > 0)
- err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI,
- link_status, DP_LINK_STATUS_SIZE - 2);
- else
- err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX,
- link_status);
-
- if (err)
- return err;
-
- if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] &
- DP_DOWNSTREAM_PORT_STATUS_CHANGED)
- WRITE_ONCE(intel_dp->downstream_port_changed, true);
-
- return 0;
-}
-
-static bool
-intel_dp_needs_link_retrain(struct intel_dp *intel_dp)
-{
- u8 link_status[DP_LINK_STATUS_SIZE];
-
- if (!intel_dp->link.active)
- return false;
-
- /*
- * While PSR source HW is enabled, it will control main-link sending
- * frames, enabling and disabling it so trying to do a retrain will fail
- * as the link would or not be on or it could mix training patterns
- * and frame data at the same time causing retrain to fail.
- * Also when exiting PSR, HW will retrain the link anyways fixing
- * any link status error.
- */
- if (intel_psr_enabled(intel_dp))
- return false;
-
- if (intel_dp->link.force_retrain)
- return true;
-
- if (intel_dp_read_link_status(intel_dp, link_status) < 0)
- return false;
-
- /*
- * Validate the cached values of intel_dp->link_rate and
- * intel_dp->lane_count before attempting to retrain.
- *
- * FIXME would be nice to user the crtc state here, but since
- * we need to call this from the short HPD handler that seems
- * a bit hard.
- */
- if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate,
- intel_dp->lane_count))
- return false;
-
- if (intel_dp->link.retrain_disabled)
- return false;
-
- if (intel_dp->link.seq_train_failures)
- return true;
-
- /* Retrain if link not ok */
- return !intel_dp_link_ok(intel_dp, link_status) &&
- !intel_psr_link_ok(intel_dp);
-}
-
bool intel_dp_has_connector(struct intel_dp *intel_dp,
const struct drm_connector_state *conn_state)
{
@@ -6010,86 +5752,6 @@ void intel_dp_flush_connector_commits(struct intel_connector *connector)
wait_for_connector_hw_done(connector->base.state);
}
-static bool intel_dp_is_connected(struct intel_dp *intel_dp)
-{
- struct intel_connector *connector = intel_dp->attached_connector;
-
- return connector->base.status == connector_status_connected ||
- intel_dp->is_mst;
-}
-
-static int intel_dp_retrain_link(struct intel_encoder *encoder,
- struct drm_modeset_acquire_ctx *ctx)
-{
- struct intel_display *display = to_intel_display(encoder);
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- u8 pipe_mask;
- int ret;
-
- if (!intel_dp_is_connected(intel_dp))
- return 0;
-
- ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex,
- ctx);
- if (ret)
- return ret;
-
- if (!intel_dp_needs_link_retrain(intel_dp))
- return 0;
-
- ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask);
- if (ret)
- return ret;
-
- if (pipe_mask == 0)
- return 0;
-
- if (!intel_dp_needs_link_retrain(intel_dp))
- return 0;
-
- drm_dbg_kms(display->drm,
- "[ENCODER:%d:%s] retraining link (forced %s)\n",
- encoder->base.base.id, encoder->base.name,
- str_yes_no(intel_dp->link.force_retrain));
-
- ret = intel_modeset_commit_pipes(display, pipe_mask, ctx);
- if (ret == -EDEADLK)
- return ret;
-
- intel_dp->link.force_retrain = false;
-
- if (ret)
- drm_dbg_kms(display->drm,
- "[ENCODER:%d:%s] link retraining failed: %pe\n",
- encoder->base.base.id, encoder->base.name,
- ERR_PTR(ret));
-
- return ret;
-}
-
-void intel_dp_link_check(struct intel_encoder *encoder)
-{
- struct drm_modeset_acquire_ctx ctx;
- int ret;
-
- intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret)
- ret = intel_dp_retrain_link(encoder, &ctx);
-}
-
-void intel_dp_check_link_state(struct intel_dp *intel_dp)
-{
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *encoder = &dig_port->base;
-
- if (!intel_dp_is_connected(intel_dp))
- return;
-
- if (!intel_dp_needs_link_retrain(intel_dp))
- return;
-
- intel_encoder_link_check_queue_work(encoder, 0);
-}
-
static void intel_dp_handle_device_service_irq(struct intel_dp *intel_dp, u8 irq_mask)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -6185,7 +5847,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
/*
* Force checking the link status for DPCD_REV < 1.2
* TODO: let the link status check depend on LINK_STATUS_CHANGED
- * or intel_dp->link.force_retrain for DPCD_REV >= 1.2
+ * or intel_dp->link.training.force_retrain for DPCD_REV >= 1.2
*/
esi[3] |= LINK_STATUS_CHANGED;
if (intel_dp_handle_link_service_irq(intel_dp, esi[3]))
@@ -7688,3 +7350,25 @@ u8 intel_dp_as_sdp_transmission_time(void)
return DP_PR_AS_SDP_SETUP_TIME_T1;
}
+
+int intel_dp_link_init(struct intel_dp *intel_dp)
+{
+ intel_dp->link.training = intel_dp_link_training_init(intel_dp);
+ if (!intel_dp->link.training)
+ return -ENOMEM;
+
+ intel_dp->link.caps = intel_dp_link_caps_init(intel_dp);
+ if (!intel_dp->link.caps) {
+ intel_dp_link_training_cleanup(intel_dp->link.training);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void intel_dp_link_cleanup(struct intel_dp *intel_dp)
+{
+ intel_dp_link_caps_cleanup(intel_dp->link.caps);
+ intel_dp_link_training_cleanup(intel_dp->link.training);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h
index 46a7f5c70981..02b691df6755 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.h
+++ b/drivers/gpu/drm/i915/display/intel_dp.h
@@ -59,8 +59,6 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp,
struct drm_modeset_acquire_ctx *ctx,
u8 *pipe_mask);
void intel_dp_flush_connector_commits(struct intel_connector *connector);
-void intel_dp_link_check(struct intel_encoder *encoder);
-void intel_dp_check_link_state(struct intel_dp *intel_dp);
void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode);
void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
@@ -73,7 +71,8 @@ void intel_dp_sink_disable_decompression(struct intel_atomic_state *state,
void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder);
void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder);
void intel_dp_encoder_flush_work(struct drm_encoder *encoder);
-int intel_dp_compute_config(struct intel_encoder *encoder,
+int intel_dp_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state);
bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state,
@@ -102,17 +101,13 @@ void intel_edp_backlight_off(const struct drm_connector_state *conn_state);
void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp);
void intel_dp_mst_suspend(struct intel_display *display);
void intel_dp_mst_resume(struct intel_display *display);
+int intel_dp_rate_limit_len(const int *rates, int len, int max_rate);
int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port);
int intel_dp_max_link_rate(struct intel_dp *intel_dp);
int intel_dp_max_lane_count(struct intel_dp *intel_dp);
int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state);
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate);
-int intel_dp_max_common_rate(struct intel_dp *intel_dp);
-int intel_dp_max_common_lane_count(struct intel_dp *intel_dp);
-int intel_dp_common_rate(struct intel_dp *intel_dp, int index);
int intel_dp_rate_index(const int *rates, int len, int rate);
-int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count);
-void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count);
void intel_dp_update_sink_caps(struct intel_dp *intel_dp);
void intel_dp_reset_link_params(struct intel_dp *intel_dp);
@@ -209,8 +204,6 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev,
struct intel_connector *connector);
bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder);
-bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
- u8 lane_count);
bool intel_dp_has_connector(struct intel_dp *intel_dp,
const struct drm_connector_state *conn_state);
int intel_dp_dsc_max_src_input_bpc(struct intel_display *display);
@@ -243,4 +236,7 @@ bool intel_dp_joiner_candidate_valid(struct intel_connector *connector,
u8 intel_dp_as_sdp_transmission_time(void);
+int intel_dp_link_init(struct intel_dp *intel_dp);
+void intel_dp_link_cleanup(struct intel_dp *intel_dp);
+
#endif /* __INTEL_DP_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c
new file mode 100644
index 000000000000..1c34ba6c49c3
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/log2.h>
+#include <linux/seq_buf.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <drm/drm_print.h>
+
+#include "intel_display_core.h"
+#include "intel_display_types.h"
+#include "intel_dp.h"
+#include "intel_dp_link_caps.h"
+
+struct intel_dp_link_caps {
+ struct intel_dp *dp;
+
+ /* Rate, lane count caps common to source and sink. */
+ int num_rates;
+ int rates[DP_MAX_SUPPORTED_RATES];
+ int max_lane_count;
+
+ /* common rate,lane_count configs in bw order */
+ int num_configs;
+#define INTEL_DP_MAX_LANE_COUNT 4
+#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1)
+#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
+#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS)
+#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \
+ INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
+ struct intel_dp_link_config_entry {
+ /* index into rates[] */
+ u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS;
+ u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS;
+ } configs[INTEL_DP_MAX_LINK_CONFIGS];
+
+ /*
+ * Forced parameters requested via debugfs. Remains set across sink
+ * disconnects.
+ */
+ struct intel_dp_link_config forced_params;
+
+ /*
+ * User set maximum limits. These limits constrain the currently
+ * allowed set of configurations and are not adjusted when sink
+ * capabilities change.
+ *
+ * max_limits.rate/lane_count may come from different allowed
+ * configurations, i.e. the (max_limits.rate, max_limits.lane_count)
+ * tuple itself may not be an allowed configuration.
+ */
+ struct intel_dp_link_config max_limits;
+};
+
+/* Get length of common rates array potentially limited by max_rate. */
+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)
+{
+ struct intel_display *display = to_intel_display(link_caps->dp);
+
+ if (drm_WARN_ON(display->drm,
+ index < 0 || index >= link_caps->num_rates))
+ return 162000;
+
+ 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)
+{
+ 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);
+ DECLARE_SEQ_BUF(s, 128);
+ int i;
+
+ for (i = 0; i < link_caps->num_rates; i++)
+ seq_buf_printf(&s, "%s%d", i ? ", " : "", link_caps->rates[i]);
+
+ drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s));
+}
+
+int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps)
+{
+ return link_caps->max_lane_count;
+}
+
+static int forced_lane_count(struct intel_dp_link_caps *link_caps)
+{
+ if (!link_caps->forced_params.lane_count)
+ return 0;
+
+ return clamp(link_caps->forced_params.lane_count,
+ 1, intel_dp_link_caps_max_common_lane_count(link_caps));
+}
+
+static int forced_link_rate(struct intel_dp_link_caps *link_caps)
+{
+ int len;
+
+ if (!link_caps->forced_params.rate)
+ return 0;
+
+ len = intel_dp_common_len_rate_limit(link_caps, link_caps->forced_params.rate);
+ if (len == 0)
+ return intel_dp_common_rate(link_caps, 0);
+
+ return intel_dp_common_rate(link_caps, len - 1);
+}
+
+void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps,
+ struct intel_dp_link_config *forced_params)
+{
+ forced_params->rate = forced_link_rate(link_caps);
+ forced_params->lane_count = forced_lane_count(link_caps);
+}
+
+static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps,
+ const struct intel_dp_link_config_entry *lce)
+{
+ return intel_dp_common_rate(link_caps, lce->link_rate_idx);
+}
+
+static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lce)
+{
+ 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)
+{
+ link_caps->max_limits = *max_link_limits;
+}
+
+static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps)
+{
+ struct intel_dp_link_config max_link_limits = {
+ .rate = intel_dp_max_common_rate(link_caps),
+ .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps),
+ };
+
+ set_max_link_limits_no_update(link_caps, &max_link_limits);
+}
+
+/**
+ * intel_dp_link_caps_get_max_limits - get the current maximum link limits
+ * @link_caps: link capabilities state
+ * @max_link_limits: returned maximum link limits
+ *
+ * Return the current maximum rate and lane count limits in
+ * @max_link_limits.
+ *
+ * These limits constrain the set of allowed configurations.
+ *
+ * The limits are set to the maximum common supported values after
+ * intel_dp_link_caps_reset() is called, and can later be modified by
+ * intel_dp_link_caps_set_max_limits(). The max rate and lane count
+ * parameters are independent limits, so the pair does not necessarily
+ * define a valid configuration.
+ *
+ * This function may be called without serializing against updates to
+ * @link_caps. However, without such serialization the returned value may be
+ * an out-of-sync (link rate, lane count) tuple, i.e. the parameters may
+ * belong to different update snapshots in time.
+ */
+void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
+ struct intel_dp_link_config *max_link_limits)
+{
+ *max_link_limits = link_caps->max_limits;
+}
+
+/**
+ * intel_dp_link_caps_set_max_limits - set the current maximum link limits
+ * @link_caps: link capabilities state
+ * @max_link_limits: new maximum link limits
+ *
+ * Set the current maximum rate and lane count limits to @max_link_limits,
+ * constraining the set of allowed configurations.
+ *
+ * Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize
+ * this call against concurrent queries and updates to @link_caps, in line
+ * with the rest of the API.
+ *
+ * Return:
+ * - %true if the @link_caps cached max limits value got updated with
+ * @max_link_limits.
+ * - %false if @max_link_limits is invalid.
+ */
+bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps,
+ const struct intel_dp_link_config *max_link_limits)
+{
+ set_max_link_limits_no_update(link_caps, max_link_limits);
+
+ /* TODO: validate max_link_limits */
+ return true;
+}
+
+/**
+ * intel_dp_link_caps_reset_max_limits - reset the current maximum link limits
+ * @link_caps: link capabilities state
+ *
+ * Reset the current maximum link limits to the maximum supported common link
+ * rate and lane count.
+ */
+void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps)
+{
+ reset_max_link_limits_no_update(link_caps);
+}
+
+static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps,
+ const struct intel_dp_link_config_entry *lce)
+{
+ return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lce),
+ intel_dp_link_config_lane_count(lce));
+}
+
+static int link_config_cmp_by_bw(const void *a, const void *b, const void *p)
+{
+ struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+
+ const struct intel_dp_link_config_entry *lce_a = a;
+ const struct intel_dp_link_config_entry *lce_b = b;
+ int bw_a = intel_dp_link_config_bw(link_caps, lce_a);
+ int bw_b = intel_dp_link_config_bw(link_caps, lce_b);
+
+ if (bw_a != bw_b)
+ return bw_a - bw_b;
+
+ return intel_dp_link_config_rate(link_caps, lce_a) -
+ intel_dp_link_config_rate(link_caps, lce_b);
+}
+
+/* Return %true if the supported link parameters have changed. */
+bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps,
+ const int *rates, int num_rates, int max_lane_count)
+{
+ struct intel_dp *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;
+ int num_common_lane_configs;
+ int i;
+ int j;
+
+ if (drm_WARN_ON(display->drm, !is_power_of_2(max_lane_count)))
+ return false;
+
+ if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(link_caps->rates)))
+ return false;
+
+ num_common_lane_configs = ilog2(max_lane_count) + 1;
+
+ if (drm_WARN_ON(display->drm, num_rates * num_common_lane_configs >
+ ARRAY_SIZE(link_caps->configs)))
+ return false;
+
+ /* TODO: Add a struct containing both rates and number of rates. */
+ static_assert(__same_type(rates[0], link_caps->rates[0]));
+ if (num_rates != link_caps->num_rates ||
+ memcmp(rates, link_caps->rates, num_rates * sizeof(rates[0])))
+ link_params_changed = true;
+
+ if (max_lane_count != link_caps->max_lane_count)
+ link_params_changed = true;
+
+ memcpy(link_caps->rates, rates, num_rates * sizeof(rates[0]));
+ link_caps->num_rates = num_rates;
+ link_caps->max_lane_count = max_lane_count;
+
+ link_caps->num_configs = num_rates * num_common_lane_configs;
+
+ lce = &link_caps->configs[0];
+ for (i = 0; i < link_caps->num_rates; i++) {
+ for (j = 0; j < num_common_lane_configs; j++) {
+ lce->lane_count_exp = j;
+ lce->link_rate_idx = i;
+
+ lce++;
+ }
+ }
+
+ sort_r(link_caps->configs, link_caps->num_configs,
+ sizeof(link_caps->configs[0]),
+ 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);
+}
+
+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;
+
+ for (i = 0; i < link_caps->num_configs; i++) {
+ const struct intel_dp_link_config_entry *lce = &link_caps->configs[i];
+
+ if (lce->lane_count_exp == lane_count_exp &&
+ lce->link_rate_idx == link_rate_idx)
+ return i;
+ }
+
+ return -1;
+}
+
+/**
+ * intel_dp_link_caps_reset - reset link capability restrictions
+ * @link_caps: link capabilities state
+ *
+ * Reset all current restrictions except for the user requested forced
+ * parameters, thus updating the set of allowed configurations and the
+ * derived maximum link information accordingly.
+ *
+ * This function is regularly called after a sink is connected, either
+ * for the first time to the connector or after a previous sink was
+ * disconnected from it, and intel_dp_link_caps_update() was called.
+ */
+void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps)
+{
+ /* TODO: Update the maximum link information. */
+ reset_max_link_limits_no_update(link_caps);
+}
+
+static int i915_dp_force_link_rate_show(struct seq_file *m, void *data)
+{
+ struct intel_connector *connector = to_intel_connector(m->private);
+ struct intel_display *display = to_intel_display(connector);
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+ int current_rate = -1;
+ int force_rate;
+ int err;
+ int i;
+
+ err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
+ if (err)
+ return err;
+
+ intel_dp_flush_connector_commits(connector);
+
+ if (intel_dp->link.active)
+ current_rate = intel_dp->link_rate;
+
+ force_rate = link_caps->forced_params.rate;
+
+ drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+
+ seq_printf(m, "%sauto%s",
+ force_rate == 0 ? "[" : "",
+ force_rate == 0 ? "]" : "");
+
+ for (i = 0; i < intel_dp->num_source_rates; i++)
+ seq_printf(m, " %s%d%s%s",
+ intel_dp->source_rates[i] == force_rate ? "[" : "",
+ intel_dp->source_rates[i],
+ intel_dp->source_rates[i] == current_rate ? "*" : "",
+ intel_dp->source_rates[i] == force_rate ? "]" : "");
+
+ seq_putc(m, '\n');
+
+ return 0;
+}
+
+static int parse_link_rate(struct intel_dp_link_caps *link_caps, const char __user *ubuf, size_t len)
+{
+ struct intel_dp *intel_dp = link_caps->dp;
+ char *kbuf;
+ const char *p;
+ int rate;
+ int ret = 0;
+
+ kbuf = memdup_user_nul(ubuf, len);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+
+ p = strim(kbuf);
+
+ if (!strcmp(p, "auto")) {
+ rate = 0;
+ } else {
+ ret = kstrtoint(p, 0, &rate);
+ if (ret < 0)
+ goto out_free;
+
+ if (intel_dp_rate_index(intel_dp->source_rates,
+ intel_dp->num_source_rates,
+ rate) < 0)
+ ret = -EINVAL;
+ }
+
+out_free:
+ kfree(kbuf);
+
+ return ret < 0 ? ret : rate;
+}
+
+static ssize_t i915_dp_force_link_rate_write(struct file *file,
+ const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ struct intel_connector *connector = to_intel_connector(m->private);
+ struct intel_display *display = to_intel_display(connector);
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+ int rate;
+ int err;
+
+ rate = parse_link_rate(link_caps, ubuf, len);
+ if (rate < 0)
+ return rate;
+
+ err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
+ if (err)
+ return err;
+
+ intel_dp_flush_connector_commits(connector);
+
+ intel_dp_reset_link_params(intel_dp);
+ link_caps->forced_params.rate = rate;
+
+ drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+
+ *offp += len;
+
+ return len;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate);
+
+static int i915_dp_force_lane_count_show(struct seq_file *m, void *data)
+{
+ struct intel_connector *connector = to_intel_connector(m->private);
+ struct intel_display *display = to_intel_display(connector);
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+ int current_lane_count = -1;
+ int force_lane_count;
+ int err;
+ int i;
+
+ err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
+ if (err)
+ return err;
+
+ intel_dp_flush_connector_commits(connector);
+
+ if (intel_dp->link.active)
+ current_lane_count = intel_dp->lane_count;
+ force_lane_count = link_caps->forced_params.lane_count;
+
+ drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+
+ seq_printf(m, "%sauto%s",
+ force_lane_count == 0 ? "[" : "",
+ force_lane_count == 0 ? "]" : "");
+
+ for (i = 1; i <= 4; i <<= 1)
+ seq_printf(m, " %s%d%s%s",
+ i == force_lane_count ? "[" : "",
+ i,
+ i == current_lane_count ? "*" : "",
+ i == force_lane_count ? "]" : "");
+
+ seq_putc(m, '\n');
+
+ return 0;
+}
+
+static int parse_lane_count(const char __user *ubuf, size_t len)
+{
+ char *kbuf;
+ const char *p;
+ int lane_count;
+ int ret = 0;
+
+ kbuf = memdup_user_nul(ubuf, len);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+
+ p = strim(kbuf);
+
+ if (!strcmp(p, "auto")) {
+ lane_count = 0;
+ } else {
+ ret = kstrtoint(p, 0, &lane_count);
+ if (ret < 0)
+ goto out_free;
+
+ switch (lane_count) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ }
+
+out_free:
+ kfree(kbuf);
+
+ return ret < 0 ? ret : lane_count;
+}
+
+static ssize_t i915_dp_force_lane_count_write(struct file *file,
+ const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ struct intel_connector *connector = to_intel_connector(m->private);
+ struct intel_display *display = to_intel_display(connector);
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+ int lane_count;
+ int err;
+
+ lane_count = parse_lane_count(ubuf, len);
+ if (lane_count < 0)
+ return lane_count;
+
+ err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
+ if (err)
+ return err;
+
+ intel_dp_flush_connector_commits(connector);
+
+ intel_dp_reset_link_params(intel_dp);
+ link_caps->forced_params.lane_count = lane_count;
+
+ drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+
+ *offp += len;
+
+ return len;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count);
+
+static int i915_dp_max_link_rate_show(void *data, u64 *val)
+{
+ struct intel_connector *connector = to_intel_connector(data);
+ struct intel_display *display = to_intel_display(connector);
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_config max_link_limits;
+ int err;
+
+ err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
+ if (err)
+ return err;
+
+ intel_dp_flush_connector_commits(connector);
+
+ intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits);
+ *val = max_link_limits.rate;
+
+ drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n");
+
+static int i915_dp_max_lane_count_show(void *data, u64 *val)
+{
+ struct intel_connector *connector = to_intel_connector(data);
+ struct intel_display *display = to_intel_display(connector);
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_config max_link_limits;
+ int err;
+
+ err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
+ if (err)
+ return err;
+
+ intel_dp_flush_connector_commits(connector);
+
+ intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits);
+ *val = max_link_limits.lane_count;
+
+ drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n");
+
+
+/**
+ * intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector
+ * @connector: connector to add the debugfs files for
+ *
+ * Add the link-capability debugfs files for a DP @connector.
+ */
+void intel_dp_link_caps_debugfs_add(struct intel_connector *connector)
+{
+ struct dentry *root = connector->base.debugfs_entry;
+
+ if (connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort &&
+ connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
+ return;
+
+ debugfs_create_file("i915_dp_force_link_rate", 0644, root,
+ connector, &i915_dp_force_link_rate_fops);
+
+ debugfs_create_file("i915_dp_force_lane_count", 0644, root,
+ connector, &i915_dp_force_lane_count_fops);
+
+ debugfs_create_file("i915_dp_max_link_rate", 0444, root,
+ connector, &i915_dp_max_link_rate_fops);
+
+ debugfs_create_file("i915_dp_max_lane_count", 0444, root,
+ connector, &i915_dp_max_lane_count_fops);
+}
+
+struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp)
+{
+ struct intel_dp_link_caps *link_caps;
+
+ link_caps = kzalloc_obj(*link_caps);
+ if (!link_caps)
+ return NULL;
+
+ link_caps->dp = intel_dp;
+
+ return link_caps;
+}
+
+void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps)
+{
+ kfree(link_caps);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h
new file mode 100644
index 000000000000..af9028e7cb98
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2026 Intel Corporation */
+
+#ifndef __INTEL_DP_LINK_CAPS_H__
+#define __INTEL_DP_LINK_CAPS_H__
+
+#include <linux/types.h>
+
+struct intel_connector;
+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);
+
+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);
+
+void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
+ struct intel_dp_link_config *max_link_limits);
+bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps,
+ const struct intel_dp_link_config *max_link_limits);
+void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps);
+
+bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps,
+ const int *rates, int num_rates, int max_lane_count);
+void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps);
+
+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);
+
+#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 e566f2b49594..b521dd11b62a 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
@@ -27,18 +27,344 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_print.h>
+#include "intel_display.h"
#include "intel_display_core.h"
#include "intel_display_jiffies.h"
#include "intel_display_types.h"
#include "intel_display_utils.h"
#include "intel_dp.h"
+#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
+#include "intel_dp_mst.h"
#include "intel_encoder.h"
#include "intel_hdmi.h"
#include "intel_hotplug.h"
+#include "intel_modeset_lock.h"
#include "intel_panel.h"
#include "intel_psr.h"
+/**
+ * DOC: DisplayPort link training
+ *
+ * This documents the Intel DisplayPort link training implementation and
+ * its internal interfaces, with a current focus on link recovery.
+ *
+ * Documentation of the full link training procedure is not yet included.
+ *
+ * The Intel DP link recovery logic governs how the driver reacts to
+ * link training failures and to links that degrade asynchronously
+ * after a previously successful training. Recovery is first attempted
+ * via automatic retraining (``autoretrain``) and, when that is no
+ * longer possible, by selecting fallback link configurations and
+ * notifying userspace to recover the link via a modeset.
+ *
+ * Recovery sequence and userspace notification
+ * --------------------------------------------
+ *
+ * After the first link training failure following initialization or a
+ * previously successful training, recovery is first attempted by the
+ * driver via automatic retraining, without userspace involvement.
+ * During this phase, a given link configuration is attempted twice
+ * before being abandoned: after the initial link training failure, an
+ * automatic retraining modeset is performed with the same link
+ * parameters, constituting the second attempt.
+ *
+ * Once automatic retraining is no longer possible, recovery is delegated
+ * to userspace, which must select a new modeset configuration, as the
+ * kernel must not do so. From this point onwards, each link configuration
+ * may be attempted only once as userspace iterates through alternative
+ * configurations. A successful link training restores the automatic
+ * retraining model for subsequent failures.
+ *
+ * The failure of the last automatic retraining attempt is reported to
+ * userspace, and from that point onward the driver notifies userspace of
+ * each subsequent failure. This allows userspace to both initiate
+ * recovery via modesets and observe the outcome of those recovery
+ * attempts, even when no further fallback configurations remain.
+ *
+ * Link training failures are always reported to userspace, even when they
+ * result from a kernel-internal modeset. Such modesets only re-apply the
+ * existing userspace-provided state and must not modify it. A failure
+ * triggered by such a modeset is therefore treated the same as a link
+ * degradation after a previously successful training, and recovery is
+ * handled by userspace in place of the kernel caller.
+ *
+ * Contexts
+ * --------
+ *
+ * The following execution contexts (A/B/C) describe how the different
+ * recovery states are reached but are not themselves implementation
+ * states. The actual state machine is defined by &enum
+ * intel_dp_link_training_recovery_state.
+ *
+ * A. Modeset context:
+ *
+ * Triggered by:
+ * - link training during a modeset, or
+ * - via the "i915_dp_force_link_training_failure" debugfs entry,
+ * forcing this path by emulating a link training failure.
+ *
+ * Transitions:
+ * - A1 Link training succeeds.
+ *
+ * A link check work to recover any degraded link is scheduled
+ * (and handled if needed in context B).
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_IDLE.
+ *
+ * - A2 First link training fails after initialization or a previously
+ * successful link training.
+ *
+ * An automatic retraining work is scheduled (and handled in
+ * context B) with the same link parameters with which the link
+ * training failed.
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING.
+ *
+ * - A3 Link training fails again after A2 or A3.
+ *
+ * Through fallback selection, the driver attempts to restrict the
+ * allowed link configurations for subsequent modesets. This may
+ * be done either by lowering global limits (rate/lane caps), or by
+ * disabling only the currently failing configuration while leaving
+ * all other configurations allowed, even if they use higher rate or
+ * lane count.
+ *
+ * (The current implementation may still apply parameter capping as
+ * a coarse fallback selection mechanism. This is transitional and is
+ * expected to be replaced by a scheme that disables only the failing
+ * configuration, rather than removing configurations that have not
+ * been observed to fail and may still train successfully.)
+ *
+ * This case may repeat in a loop:
+ * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED ->
+ * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED
+ *
+ * via repeated A3a -> A3a transitions until the configuration fallback
+ * space is exhausted, reaching the A3b terminal case.
+ *
+ * - A3a Fallback selection succeeds.
+ *
+ * Userspace is notified to retry the modeset.
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED.
+ *
+ * - A3b Fallback selection fails.
+ *
+ * Userspace is notified of the failure and may continue recovery
+ * by retrying the modeset with the remaining allowed link
+ * configuration.
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK.
+ *
+ * B. Automatic retraining context:
+ *
+ * Triggered by:
+ * - after a successful link training in context A1 followed by
+ * asynchronous link degradation, or
+ * - after the first failed link training attempt in context A2, or
+ * - via the "i915_dp_force_link_retrain" debugfs entry, which may
+ * bypass normal gating and force this path.
+ *
+ * Transitions:
+ * - B1 ``Autoretrain`` modeset check and link training succeeds.
+ *
+ * The case is handled as in A1, scheduling a link check work to
+ * recover any degraded link.
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_IDLE.
+ *
+ * - B2 ``Autoretrain`` modeset check succeeds but link training fails.
+ *
+ * - B2a Previously the link degraded asynchronously (current state
+ * is %INTEL_DP_LINK_RECOVERY_IDLE).
+ *
+ * This corresponds to a first failure in a new failure
+ * sequence and is handled as in A2: an automatic retraining
+ * attempt is scheduled with the same link parameters.
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING.
+ *
+ * - B2b Previously a link training failed (current state is
+ * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING).
+ *
+ * In non-regular (debug-forced) scenarios this may also be
+ * reached from
+ * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED or
+ * %INTEL_DP_LINK_RECOVERY_NO_FALLBACK, effectively behaving
+ * like a userspace-driven recovery attempt.
+ *
+ * The failure is handled as in A3, performing a fallback selection:
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED (via A3a).
+ *
+ * or
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK (via A3b).
+ *
+ * - B3 ``Autoretrain`` modeset check fails (and hence the link training
+ * cannot be started).
+ *
+ * The modeset check may fail, for example, due to external conditions
+ * such as changed shared link bandwidth, which can make previously
+ * valid modeset parameters no longer acceptable.
+ *
+ * In this case, automatic retraining is disabled without selecting
+ * a fallback configuration. The driver hands recovery over to
+ * userspace without modifying the allowed configuration set, so a
+ * subsequent userspace modeset will retry with the current link
+ * configuration. Userspace is in a better position to select new
+ * modeset parameters (e.g. video mode or enabled outputs) that
+ * satisfy the updated constraints, as the driver is only allowed
+ * to retry the modeset with the existing userspace-provided modeset
+ * configuration.
+ *
+ * This policy preserves the normal retry model, where a given link
+ * configuration is attempted twice in the automatic retraining
+ * flow before being abandoned: after a first link training failure,
+ * an automatic retraining modeset is performed with the same link
+ * parameters, and if its atomic check passes, the link training
+ * itself may either succeed or fail, constituting the second
+ * attempt. In this case, however, the retry modeset's atomic check
+ * failed, so no second link training attempt with those parameters
+ * was performed, and selecting a fallback would cause that
+ * configuration to be tried only once rather than twice.
+ *
+ * The userspace-driven link recovery continues with subsequent
+ * userspace modesets handled in A3.
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED.
+ *
+ * C. State reset context:
+ *
+ * Triggered by:
+ * - sink capability changes, or
+ * - sink disconnect/reconnect, or
+ * - system suspend/resume or power transitions where HPD
+ * handling may have been suppressed, or
+ * - successful link training.
+ *
+ * Transitions:
+ * - The recovery state is reset from any of the recovery states
+ *
+ * State -> %INTEL_DP_LINK_RECOVERY_IDLE.
+ *
+ * After reset, the driver may re-check link status and schedule
+ * retraining if the link is found to remain degraded.
+ *
+ * State transition summary
+ * ------------------------
+ *
+ * - From %INTEL_DP_LINK_RECOVERY_IDLE
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_IDLE
+ *
+ * - | In context: B1
+ * | Action: no action
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING
+ *
+ * - | In contexts: A2, B2a
+ * | Action: queue ``autoretrain`` work
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED
+ *
+ * - | In context: B3
+ * | Action: notify userspace
+ *
+ * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED
+ *
+ * - | In contexts: A3a, B2b
+ * | Action: select fallback configurations, notify userspace
+ *
+ * - | In context: B3
+ * | Action: notify userspace
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK
+ *
+ * - | In contexts: A3b, B2b
+ * | Action: notify userspace
+ *
+ * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED
+ *
+ * - | In contexts: A3a, B2b
+ * | Action: select fallback configurations, notify userspace
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK
+ *
+ * - | In contexts: A3b, B2b
+ * | Action: notify userspace
+ *
+ * - From %INTEL_DP_LINK_RECOVERY_NO_FALLBACK
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK
+ *
+ * - | In contexts: A3b
+ * | Action: notify userspace
+ *
+ * - From any state
+ *
+ * - To %INTEL_DP_LINK_RECOVERY_IDLE
+ *
+ * - | In contexts: C
+ * | Action: no action
+ *
+ * Recovery flows
+ * --------------
+ *
+ * Userspace modeset link recovery::
+ *
+ * [IDLE]
+ * |
+ * | userspace modeset link training fails
+ * | (autoretrain link recovery work scheduled)
+ * v
+ * [AUTORETRAIN_PENDING]-- autoretrain link recovery succeeds -> [IDLE]
+ * |
+ * | autoretrain link recovery modeset check or link training fails
+ * |
+ * +--o--+
+ * modeset check fails | | link training fails
+ * (userspace notified) | |
+ * | o-------- no fallback (userspace notified) ---> [NO_FALLBACK]
+ * | |
+ * +-------------+ | | fallback selected (userspace notified)
+ * | | | |
+ * | v v v
+ * | [AUTORETRAIN_DISABLED]--- userspace link recovery succeeds ----> [IDLE]
+ * | |
+ * | | userspace link recovery fails
+ * | |
+ * +-------------------o------------- no fallback (userspace notified) ----> [NO_FALLBACK]
+ * fallback selected
+ * (userspace notified)
+ *
+ * Asynchronous link degradation recovery::
+ *
+ * [IDLE]
+ * |
+ * | link degrades
+ * | (autoretrain link recovery performed)
+ * |
+ * o--- autoretrain link recovery succeeds ---> [IDLE]
+ * |
+ * | autoretrain link recovery modeset check or link training fails
+ * |
+ * +--o--+
+ * modeset check fails | | link training fails
+ * (userspace notified) | | (autoretrain work scheduled)
+ * v v
+ * [AUTORETRAIN_DISABLED*] [AUTORETRAIN_PENDING*]
+ *
+ * ``*`` marks states where the sequence continues from the corresponding state
+ * in the Userspace modeset link recovery flow above.
+ *
+ */
+
#define LT_MSG_PREFIX "[CONNECTOR:%d:%s][ENCODER:%d:%s][%s] "
#define LT_MSG_ARGS(_intel_dp, _dp_phy) (_intel_dp)->attached_connector->base.base.id, \
(_intel_dp)->attached_connector->base.name, \
@@ -60,7 +386,72 @@
lt_dbg(_intel_dp, _dp_phy, "Sink disconnected: " _format, ## __VA_ARGS__); \
} while (0)
-#define MAX_SEQ_TRAIN_FAILURES 2
+/*
+ * enum intel_dp_link_recovery_state - LT recovery state
+ * @INTEL_DP_LINK_RECOVERY_IDLE:
+ * No link training failure is currently tracked and no recovery is
+ * in progress. This is the initial state after driver initialization,
+ * power state transitions, sink (re-)connection, or after a successful
+ * link training.
+ *
+ * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING:
+ * A first link training failure has been observed and an automatic
+ * retraining attempt with the same link parameters is pending. Exactly
+ * one such attempt is allowed before switching to userspace-driven
+ * recovery.
+ *
+ * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED:
+ * Automatic retraining is no longer possible. At this point, a
+ * fallback selection is made and userspace is notified to take over
+ * recovery, performing modesets with parameters it determines are
+ * required. The driver then selects a link configuration from the
+ * remaining fallback configuration set. Subsequent link training
+ * failures trigger further fallback selections and userspace
+ * notifications.
+ *
+ * @INTEL_DP_LINK_RECOVERY_NO_FALLBACK:
+ * Fallback selection is no longer possible, as no usable fallback link
+ * configurations remain. Recovery must proceed via userspace modesets
+ * using the remaining allowed link configuration. Userspace continues
+ * to be notified of subsequent link training failures.
+ *
+ * Describes the link recovery state used by the Intel DP link recovery
+ * logic.
+ *
+ * See also:
+ * - DOC: DisplayPort link training
+ * - link_recovery_autoretrain_pending()
+ * - link_recovery_autoretrain_allowed()
+ * - link_recovery_has_no_fallback()
+ * - link_recovery_mark_train_failure()
+ * - link_recovery_mark_autoretrain_modeset_failure()
+ * - link_recovery_mark_no_fallback()
+ * - link_recovery_reset()
+ */
+enum intel_dp_link_recovery_state {
+ /*
+ * Keep the enum values ordered from least to most severe
+ * recovery state; helper logic relies on that ordering.
+ */
+ INTEL_DP_LINK_RECOVERY_IDLE,
+ INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING,
+ INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED,
+ INTEL_DP_LINK_RECOVERY_NO_FALLBACK,
+};
+
+struct intel_dp_link_training {
+ struct intel_dp *dp;
+
+ enum intel_dp_link_recovery_state recovery_state;
+
+ int force_train_failure;
+ bool force_retrain;
+};
+
+static struct intel_dp_link_training *connector_to_link_training(struct intel_connector *connector)
+{
+ return intel_attached_dp(connector)->link.training;
+}
static void intel_dp_reset_lttpr_common_caps(struct intel_dp *intel_dp)
{
@@ -245,12 +636,12 @@ int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_S
* transparent mode link training mode.
*
* Returns:
- * >0 if LTTPRs were detected and the non-transparent LT mode was set. The
+ * - >0 if LTTPRs were detected and the non-transparent LT mode was
+ * set. The DPRX capabilities are read out.
+ * - 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of
+ * a detection failure and the transparent LT mode was set. The
* DPRX capabilities are read out.
- * 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of a
- * detection failure and the transparent LT mode was set. The DPRX
- * capabilities are read out.
- * <0 Reading out the DPRX capabilities failed.
+ * - <0 Reading out the DPRX capabilities failed.
*/
int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp)
{
@@ -1247,6 +1638,124 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp,
return sink_status & DP_INTRA_HOP_AUX_REPLY_INDICATION ? 1 : 0;
}
+static bool
+link_recovery_autoretrain_pending(struct intel_dp_link_training *link_training)
+{
+ return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING;
+}
+
+/*
+ * Automatic retraining is a driver-driven link recovery mechanism that
+ * retrains the link with the current userspace provided modeset
+ * configuration and link parameters.
+ *
+ * Autoretrain is allowed while the link configurations available for
+ * retraining, i.e. those not disabled yet via fallback selection, still
+ * make it possible to retrain the link for the current userspace provided
+ * modeset configuration.
+ *
+ * Once automatic retraining is no longer allowed, userspace driven link
+ * recovery via userspace notifications and userspace modesets takes over.
+ *
+ * See also:
+ * - DOC: DisplayPort link training
+ */
+static bool
+link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training)
+{
+ switch (link_training->recovery_state) {
+ case INTEL_DP_LINK_RECOVERY_IDLE:
+ case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+link_recovery_has_no_fallback(struct intel_dp_link_training *link_training)
+{
+ return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_NO_FALLBACK;
+}
+
+/*
+ * Record a link training failure and advance the recovery state to
+ * indicate the next required recovery step.
+ *
+ * The caller must proceed with recovery as instructed by the return
+ * value, either via automatic retraining or, once automatic retraining
+ * is no longer possible, via userspace modesets after fallback
+ * selection.
+ *
+ * Note that the error reported via this function is the error seen by
+ * the link training failure handler proper after an actual link
+ * training failure indicated by the sink device, and so the error and
+ * corresponding actions required are distinct from an autoretrain
+ * modeset failure. See link_recovery_mark_autoretrain_modeset_failure() to
+ * report a modeset failure.
+ *
+ * See also:
+ * - DOC: DisplayPort link training
+ */
+static bool
+link_recovery_mark_train_failure(struct intel_dp_link_training *link_training)
+{
+ switch (link_training->recovery_state) {
+ case INTEL_DP_LINK_RECOVERY_IDLE:
+ link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING;
+ break;
+ case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING:
+ link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED;
+ break;
+ default:
+ break;
+ }
+
+ return link_recovery_autoretrain_allowed(link_training);
+}
+
+/*
+ * Record a failure of the autoretrain modeset before link training
+ * itself could run.
+ *
+ * Note that the error reported via this function and the corresponding
+ * expected actions are distinct from an actual link training failure:
+ * the modeset failed before a link training attempt could be performed.
+ * See link_recovery_mark_train_failure() to report an actual link
+ * training failure.
+ *
+ * Update the state to indicate that further recovery is to be delegated to
+ * userspace via a regular modeset.
+ *
+ * See also:
+ * - DOC: DisplayPort link training
+ */
+static void
+link_recovery_mark_autoretrain_modeset_failure(struct intel_dp_link_training *link_training)
+{
+ if (link_recovery_autoretrain_allowed(link_training))
+ link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED;
+}
+
+/* Record that no more link fallback configuration is available. */
+static void
+link_recovery_mark_no_fallback(struct intel_dp_link_training *link_training)
+{
+ link_training->recovery_state = INTEL_DP_LINK_RECOVERY_NO_FALLBACK;
+}
+
+/**
+ * link_recovery_reset - reset the link recovery state
+ * @link_training: link training state
+ *
+ * Reset the link recovery state to indicate that no link recovery is
+ * required.
+ */
+static void link_recovery_reset(struct intel_dp_link_training *link_training)
+{
+ link_training->recovery_state = INTEL_DP_LINK_RECOVERY_IDLE;
+}
+
/**
* intel_dp_stop_link_train - stop link training
* @intel_dp: DP struct
@@ -1266,6 +1775,7 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp,
void intel_dp_stop_link_train(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
{
+ struct intel_dp_link_training *link_training = intel_dp->link.training;
struct intel_display *display = to_intel_display(intel_dp);
struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
int ret;
@@ -1286,8 +1796,8 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp,
intel_hpd_unblock(encoder);
if (!display->hotplug.ignore_long_hpd &&
- intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES) {
- int delay_ms = intel_dp->link.seq_train_failures ? 0 : 2000;
+ link_recovery_autoretrain_allowed(link_training)) {
+ int delay_ms = link_recovery_autoretrain_pending(link_training) ? 0 : 2000;
intel_encoder_link_check_queue_work(encoder, delay_ms);
}
@@ -1340,18 +1850,23 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state,
int *new_link_rate, int *new_lane_count)
{
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+ struct intel_dp_link_config forced_params;
int link_rate;
int lane_count;
int i;
- i = intel_dp_link_config_index(intel_dp, crtc_state->port_clock, crtc_state->lane_count);
+ 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, i, &link_rate, &lane_count);
+ intel_dp_link_config_get(intel_dp->link.caps, i, &link_rate, &lane_count);
- if ((intel_dp->link.force_rate &&
- intel_dp->link.force_rate != link_rate) ||
- (intel_dp->link.force_lane_count &&
- intel_dp->link.force_lane_count != lane_count))
+ if ((forced_params.rate &&
+ forced_params.rate != link_rate) ||
+ (forced_params.lane_count &&
+ forced_params.lane_count != lane_count))
continue;
break;
@@ -1368,20 +1883,22 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp,
static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate)
{
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+ struct intel_dp_link_config forced_params;
int rate_index;
int new_rate;
- if (intel_dp->link.force_rate)
+ intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
+ if (forced_params.rate)
return -1;
- rate_index = intel_dp_rate_index(intel_dp->common_rates,
- intel_dp->num_common_rates,
- current_rate);
+ rate_index = intel_dp_link_caps_common_rate_idx(link_caps,
+ current_rate);
if (rate_index <= 0)
return -1;
- new_rate = intel_dp_common_rate(intel_dp, rate_index - 1);
+ new_rate = intel_dp_common_rate(link_caps, rate_index - 1);
/* TODO: Make switching from UHBR to non-UHBR rates work. */
if (drm_dp_is_uhbr_rate(current_rate) != drm_dp_is_uhbr_rate(new_rate))
@@ -1392,7 +1909,10 @@ static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate)
static int reduce_lane_count(struct intel_dp *intel_dp, int current_lane_count)
{
- if (intel_dp->link.force_lane_count)
+ struct intel_dp_link_config forced_params;
+
+ intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params);
+ if (forced_params.lane_count)
return -1;
if (current_lane_count == 1)
@@ -1405,6 +1925,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state,
int *new_link_rate, int *new_lane_count)
{
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int link_rate;
int lane_count;
@@ -1412,7 +1933,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp,
link_rate = reduce_link_rate(intel_dp, crtc_state->port_clock);
if (link_rate < 0) {
lane_count = reduce_lane_count(intel_dp, crtc_state->lane_count);
- link_rate = intel_dp_max_common_rate(intel_dp);
+ link_rate = intel_dp_max_common_rate(link_caps);
}
if (lane_count < 0)
@@ -1439,6 +1960,8 @@ static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crt
static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
{
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+ struct intel_dp_link_config max_link_limits;
int new_link_rate;
int new_lane_count;
@@ -1464,8 +1987,11 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
crtc_state->lane_count, crtc_state->port_clock,
new_lane_count, new_link_rate);
- intel_dp->link.max_rate = new_link_rate;
- intel_dp->link.max_lane_count = new_lane_count;
+ max_link_limits.rate = new_link_rate;
+ max_link_limits.lane_count = new_lane_count;
+
+ /* TODO: handle an update failure */
+ intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits);
return 0;
}
@@ -1780,6 +2306,9 @@ void intel_dp_start_link_train(struct intel_atomic_state *state,
struct intel_display *display = to_intel_display(state);
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
struct intel_encoder *encoder = &dig_port->base;
+ struct intel_dp_link_training *link_training =
+ intel_dp->link.training;
+ bool autoretrain_allowed;
bool passed;
/*
* Reinit the LTTPRs here to ensure that they are switched to
@@ -1803,15 +2332,15 @@ void intel_dp_start_link_train(struct intel_atomic_state *state,
else
passed = intel_dp_link_train_all_phys(intel_dp, crtc_state, lttpr_count);
- if (intel_dp->link.force_train_failure) {
- intel_dp->link.force_train_failure--;
+ if (link_training->force_train_failure) {
+ link_training->force_train_failure--;
lt_dbg(intel_dp, DP_PHY_DPRX, "Forcing link training failure\n");
} else if (passed) {
- intel_dp->link.seq_train_failures = 0;
+ link_recovery_reset(link_training);
return;
}
- intel_dp->link.seq_train_failures++;
+ autoretrain_allowed = link_recovery_mark_train_failure(link_training);
/*
* Ignore the link failure in CI
@@ -1830,13 +2359,13 @@ void intel_dp_start_link_train(struct intel_atomic_state *state,
return;
}
- if (intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES)
+ if (autoretrain_allowed)
return;
if (intel_dp_schedule_fallback_link_training(state, intel_dp, crtc_state))
return;
- intel_dp->link.retrain_disabled = true;
+ link_recovery_mark_no_fallback(link_training);
if (!passed)
lt_err(intel_dp, DP_PHY_DPRX, "Can't reduce link training parameters after failure\n");
@@ -1864,256 +2393,307 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp,
lt_dbg(intel_dp, DP_PHY_DPRX, "DP2.0 SDP CRC16 for 128b/132b enabled\n");
}
-static int i915_dp_force_link_rate_show(struct seq_file *m, void *data)
+bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
+ u8 lane_count)
{
- struct intel_connector *connector = to_intel_connector(m->private);
- struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
- int current_rate = -1;
- int force_rate;
- int err;
- int i;
-
- err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
- if (err)
- return err;
-
- if (intel_dp->link.active)
- current_rate = intel_dp->link_rate;
- force_rate = intel_dp->link.force_rate;
-
- drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+ struct intel_dp_link_config max_link_limits;
- seq_printf(m, "%sauto%s",
- force_rate == 0 ? "[" : "",
- force_rate == 0 ? "]" : "");
+ /*
+ * FIXME: we need to synchronize the current link parameters with
+ * hardware readout. Currently fast link training doesn't work on
+ * boot-up.
+ *
+ * NOTE:
+ * This may be called from both serialized (locked and synced against
+ * async commit tails) and unserialized (e.g. HPD IRQ) contexts. It
+ * uses the current max link limits as upper bounds to reject
+ * obviously bogus values, even if those bounds may be observed in a
+ * transient or slightly stale state.
+ *
+ * This is not a full validation of the link configuration. Even in
+ * serialized contexts, additional constraints (e.g. source limitations,
+ * bandwidth checks, and other atomic state dependencies) are only
+ * verified during the atomic check of the subsequent commit.
+ *
+ * max_link_limits only provides independent upper bounds for rate and
+ * lane count. Callers must not assume it is itself an allowed link
+ * configuration. Although that happens to be true for now, it will
+ * stop being guaranteed once fallback depends only on disabled configs.
+ */
+ intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits);
- for (i = 0; i < intel_dp->num_source_rates; i++)
- seq_printf(m, " %s%d%s%s",
- intel_dp->source_rates[i] == force_rate ? "[" : "",
- intel_dp->source_rates[i],
- intel_dp->source_rates[i] == current_rate ? "*" : "",
- intel_dp->source_rates[i] == force_rate ? "]" : "");
+ if (link_rate == 0 ||
+ link_rate > max_link_limits.rate)
+ return false;
- seq_putc(m, '\n');
+ if (lane_count == 0 ||
+ lane_count > max_link_limits.lane_count)
+ return false;
- return 0;
+ return true;
}
-static int parse_link_rate(struct intel_dp *intel_dp, const char __user *ubuf, size_t len)
+static bool intel_dp_link_ok(struct intel_dp *intel_dp,
+ u8 link_status[DP_LINK_STATUS_SIZE])
{
- char *kbuf;
- const char *p;
- int rate;
- int ret = 0;
-
- kbuf = memdup_user_nul(ubuf, len);
- if (IS_ERR(kbuf))
- return PTR_ERR(kbuf);
+ struct intel_display *display = to_intel_display(intel_dp);
+ struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
+ bool uhbr = intel_dp->link_rate >= 1000000;
+ bool ok;
- p = strim(kbuf);
+ if (uhbr)
+ ok = drm_dp_128b132b_lane_channel_eq_done(link_status,
+ intel_dp->lane_count);
+ else
+ ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count);
- if (!strcmp(p, "auto")) {
- rate = 0;
- } else {
- ret = kstrtoint(p, 0, &rate);
- if (ret < 0)
- goto out_free;
-
- if (intel_dp_rate_index(intel_dp->source_rates,
- intel_dp->num_source_rates,
- rate) < 0)
- ret = -EINVAL;
- }
+ if (ok)
+ return true;
-out_free:
- kfree(kbuf);
+ intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status);
+ drm_dbg_kms(display->drm,
+ "[ENCODER:%d:%s] %s link not ok, retraining\n",
+ encoder->base.base.id, encoder->base.name,
+ uhbr ? "128b/132b" : "8b/10b");
- return ret < 0 ? ret : rate;
+ return false;
}
-static ssize_t i915_dp_force_link_rate_write(struct file *file,
- const char __user *ubuf,
- size_t len, loff_t *offp)
+static int
+intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE])
{
- struct seq_file *m = file->private_data;
- struct intel_connector *connector = to_intel_connector(m->private);
- struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
- int rate;
int err;
- rate = parse_link_rate(intel_dp, ubuf, len);
- if (rate < 0)
- return rate;
+ memset(link_status, 0, DP_LINK_STATUS_SIZE);
+
+ if (intel_dp_mst_active_streams(intel_dp) > 0)
+ err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI,
+ link_status, DP_LINK_STATUS_SIZE - 2);
+ else
+ err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX,
+ link_status);
- err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
- intel_dp_reset_link_params(intel_dp);
- intel_dp->link.force_rate = rate;
+ if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] &
+ DP_DOWNSTREAM_PORT_STATUS_CHANGED)
+ WRITE_ONCE(intel_dp->downstream_port_changed, true);
- drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+ return 0;
+}
- *offp += len;
+bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training)
+{
+ return link_training->force_retrain;
+}
- return len;
+static void intel_dp_link_training_set_force_retrain(struct intel_dp_link_training *link_training,
+ bool forced)
+{
+ link_training->force_retrain = forced;
}
-DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate);
-static int i915_dp_force_lane_count_show(struct seq_file *m, void *data)
+static bool
+intel_dp_needs_link_retrain(struct intel_dp *intel_dp)
{
- struct intel_connector *connector = to_intel_connector(m->private);
- struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
- int current_lane_count = -1;
- int force_lane_count;
- int err;
- int i;
+ struct intel_dp_link_training *link_training = intel_dp->link.training;
+ u8 link_status[DP_LINK_STATUS_SIZE];
- err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
- if (err)
- return err;
+ if (!intel_dp->link.active)
+ return false;
- if (intel_dp->link.active)
- current_lane_count = intel_dp->lane_count;
- force_lane_count = intel_dp->link.force_lane_count;
+ /*
+ * While PSR source HW is enabled, it will control main-link sending
+ * frames, enabling and disabling it so trying to do a retrain will fail
+ * as the link would or not be on or it could mix training patterns
+ * and frame data at the same time causing retrain to fail.
+ * Also when exiting PSR, HW will retrain the link anyways fixing
+ * any link status error.
+ */
+ if (intel_psr_enabled(intel_dp))
+ return false;
- drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+ if (intel_dp_link_training_get_force_retrain(link_training))
+ return true;
- seq_printf(m, "%sauto%s",
- force_lane_count == 0 ? "[" : "",
- force_lane_count == 0 ? "]" : "");
+ if (intel_dp_read_link_status(intel_dp, link_status) < 0)
+ return false;
+
+ /*
+ * Validate the cached values of intel_dp->link_rate and
+ * intel_dp->lane_count before attempting to retrain.
+ *
+ * FIXME would be nice to user the crtc state here, but since
+ * we need to call this from the short HPD handler that seems
+ * a bit hard.
+ */
+ if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate,
+ intel_dp->lane_count))
+ return false;
- for (i = 1; i <= 4; i <<= 1)
- seq_printf(m, " %s%d%s%s",
- i == force_lane_count ? "[" : "",
- i,
- i == current_lane_count ? "*" : "",
- i == force_lane_count ? "]" : "");
+ if (!link_recovery_autoretrain_allowed(link_training))
+ return false;
- seq_putc(m, '\n');
+ if (link_recovery_autoretrain_pending(link_training))
+ return true;
- return 0;
+ /* Retrain if link not ok */
+ return !intel_dp_link_ok(intel_dp, link_status) &&
+ !intel_psr_link_ok(intel_dp);
}
-static int parse_lane_count(const char __user *ubuf, size_t len)
+static bool intel_dp_is_connected(struct intel_dp *intel_dp)
{
- char *kbuf;
- const char *p;
- int lane_count;
- int ret = 0;
+ struct intel_connector *connector = intel_dp->attached_connector;
- kbuf = memdup_user_nul(ubuf, len);
- if (IS_ERR(kbuf))
- return PTR_ERR(kbuf);
-
- p = strim(kbuf);
+ return connector->base.status == connector_status_connected ||
+ intel_dp->is_mst;
+}
- if (!strcmp(p, "auto")) {
- lane_count = 0;
- } else {
- ret = kstrtoint(p, 0, &lane_count);
- if (ret < 0)
- goto out_free;
-
- switch (lane_count) {
- case 1:
- case 2:
- case 4:
- break;
- default:
- ret = -EINVAL;
- }
- }
+static void queue_modeset_retry_for_links_in_state(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
+ u8 pipe_mask)
+{
+ const struct intel_crtc_state *crtc_state;
+ struct intel_crtc *crtc;
-out_free:
- kfree(kbuf);
+ for_each_new_intel_crtc_in_state(state, crtc, crtc_state) {
+ if (!(BIT(crtc->pipe) & pipe_mask))
+ continue;
- return ret < 0 ? ret : lane_count;
+ intel_dp_queue_modeset_retry_for_link(state, encoder, crtc_state);
+ }
}
-static ssize_t i915_dp_force_lane_count_write(struct file *file,
- const char __user *ubuf,
- size_t len, loff_t *offp)
+static int intel_dp_retrain_link(struct intel_encoder *encoder,
+ struct drm_modeset_acquire_ctx *ctx)
{
- struct seq_file *m = file->private_data;
- struct intel_connector *connector = to_intel_connector(m->private);
- struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
- int lane_count;
- int err;
+ struct intel_display *display = to_intel_display(encoder);
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ struct intel_dp_link_training *link_training =
+ intel_dp->link.training;
+ struct intel_atomic_state *state;
+ struct drm_atomic_commit *_state;
+ u8 pipe_mask;
+ int ret;
- lane_count = parse_lane_count(ubuf, len);
- if (lane_count < 0)
- return lane_count;
+ if (!intel_dp_is_connected(intel_dp))
+ return 0;
- err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
- if (err)
- return err;
+ ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex,
+ ctx);
+ if (ret)
+ return ret;
- intel_dp_reset_link_params(intel_dp);
- intel_dp->link.force_lane_count = lane_count;
+ if (!intel_dp_needs_link_retrain(intel_dp))
+ return 0;
- drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+ ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask);
+ if (ret)
+ return ret;
- *offp += len;
+ if (pipe_mask == 0)
+ return 0;
- return len;
-}
-DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count);
+ if (!intel_dp_needs_link_retrain(intel_dp))
+ return 0;
-static int i915_dp_max_link_rate_show(void *data, u64 *val)
-{
- struct intel_connector *connector = to_intel_connector(data);
- struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
- int err;
+ drm_dbg_kms(display->drm,
+ "[ENCODER:%d:%s] retraining link (forced %s)\n",
+ encoder->base.base.id, encoder->base.name,
+ str_yes_no(intel_dp_link_training_get_force_retrain(link_training)));
- err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
- if (err)
- return err;
+ _state = drm_atomic_commit_alloc(display->drm);
+ if (!_state)
+ return -ENOMEM;
- *val = intel_dp->link.max_rate;
+ state = to_intel_atomic_state(_state);
- drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+ ret = intel_modeset_commit_pipes_for_atomic_state(state, pipe_mask, ctx);
+ if (ret == -EDEADLK)
+ goto out;
- return 0;
+ intel_dp_link_training_set_force_retrain(link_training, false);
+
+ if (ret) {
+ drm_dbg_kms(display->drm,
+ "[ENCODER:%d:%s] link retraining failed: %pe\n",
+ encoder->base.base.id, encoder->base.name,
+ ERR_PTR(ret));
+ /*
+ * intel_dp_needs_link_retrain() only performs a coarse check of
+ * retrainability, so the modeset commit may still fail. Disable
+ * further auto-retrain attempts in that case.
+ *
+ * A sink capability change may restore the retrainable state (see
+ * intel_dp_update_sink_caps(), intel_dp_reset_link_params()),
+ * allowing retraining to be attempted again.
+ */
+ link_recovery_mark_autoretrain_modeset_failure(link_training);
+ queue_modeset_retry_for_links_in_state(state, encoder, pipe_mask);
+ }
+out:
+ drm_atomic_commit_put(&state->base);
+
+ return ret;
}
-DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n");
-static int i915_dp_max_lane_count_show(void *data, u64 *val)
+void intel_dp_link_check(struct intel_encoder *encoder)
{
- struct intel_connector *connector = to_intel_connector(data);
- struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
- int err;
+ struct drm_modeset_acquire_ctx ctx;
+ int ret;
- err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
- if (err)
- return err;
+ intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret)
+ ret = intel_dp_retrain_link(encoder, &ctx);
+}
- *val = intel_dp->link.max_lane_count;
+void intel_dp_check_link_state(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ struct intel_encoder *encoder = &dig_port->base;
- drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
+ if (!intel_dp_is_connected(intel_dp))
+ return;
- return 0;
+ /*
+ * NOTE:
+ * This may race with an ongoing modeset updating the max link limits
+ * and, with that, the link's retrainability, so
+ * intel_dp_needs_link_retrain() may observe stale state.
+ *
+ * This is harmless: stale params captured as valid may spuriously
+ * allow retraining here, but the decision is rechecked later in a
+ * properly serialized context.
+ *
+ * Conversely, stale params captured as invalid may skip retraining,
+ * but that can only happen before the modeset has completed its own
+ * link training for the new, valid configuration, after which the
+ * link state is rechecked.
+ *
+ * See intel_dp_link_params_valid() for capturing and validating the
+ * params.
+ */
+ if (!intel_dp_needs_link_retrain(intel_dp))
+ return;
+
+ intel_encoder_link_check_queue_work(encoder, 0);
}
-DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n");
static int i915_dp_force_link_training_failure_show(void *data, u64 *val)
{
struct intel_connector *connector = to_intel_connector(data);
struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_training *link_training = connector_to_link_training(connector);
int err;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
- *val = intel_dp->link.force_train_failure;
+ intel_dp_flush_connector_commits(connector);
+
+ *val = link_training->force_train_failure;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
@@ -2124,7 +2704,7 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val)
{
struct intel_connector *connector = to_intel_connector(data);
struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_training *link_training = connector_to_link_training(connector);
int err;
if (val > 2)
@@ -2134,7 +2714,9 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val)
if (err)
return err;
- intel_dp->link.force_train_failure = val;
+ intel_dp_flush_connector_commits(connector);
+
+ link_training->force_train_failure = val;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
@@ -2148,14 +2730,16 @@ static int i915_dp_force_link_retrain_show(void *data, u64 *val)
{
struct intel_connector *connector = to_intel_connector(data);
struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_training *link_training = connector_to_link_training(connector);
int err;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
- *val = intel_dp->link.force_retrain;
+ intel_dp_flush_connector_commits(connector);
+
+ *val = intel_dp_link_training_get_force_retrain(link_training);
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
@@ -2166,14 +2750,17 @@ static int i915_dp_force_link_retrain_write(void *data, u64 val)
{
struct intel_connector *connector = to_intel_connector(data);
struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_training *link_training = connector_to_link_training(connector);
+ struct intel_dp *intel_dp = link_training->dp;
int err;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
- intel_dp->link.force_retrain = val;
+ intel_dp_flush_connector_commits(connector);
+
+ intel_dp_link_training_set_force_retrain(link_training, val);
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
@@ -2189,14 +2776,17 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data)
{
struct intel_connector *connector = to_intel_connector(m->private);
struct intel_display *display = to_intel_display(connector);
- struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_dp_link_training *link_training = connector_to_link_training(connector);
int err;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
- seq_printf(m, "%s\n", str_yes_no(intel_dp->link.retrain_disabled));
+ intel_dp_flush_connector_commits(connector);
+
+ /* TODO: Expose this via a debugfs entry reflecting what the state represents. */
+ seq_printf(m, "%s\n", str_yes_no(link_recovery_has_no_fallback(link_training)));
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
@@ -2212,18 +2802,6 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector)
connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
return;
- debugfs_create_file("i915_dp_force_link_rate", 0644, root,
- connector, &i915_dp_force_link_rate_fops);
-
- debugfs_create_file("i915_dp_force_lane_count", 0644, root,
- connector, &i915_dp_force_lane_count_fops);
-
- debugfs_create_file("i915_dp_max_link_rate", 0444, root,
- connector, &i915_dp_max_link_rate_fops);
-
- debugfs_create_file("i915_dp_max_lane_count", 0444, root,
- connector, &i915_dp_max_lane_count_fops);
-
debugfs_create_file("i915_dp_force_link_training_failure", 0644, root,
connector, &i915_dp_force_link_training_failure_fops);
@@ -2233,3 +2811,26 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector)
debugfs_create_file("i915_dp_link_retrain_disabled", 0444, root,
connector, &i915_dp_link_retrain_disabled_fops);
}
+
+void intel_dp_link_training_reset(struct intel_dp_link_training *link_training)
+{
+ link_recovery_reset(link_training);
+}
+
+struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp)
+{
+ struct intel_dp_link_training *link_training;
+
+ link_training = kzalloc_obj(*link_training);
+ if (!link_training)
+ return NULL;
+
+ link_training->dp = intel_dp;
+
+ return link_training;
+}
+
+void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training)
+{
+ kfree(link_training);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h
index 18c34c1a472f..ef16fcabd6da 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h
@@ -12,6 +12,8 @@ struct intel_atomic_state;
struct intel_connector;
struct intel_crtc_state;
struct intel_dp;
+struct intel_dp_link_training;
+struct intel_encoder;
int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_SIZE]);
int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp);
@@ -54,6 +56,19 @@ static inline u8 intel_dp_training_pattern_symbol(u8 pattern)
void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
+bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
+ u8 lane_count);
+
+bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training);
+
+void intel_dp_link_check(struct intel_encoder *encoder);
+void intel_dp_check_link_state(struct intel_dp *intel_dp);
+
void intel_dp_link_training_debugfs_add(struct intel_connector *connector);
+void intel_dp_link_training_reset(struct intel_dp_link_training *link_training);
+
+struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp);
+void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training);
+
#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 0aa3e6b4c781..ecc90e8faee1 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -697,12 +697,12 @@ static int mst_stream_compute_link_for_joined_pipes(struct intel_encoder *encode
return 0;
}
-static int mst_stream_compute_config(struct intel_encoder *encoder,
+static int mst_stream_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
- struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state);
struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
struct intel_dp *intel_dp = to_primary_dp(encoder);
struct intel_connector *connector =
@@ -925,11 +925,11 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
return 0;
}
-static int mst_stream_compute_config_late(struct intel_encoder *encoder,
+static int mst_stream_compute_config_late(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
- struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state);
struct intel_dp *intel_dp = to_primary_dp(encoder);
/* lowest numbered transcoder will be designated master */
diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.c b/drivers/gpu/drm/i915/display/intel_dp_test.c
index ba44769c9cfb..da7632536dac 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_test.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_test.c
@@ -14,6 +14,7 @@
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_dp.h"
+#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
@@ -32,6 +33,7 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *pipe_config,
struct link_config_limits *limits)
{
+ struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_display *display = to_intel_display(intel_dp);
/* For DP Compliance we override the computed bpp for the pipe */
@@ -54,9 +56,8 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp,
*/
if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate,
intel_dp->compliance.test_lane_count)) {
- index = intel_dp_rate_index(intel_dp->common_rates,
- intel_dp->num_common_rates,
- intel_dp->compliance.test_link_rate);
+ index = intel_dp_link_caps_common_rate_idx(link_caps,
+ intel_dp->compliance.test_link_rate);
if (index >= 0) {
limits->min_rate = intel_dp->compliance.test_link_rate;
limits->max_rate = intel_dp->compliance.test_link_rate;
diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c
index d6bd1f7e01e1..76e9753766b9 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c
@@ -11,6 +11,7 @@
#include "intel_display_limits.h"
#include "intel_display_types.h"
#include "intel_dp.h"
+#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_tunnel.h"
@@ -56,8 +57,9 @@ static int kbytes_to_mbits(int kbytes)
static int get_current_link_bw(struct intel_dp *intel_dp)
{
- int rate = intel_dp_max_common_rate(intel_dp);
- int lane_count = intel_dp_max_common_lane_count(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);
return intel_dp_max_link_data_rate(intel_dp, rate, lane_count);
}
diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c
index fec8a56e21ea..d9a270362a82 100644
--- a/drivers/gpu/drm/i915/display/intel_dsb.c
+++ b/drivers/gpu/drm/i915/display/intel_dsb.c
@@ -210,6 +210,18 @@ static int dsb_scanline_to_hw(struct intel_atomic_state *state,
return (scanline + vtotal - intel_crtc_scanline_offset(crtc_state)) % vtotal;
}
+static
+bool pre_commit_use_safe_window(struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_display *display = to_intel_display(crtc->base.dev);
+
+ if (intel_vrr_always_use_vrr_tg(display))
+ return true;
+
+ return pre_commit_is_vrr_active(state, crtc);
+}
+
/*
* Bspec suggests that we should always set DSB_SKIP_WAITS_EN. We have approach
* different from what is explained in Bspec on how flip is considered being
@@ -229,7 +241,7 @@ static u32 dsb_chicken(struct intel_atomic_state *state,
u32 chicken = intel_psr_use_trans_push(new_crtc_state) ?
DSB_SKIP_WAITS_EN : 0;
- if (pre_commit_is_vrr_active(state, crtc))
+ if (pre_commit_use_safe_window(state, crtc))
chicken |= DSB_CTRL_WAIT_SAFE_WINDOW |
DSB_CTRL_NO_WAIT_VBLANK |
DSB_INST_WAIT_SAFE_WINDOW |
@@ -798,6 +810,12 @@ void intel_dsb_vblank_evade(struct intel_atomic_state *state,
end = intel_vrr_vmax_vblank_start(crtc_state);
start = end - vblank_delay - latency;
intel_dsb_wait_scanline_out(state, dsb, start, end);
+ } else if (pre_commit_use_safe_window(state, crtc)) {
+ int vblank_delay = crtc_state->set_context_latency;
+
+ end = intel_mode_vblank_start(&crtc_state->hw.adjusted_mode);
+ start = end - vblank_delay - latency;
+ intel_dsb_wait_scanline_out(state, dsb, start, end);
} else {
int vblank_delay = intel_mode_vblank_delay(&crtc_state->hw.adjusted_mode);
@@ -891,7 +909,7 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
&crtc_state->hw.adjusted_mode;
int wait_scanlines;
- if (pre_commit_is_vrr_active(state, crtc)) {
+ if (pre_commit_use_safe_window(state, crtc)) {
/*
* If the push happened before the vmin decision boundary
* we don't know how far we are from the undelayed vblank.
@@ -902,9 +920,17 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
* the hardware itself guarantees that we're SCL lines
* away from the delayed vblank, and we won't be inside
* the vmin safe window so this extra wait does nothing.
+ *
+ * Experimentally, DSB may observe a slightly stale
+ * PIPEDSL value. When the actual scanline has just reached
+ * safe_window_start, WAIT_DSL_OUT may complete immediately
+ * due to the stale value.
+ *
+ * Shift the start back by one scanline to ensure the wait
+ * window is entered reliably.
*/
intel_dsb_wait_scanline_out(state, dsb,
- intel_vrr_safe_window_start(crtc_state),
+ intel_vrr_safe_window_start(crtc_state) - 1,
intel_vrr_vmin_safe_window_end(crtc_state));
/*
* When the push is sent during vblank it will trigger
diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c
index dd1a995c2979..f157699a7c4c 100644
--- a/drivers/gpu/drm/i915/display/intel_dvo.c
+++ b/drivers/gpu/drm/i915/display/intel_dvo.c
@@ -242,7 +242,8 @@ intel_dvo_mode_valid(struct drm_connector *_connector,
return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode);
}
-static int intel_dvo_compute_config(struct intel_encoder *encoder,
+static int intel_dvo_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -256,7 +257,7 @@ static int intel_dvo_compute_config(struct intel_encoder *encoder,
* with the panel scaling set up to source from the H/VDisplay
* of the original mode.
*/
- ret = intel_panel_compute_config(connector, adjusted_mode);
+ ret = intel_panel_compute_config(state, pipe_config, connector);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c
index 049157c41fe2..60a70dea5d85 100644
--- a/drivers/gpu/drm/i915/display/intel_gmbus.c
+++ b/drivers/gpu/drm/i915/display/intel_gmbus.c
@@ -54,21 +54,21 @@ struct intel_gmbus {
};
enum gmbus_gpio {
- GPIOA,
- GPIOB,
- GPIOC,
- GPIOD,
- GPIOE,
- GPIOF,
- GPIOG,
- GPIOH,
- __GPIOI_UNUSED,
- GPIOJ,
- GPIOK,
- GPIOL,
- GPIOM,
- GPION,
- GPIOO,
+ GPIO_0,
+ GPIO_1,
+ GPIO_2,
+ GPIO_3,
+ GPIO_4,
+ GPIO_5,
+ GPIO_6,
+ GPIO_7,
+ GPIO_8,
+ GPIO_9,
+ GPIO_10,
+ GPIO_11,
+ GPIO_12,
+ GPIO_13,
+ GPIO_14,
};
struct gmbus_pin {
@@ -78,77 +78,82 @@ struct gmbus_pin {
/* Map gmbus pin pairs to names and registers. */
static const struct gmbus_pin gmbus_pins[] = {
- [GMBUS_PIN_SSC] = { "ssc", GPIOB },
- [GMBUS_PIN_VGADDC] = { "vga", GPIOA },
- [GMBUS_PIN_PANEL] = { "panel", GPIOC },
- [GMBUS_PIN_DPC] = { "dpc", GPIOD },
- [GMBUS_PIN_DPB] = { "dpb", GPIOE },
- [GMBUS_PIN_DPD] = { "dpd", GPIOF },
+ [GMBUS_PIN_SSC] = { "ssc", GPIO_1 },
+ [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 },
+ [GMBUS_PIN_PANEL] = { "panel", GPIO_2 },
+ [GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
+ [GMBUS_PIN_DPD] = { "dpd", GPIO_5 },
};
-static const struct gmbus_pin gmbus_pins_bdw[] = {
- [GMBUS_PIN_VGADDC] = { "vga", GPIOA },
- [GMBUS_PIN_DPC] = { "dpc", GPIOD },
- [GMBUS_PIN_DPB] = { "dpb", GPIOE },
- [GMBUS_PIN_DPD] = { "dpd", GPIOF },
+static const struct gmbus_pin gmbus_pins_lpt_h[] = {
+ [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 },
+ [GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
+ [GMBUS_PIN_DPD] = { "dpd", GPIO_5 },
};
-static const struct gmbus_pin gmbus_pins_skl[] = {
- [GMBUS_PIN_DPC] = { "dpc", GPIOD },
- [GMBUS_PIN_DPB] = { "dpb", GPIOE },
- [GMBUS_PIN_DPD] = { "dpd", GPIOF },
+static const struct gmbus_pin gmbus_pins_lpt_lp[] = {
+ [GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
+};
+
+static const struct gmbus_pin gmbus_pins_spt[] = {
+ [GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
+ [GMBUS_PIN_DPD] = { "dpd", GPIO_5 },
};
static const struct gmbus_pin gmbus_pins_bxt[] = {
- [GMBUS_PIN_1_BXT] = { "dpb", GPIOB },
- [GMBUS_PIN_2_BXT] = { "dpc", GPIOC },
- [GMBUS_PIN_3_BXT] = { "misc", GPIOD },
+ [GMBUS_PIN_1] = { "dpb", GPIO_1 },
+ [GMBUS_PIN_2] = { "dpc", GPIO_2 },
+ [GMBUS_PIN_3] = { "misc", GPIO_3 },
};
static const struct gmbus_pin gmbus_pins_cnp[] = {
- [GMBUS_PIN_1_BXT] = { "dpb", GPIOB },
- [GMBUS_PIN_2_BXT] = { "dpc", GPIOC },
- [GMBUS_PIN_3_BXT] = { "misc", GPIOD },
- [GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
+ [GMBUS_PIN_1] = { "dpb", GPIO_1 },
+ [GMBUS_PIN_2] = { "dpc", GPIO_2 },
+ [GMBUS_PIN_3] = { "misc", GPIO_3 },
+ [GMBUS_PIN_4] = { "dpd", GPIO_4 },
};
static const struct gmbus_pin gmbus_pins_icp[] = {
- [GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
- [GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
- [GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
- [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ },
- [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK },
- [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL },
- [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM },
- [GMBUS_PIN_13_TC5_TGP] = { "tc5", GPION },
- [GMBUS_PIN_14_TC6_TGP] = { "tc6", GPIOO },
+ [GMBUS_PIN_1] = { "dpa", GPIO_1 },
+ [GMBUS_PIN_2] = { "dpb", GPIO_2 },
+ [GMBUS_PIN_3] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 },
+ [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 },
+ [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 },
+ [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 },
+ [GMBUS_PIN_13_TC5] = { "tc5", GPIO_13 },
+ [GMBUS_PIN_14_TC6] = { "tc6", GPIO_14 },
};
static const struct gmbus_pin gmbus_pins_dg1[] = {
- [GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
- [GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
- [GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
- [GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
+ [GMBUS_PIN_1] = { "dpa", GPIO_1 },
+ [GMBUS_PIN_2] = { "dpb", GPIO_2 },
+ [GMBUS_PIN_3] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_4] = { "dpd", GPIO_4 },
};
static const struct gmbus_pin gmbus_pins_dg2[] = {
- [GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
- [GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
- [GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
- [GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
- [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ },
+ [GMBUS_PIN_1] = { "dpa", GPIO_1 },
+ [GMBUS_PIN_2] = { "dpb", GPIO_2 },
+ [GMBUS_PIN_3] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_4] = { "dpd", GPIO_4 },
+ [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 },
};
static const struct gmbus_pin gmbus_pins_mtp[] = {
- [GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
- [GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
- [GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
- [GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
- [GMBUS_PIN_5_MTP] = { "dpe", GPIOF },
- [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ },
- [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK },
- [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL },
- [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM },
+ [GMBUS_PIN_1] = { "dpa", GPIO_1 },
+ [GMBUS_PIN_2] = { "dpb", GPIO_2 },
+ [GMBUS_PIN_3] = { "dpc", GPIO_3 },
+ [GMBUS_PIN_4] = { "dpd", GPIO_4 },
+ [GMBUS_PIN_5] = { "dpe", GPIO_5 },
+ [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 },
+ [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 },
+ [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 },
+ [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 },
};
static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display,
@@ -175,12 +180,15 @@ static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display,
} else if (display->platform.geminilake || display->platform.broxton) {
pins = gmbus_pins_bxt;
size = ARRAY_SIZE(gmbus_pins_bxt);
- } else if (DISPLAY_VER(display) == 9) {
- pins = gmbus_pins_skl;
- size = ARRAY_SIZE(gmbus_pins_skl);
- } else if (display->platform.broadwell) {
- pins = gmbus_pins_bdw;
- size = ARRAY_SIZE(gmbus_pins_bdw);
+ } else if (HAS_PCH_SPT(display)) {
+ pins = gmbus_pins_spt;
+ size = ARRAY_SIZE(gmbus_pins_spt);
+ } else if (HAS_PCH_LPT_LP(display)) {
+ pins = gmbus_pins_lpt_lp;
+ size = ARRAY_SIZE(gmbus_pins_lpt_lp);
+ } else if (HAS_PCH_LPT(display)) {
+ pins = gmbus_pins_lpt_h;
+ size = ARRAY_SIZE(gmbus_pins_lpt_h);
} else {
pins = gmbus_pins;
size = ARRAY_SIZE(gmbus_pins);
diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.h b/drivers/gpu/drm/i915/display/intel_gmbus.h
index 35a200a9efc0..5fdeab1aa794 100644
--- a/drivers/gpu/drm/i915/display/intel_gmbus.h
+++ b/drivers/gpu/drm/i915/display/intel_gmbus.h
@@ -20,17 +20,17 @@ struct intel_display;
#define GMBUS_PIN_DPB 5 /* SDVO, HDMIB */
#define GMBUS_PIN_DPD 6 /* HDMID */
#define GMBUS_PIN_RESERVED 7 /* 7 reserved */
-#define GMBUS_PIN_1_BXT 1 /* BXT+ (atom) and CNP+ (big core) */
-#define GMBUS_PIN_2_BXT 2
-#define GMBUS_PIN_3_BXT 3
-#define GMBUS_PIN_4_CNP 4
-#define GMBUS_PIN_5_MTP 5
-#define GMBUS_PIN_9_TC1_ICP 9
-#define GMBUS_PIN_10_TC2_ICP 10
-#define GMBUS_PIN_11_TC3_ICP 11
-#define GMBUS_PIN_12_TC4_ICP 12
-#define GMBUS_PIN_13_TC5_TGP 13
-#define GMBUS_PIN_14_TC6_TGP 14
+#define GMBUS_PIN_1 1 /* BXT+ (atom) and CNP+ (big core) */
+#define GMBUS_PIN_2 2
+#define GMBUS_PIN_3 3
+#define GMBUS_PIN_4 4
+#define GMBUS_PIN_5 5
+#define GMBUS_PIN_9_TC1 9 /* ICP+ */
+#define GMBUS_PIN_10_TC2 10
+#define GMBUS_PIN_11_TC3 11
+#define GMBUS_PIN_12_TC4 12
+#define GMBUS_PIN_13_TC5 13
+#define GMBUS_PIN_14_TC6 14
#define GMBUS_NUM_PINS 15 /* including 0 */
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 9076c2b176ec..8a019d3574df 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -71,7 +71,7 @@
bool intel_hdmi_is_frl(u32 clock)
{
- u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 };
+ static const u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 };
int i;
for (i = 0; i < ARRAY_SIZE(rates); i++)
@@ -2729,6 +2729,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_
}
/*
+ * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after
+ * enabling Scrambling_Enable and starting scrambled video
+ * transmission, poll Scrambling_Status for up to 200 ms.
+ */
+void
+intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
+ struct drm_connector *_connector)
+{
+ struct intel_connector *connector = to_intel_connector(_connector);
+ struct intel_display *display = to_intel_display(crtc_state);
+ bool scrambling_enabled = false;
+ int ret;
+
+ if (!crtc_state->hdmi_scrambling)
+ return;
+
+ /* Poll for a max of 200 msec as per HDMI spec */
+ ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base),
+ scrambling_enabled, 1000, 200 * 1000, false);
+ if (ret)
+ drm_dbg_kms(display->drm,
+ "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n",
+ connector->base.base.id, connector->base.name);
+}
+
+/*
* intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup
* @encoder: intel_encoder
* @connector: drm_connector
@@ -2799,14 +2825,14 @@ static u8 bxt_encoder_to_ddc_pin(struct intel_encoder *encoder)
switch (port) {
case PORT_B:
- ddc_pin = GMBUS_PIN_1_BXT;
+ ddc_pin = GMBUS_PIN_1;
break;
case PORT_C:
- ddc_pin = GMBUS_PIN_2_BXT;
+ ddc_pin = GMBUS_PIN_2;
break;
default:
MISSING_CASE(port);
- ddc_pin = GMBUS_PIN_1_BXT;
+ ddc_pin = GMBUS_PIN_1;
break;
}
return ddc_pin;
@@ -2819,20 +2845,17 @@ static u8 cnp_encoder_to_ddc_pin(struct intel_encoder *encoder)
switch (port) {
case PORT_B:
- ddc_pin = GMBUS_PIN_1_BXT;
+ ddc_pin = GMBUS_PIN_1;
break;
case PORT_C:
- ddc_pin = GMBUS_PIN_2_BXT;
+ ddc_pin = GMBUS_PIN_2;
break;
case PORT_D:
- ddc_pin = GMBUS_PIN_4_CNP;
- break;
- case PORT_F:
- ddc_pin = GMBUS_PIN_3_BXT;
+ ddc_pin = GMBUS_PIN_4;
break;
default:
MISSING_CASE(port);
- ddc_pin = GMBUS_PIN_1_BXT;
+ ddc_pin = GMBUS_PIN_1;
break;
}
return ddc_pin;
@@ -2844,12 +2867,12 @@ static u8 icl_encoder_to_ddc_pin(struct intel_encoder *encoder)
enum port port = encoder->port;
if (intel_encoder_is_combo(encoder))
- return GMBUS_PIN_1_BXT + port;
+ return GMBUS_PIN_1 + port;
else if (intel_encoder_is_tc(encoder))
- return GMBUS_PIN_9_TC1_ICP + intel_encoder_to_tc(encoder);
+ return GMBUS_PIN_9_TC1 + intel_encoder_to_tc(encoder);
drm_WARN(display->drm, 1, "Unknown port:%c\n", port_name(port));
- return GMBUS_PIN_2_BXT;
+ return GMBUS_PIN_2;
}
static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder)
@@ -2859,17 +2882,17 @@ static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder)
switch (phy) {
case PHY_A:
- ddc_pin = GMBUS_PIN_1_BXT;
+ ddc_pin = GMBUS_PIN_1;
break;
case PHY_B:
- ddc_pin = GMBUS_PIN_2_BXT;
+ ddc_pin = GMBUS_PIN_2;
break;
case PHY_C:
- ddc_pin = GMBUS_PIN_9_TC1_ICP;
+ ddc_pin = GMBUS_PIN_9_TC1;
break;
default:
MISSING_CASE(phy);
- ddc_pin = GMBUS_PIN_1_BXT;
+ ddc_pin = GMBUS_PIN_1;
break;
}
return ddc_pin;
@@ -2889,9 +2912,9 @@ static u8 rkl_encoder_to_ddc_pin(struct intel_encoder *encoder)
* all outputs.
*/
if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C)
- return GMBUS_PIN_9_TC1_ICP + phy - PHY_C;
+ return GMBUS_PIN_9_TC1 + phy - PHY_C;
- return GMBUS_PIN_1_BXT + phy;
+ return GMBUS_PIN_1 + phy;
}
static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder)
@@ -2908,9 +2931,9 @@ static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder)
* all outputs.
*/
if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C)
- return GMBUS_PIN_9_TC1_ICP + phy - PHY_C;
+ return GMBUS_PIN_9_TC1 + phy - PHY_C;
- return GMBUS_PIN_1_BXT + phy;
+ return GMBUS_PIN_1 + phy;
}
static u8 dg1_encoder_to_ddc_pin(struct intel_encoder *encoder)
@@ -2929,9 +2952,9 @@ static u8 adls_encoder_to_ddc_pin(struct intel_encoder *encoder)
* except first combo output.
*/
if (phy == PHY_A)
- return GMBUS_PIN_1_BXT;
+ return GMBUS_PIN_1;
- return GMBUS_PIN_9_TC1_ICP + phy - PHY_B;
+ return GMBUS_PIN_9_TC1 + phy - PHY_B;
}
static u8 g4x_encoder_to_ddc_pin(struct intel_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
index be2fad57e4ad..0fa3661568e8 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
@@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
unsigned int type,
void *frame, ssize_t len);
+void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
+ struct drm_connector *_connector);
#endif /* __INTEL_HDMI_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_initial_plane.c b/drivers/gpu/drm/i915/display/intel_initial_plane.c
index 6aa253678ec9..e414b5d1085c 100644
--- a/drivers/gpu/drm/i915/display/intel_initial_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_initial_plane.c
@@ -170,7 +170,7 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
drm_framebuffer_get(fb);
plane_state->uapi.crtc = &crtc->base;
- intel_plane_copy_uapi_to_hw_state(plane_state, plane_state, crtc);
+ intel_plane_copy_uapi_to_hw_state(NULL, plane_state, plane_state, crtc);
atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits);
diff --git a/drivers/gpu/drm/i915/display/intel_lpe_audio.c b/drivers/gpu/drm/i915/display/intel_lpe_audio.c
index 775493306a83..ff2cf479d8e1 100644
--- a/drivers/gpu/drm/i915/display/intel_lpe_audio.c
+++ b/drivers/gpu/drm/i915/display/intel_lpe_audio.c
@@ -262,10 +262,10 @@ void intel_lpe_audio_irq_handler(struct intel_display *display)
if (!HAS_LPE_AUDIO(display))
return;
- ret = generic_handle_irq(display->audio.lpe.irq);
+ ret = generic_handle_irq_safe(display->audio.lpe.irq);
if (ret)
drm_err_ratelimited(display->drm,
- "error handling LPE audio irq: %d\n", ret);
+ "LPE audio: irq handling failed (%pe)\n", ERR_PTR(ret));
}
/**
diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c
index 615ee980470e..8fc6d230493f 100644
--- a/drivers/gpu/drm/i915/display/intel_lt_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c
@@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder,
else
val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK);
- /* DP2.0 10G and 20G rates enable MPLLA*/
- if (port_clock == 1000000 || port_clock == 2000000)
- val |= XELPDP_SSC_ENABLE_PLLA;
- else
- val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0;
+ val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0;
intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE |
@@ -2178,8 +2174,9 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p,
{
int i, j;
- drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %d, tbt mode: %d\n",
- hw_state->lane_count, hw_state->ssc_enabled, hw_state->tbt_mode);
+ drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %s, tbt mode: %s\n",
+ hw_state->lane_count, str_yes_no(hw_state->ssc_enabled),
+ str_yes_no(hw_state->tbt_mode));
for (i = 0; i < 3; i++) {
drm_printf(p, "config[%d] = 0x%.4x,\n",
@@ -2221,6 +2218,14 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder)
XELPDP_LANE_PCLK_PLL_ACK(0);
}
+static bool readout_ssc_state(struct intel_encoder *encoder)
+{
+ struct intel_display *display = to_intel_display(encoder);
+
+ return intel_de_read(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port)) &
+ XELPDP_SSC_ENABLE_PLLA;
+}
+
bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display,
struct intel_dpll *pll,
struct intel_dpll_hw_state *hw_state)
@@ -2250,6 +2255,7 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder,
owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1;
wakeref = intel_lt_phy_transaction_begin(encoder);
+ pll_state->ssc_enabled = readout_ssc_state(encoder);
pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_LT_PHY_LANE0,
INTEL_LT_PHY_LANE1);
diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
index c8098104d853..872753478cf2 100644
--- a/drivers/gpu/drm/i915/display/intel_lvds.c
+++ b/drivers/gpu/drm/i915/display/intel_lvds.c
@@ -413,7 +413,8 @@ intel_lvds_mode_valid(struct drm_connector *_connector,
return MODE_OK;
}
-static int intel_lvds_compute_config(struct intel_encoder *encoder,
+static int intel_lvds_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -459,7 +460,7 @@ static int intel_lvds_compute_config(struct intel_encoder *encoder,
* with the panel scaling set up to source from the H/VDisplay
* of the original mode.
*/
- ret = intel_panel_compute_config(connector, adjusted_mode);
+ ret = intel_panel_compute_config(state, crtc_state, connector);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c
index 20c548eea6da..81e638d0c7b3 100644
--- a/drivers/gpu/drm/i915/display/intel_panel.c
+++ b/drivers/gpu/drm/i915/display/intel_panel.c
@@ -67,21 +67,62 @@ static bool is_best_fixed_mode(struct intel_connector *connector,
if (!best_mode)
return true;
- /*
- * With VRR always pick a mode with equal/higher than requested
- * vrefresh, which we can then reduce to match the requested
- * vrefresh by extending the vblank length.
- */
- if (intel_vrr_is_in_range(connector, vrefresh) &&
- intel_vrr_is_in_range(connector, fixed_mode_vrefresh) &&
- fixed_mode_vrefresh < vrefresh)
- return false;
-
/* pick the fixed_mode that is closest in terms of vrefresh */
return abs(fixed_mode_vrefresh - vrefresh) <
abs(drm_mode_vrefresh(best_mode) - vrefresh);
}
+static bool is_vrr_compatible(const struct drm_display_mode *mode1,
+ const struct drm_display_mode *mode2)
+{
+ return drm_mode_match(mode1, mode2,
+ DRM_MODE_MATCH_CLOCK |
+ DRM_MODE_MATCH_TIMINGS_VRR |
+ DRM_MODE_MATCH_FLAGS |
+ DRM_MODE_MATCH_3D_FLAGS);
+}
+
+static const struct drm_display_mode *
+intel_panel_fixed_mode_vrr(struct intel_connector *connector,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *vrr_ref_mode)
+{
+ const struct drm_display_mode *fixed_mode, *best_mode = NULL;
+ int vrefresh = drm_mode_vrefresh(mode);
+
+ if (!intel_vrr_is_in_range(connector, vrefresh))
+ return NULL;
+
+ if (vrr_ref_mode &&
+ !intel_vrr_is_in_range(connector, drm_mode_vrefresh(vrr_ref_mode)))
+ return NULL;
+
+ list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
+ int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
+
+ if (!intel_vrr_is_in_range(connector, fixed_mode_vrefresh))
+ continue;
+
+ /*
+ * With VRR always pick a mode with equal/higher than requested
+ * vrefresh, which we can then reduce to match the requested
+ * vrefresh by extending the vblank length.
+ */
+ if (fixed_mode_vrefresh < vrefresh)
+ continue;
+
+ if (vrr_ref_mode &&
+ !is_vrr_compatible(fixed_mode, vrr_ref_mode))
+ continue;
+
+ if (is_best_fixed_mode(connector, vrefresh,
+ fixed_mode_vrefresh, best_mode))
+ best_mode = fixed_mode;
+ }
+
+ return best_mode;
+}
+
const struct drm_display_mode *
intel_panel_fixed_mode(struct intel_connector *connector,
const struct drm_display_mode *mode)
@@ -197,56 +238,107 @@ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector)
return connector->panel.vbt.drrs_type;
}
-int intel_panel_compute_config(struct intel_connector *connector,
- struct drm_display_mode *adjusted_mode)
+static int intel_panel_compute_config_vrr(struct intel_atomic_state *state,
+ struct intel_crtc_state *crtc_state,
+ struct intel_connector *connector)
{
- const struct drm_display_mode *fixed_mode =
- intel_panel_fixed_mode(connector, adjusted_mode);
+ struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
+ const struct drm_display_mode *fixed_mode = NULL;
int vrefresh, fixed_mode_vrefresh;
- bool is_vrr;
+
+ /*
+ * Attempt a VRR based refresh rate change if possible
+ * when userspace has forbidden a full modeset.
+ */
+ if (!state->base.allow_modeset) {
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ const struct intel_crtc_state *old_crtc_state =
+ intel_atomic_get_old_crtc_state(state, crtc);
+
+ if (old_crtc_state->hw.enable &&
+ old_crtc_state->uapi.encoder_mask == crtc_state->uapi.encoder_mask)
+ fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode,
+ &old_crtc_state->hw.adjusted_mode);
+ }
if (!fixed_mode)
+ fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, NULL);
+
+ if (!fixed_mode)
+ return -EINVAL;
+
+ vrefresh = drm_mode_vrefresh(adjusted_mode);
+ fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
+
+ drm_mode_copy(adjusted_mode, fixed_mode);
+
+ if (fixed_mode_vrefresh != vrefresh) {
+ int vsync_start_offset = adjusted_mode->vtotal - adjusted_mode->vsync_start;
+ int vsync_end_offset = adjusted_mode->vtotal - adjusted_mode->vsync_end;
+
+ adjusted_mode->vtotal =
+ DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000,
+ adjusted_mode->htotal * vrefresh);
+
+ adjusted_mode->vsync_start = adjusted_mode->vtotal - vsync_start_offset;
+ adjusted_mode->vsync_end = adjusted_mode->vtotal - vsync_end_offset;
+ }
+
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+ return 0;
+}
+
+static int intel_panel_compute_config_fixed_rr(struct intel_atomic_state *state,
+ struct intel_crtc_state *crtc_state,
+ struct intel_connector *connector)
+{
+ struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
+ const struct drm_display_mode *fixed_mode;
+ int vrefresh, fixed_mode_vrefresh;
+
+ fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode);
+ if (!fixed_mode)
return 0;
vrefresh = drm_mode_vrefresh(adjusted_mode);
fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
/*
- * Assume that we shouldn't muck about with the
- * timings if they don't land in the VRR range.
+ * We don't want to lie too much to the user about the refresh
+ * rate they're going to get. But we have to allow a bit of latitude
+ * for Xorg since it likes to automagically cook up modes with slightly
+ * off refresh rates.
*/
- is_vrr = intel_vrr_is_in_range(connector, vrefresh) &&
- intel_vrr_is_in_range(connector, fixed_mode_vrefresh);
-
- if (!is_vrr) {
- /*
- * We don't want to lie too much to the user about the refresh
- * rate they're going to get. But we have to allow a bit of latitude
- * for Xorg since it likes to automagically cook up modes with slightly
- * off refresh rates.
- */
- if (abs(vrefresh - fixed_mode_vrefresh) > 1) {
- drm_dbg_kms(connector->base.dev,
- "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n",
- connector->base.base.id, connector->base.name,
- vrefresh, fixed_mode_vrefresh);
+ if (abs(vrefresh - fixed_mode_vrefresh) > 1) {
+ drm_dbg_kms(connector->base.dev,
+ "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n",
+ connector->base.base.id, connector->base.name,
+ vrefresh, fixed_mode_vrefresh);
- return -EINVAL;
- }
+ return -EINVAL;
}
drm_mode_copy(adjusted_mode, fixed_mode);
- if (is_vrr && fixed_mode_vrefresh != vrefresh)
- adjusted_mode->vtotal =
- DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000,
- adjusted_mode->htotal * vrefresh);
-
drm_mode_set_crtcinfo(adjusted_mode, 0);
return 0;
}
+int intel_panel_compute_config(struct intel_atomic_state *state,
+ struct intel_crtc_state *crtc_state,
+ struct intel_connector *connector)
+{
+ int ret;
+
+ ret = intel_panel_compute_config_vrr(state, crtc_state, connector);
+ if (ret)
+ ret = intel_panel_compute_config_fixed_rr(state, crtc_state, connector);
+
+ return ret;
+}
+
static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector)
{
struct intel_display *display = to_intel_display(connector);
diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h
index 23bd227826c9..30c6078ecb1b 100644
--- a/drivers/gpu/drm/i915/display/intel_panel.h
+++ b/drivers/gpu/drm/i915/display/intel_panel.h
@@ -14,6 +14,7 @@ struct drm_connector;
struct drm_connector_state;
struct drm_display_mode;
struct drm_edid;
+struct intel_atomic_state;
struct intel_connector;
struct intel_crtc_state;
struct intel_display;
@@ -45,8 +46,9 @@ enum drm_mode_status
intel_panel_mode_valid(struct intel_connector *connector,
const struct drm_display_mode *mode,
int *target_clock);
-int intel_panel_compute_config(struct intel_connector *connector,
- struct drm_display_mode *adjusted_mode);
+int intel_panel_compute_config(struct intel_atomic_state *state,
+ struct intel_crtc_state *crtc_state,
+ struct intel_connector *connector);
void intel_panel_add_edid_fixed_modes(struct intel_connector *connector,
bool use_alt_fixed_modes);
void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector);
diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c
index a5816561be40..a5e41ea66921 100644
--- a/drivers/gpu/drm/i915/display/intel_parent.c
+++ b/drivers/gpu/drm/i915/display/intel_parent.c
@@ -251,9 +251,11 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display)
return display->parent->panic->alloc();
}
-int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb)
+int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic,
+ struct drm_scanout_buffer *sb, struct drm_gem_object *obj,
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width))
{
- return display->parent->panic->setup(panic, sb);
+ return display->parent->panic->setup(panic, sb, obj, tiling);
}
void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic)
diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h
index 27e35f891a6b..595d4148b8eb 100644
--- a/drivers/gpu/drm/i915/display/intel_parent.h
+++ b/drivers/gpu/drm/i915/display/intel_parent.h
@@ -105,7 +105,9 @@ void intel_parent_overlay_cleanup(struct intel_display *display);
/* panic */
struct intel_panic *intel_parent_panic_alloc(struct intel_display *display);
-int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb);
+int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic,
+ struct drm_scanout_buffer *sb, struct drm_gem_object *obj,
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width));
void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic);
/* pc8 */
diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c
index 7017ed6cb9d1..a440f92ff00c 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_plane.c
@@ -408,25 +408,27 @@ intel_plane_colorop_replace_blob(struct intel_plane_state *plane_state,
}
static void
-intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
+intel_plane_color_copy_uapi_to_hw_state(struct intel_atomic_state *state,
+ struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state,
struct intel_crtc *crtc)
{
struct drm_colorop *iter_colorop, *colorop;
struct drm_colorop_state *new_colorop_state;
- struct drm_atomic_commit *state = plane_state->uapi.state;
struct intel_colorop *intel_colorop;
struct drm_property_blob *blob;
- struct intel_atomic_state *intel_atomic_state = to_intel_atomic_state(state);
- struct intel_crtc_state *new_crtc_state = intel_atomic_state ?
- intel_atomic_get_new_crtc_state(intel_atomic_state, crtc) : NULL;
+ struct intel_crtc_state *new_crtc_state = state ?
+ intel_atomic_get_new_crtc_state(state, crtc) : NULL;
bool changed = false;
int i = 0;
+ if (!state)
+ return;
+
iter_colorop = from_plane_state->uapi.color_pipeline;
while (iter_colorop) {
- for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ for_each_new_colorop_in_state(&state->base, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == iter_colorop) {
blob = new_colorop_state->bypass ? NULL : new_colorop_state->data;
intel_colorop = to_intel_colorop(colorop);
@@ -442,7 +444,8 @@ intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
new_crtc_state->plane_color_changed = true;
}
-void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
+void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state,
+ struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state,
struct intel_crtc *crtc)
{
@@ -471,19 +474,25 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi);
plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi);
- intel_plane_color_copy_uapi_to_hw_state(plane_state, from_plane_state, crtc);
+ intel_plane_color_copy_uapi_to_hw_state(state, plane_state, from_plane_state, crtc);
}
-void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
- const struct intel_plane_state *from_plane_state)
+static void intel_plane_y_copy_hw_state(struct intel_plane_state *y_plane_state,
+ const struct intel_plane_state *uv_plane_state)
{
- intel_plane_clear_hw_state(plane_state);
+ intel_plane_clear_hw_state(y_plane_state);
- memcpy(&plane_state->hw, &from_plane_state->hw,
- sizeof(plane_state->hw));
+ y_plane_state->hw.crtc = uv_plane_state->hw.crtc;
+ y_plane_state->hw.fb = uv_plane_state->hw.fb;
+ if (y_plane_state->hw.fb)
+ drm_framebuffer_get(y_plane_state->hw.fb);
- if (plane_state->hw.fb)
- drm_framebuffer_get(plane_state->hw.fb);
+ y_plane_state->hw.alpha = uv_plane_state->hw.alpha;
+ y_plane_state->hw.pixel_blend_mode = uv_plane_state->hw.pixel_blend_mode;
+ y_plane_state->hw.rotation = uv_plane_state->hw.rotation;
+ y_plane_state->hw.color_encoding = uv_plane_state->hw.color_encoding;
+ y_plane_state->hw.color_range = uv_plane_state->hw.color_range;
+ y_plane_state->hw.scaling_filter = uv_plane_state->hw.scaling_filter;
}
static void unlink_nv12_plane(struct intel_crtc_state *crtc_state,
@@ -868,7 +877,8 @@ static int plane_atomic_check(struct intel_atomic_state *state,
old_primary_crtc_plane_state,
new_primary_crtc_plane_state);
- intel_plane_copy_uapi_to_hw_state(new_plane_state,
+ intel_plane_copy_uapi_to_hw_state(state,
+ new_plane_state,
new_primary_crtc_plane_state,
crtc);
@@ -1607,17 +1617,17 @@ static int intel_get_scanout_buffer(struct drm_plane *plane,
if (fb == intel_fbdev_framebuffer(display->fbdev.fbdev)) {
intel_fbdev_get_map(display, &sb->map[0]);
} else {
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width) = NULL;
int ret;
/* Can't disable tiling if DPT is in use */
if (intel_fb_uses_dpt(&fb->base)) {
if (fb->base.format->cpp[0] != 4)
return -EOPNOTSUPP;
- fb->panic_tiling = intel_get_tiling_func(fb->base.modifier);
- if (!fb->panic_tiling)
+ tiling = intel_get_tiling_func(fb->base.modifier);
+ if (!tiling)
return -EOPNOTSUPP;
}
- sb->private = fb;
- ret = intel_parent_panic_setup(display, fb->panic, sb);
+ ret = intel_parent_panic_setup(display, fb->panic, sb, obj, tiling);
if (ret)
return ret;
}
@@ -1688,7 +1698,7 @@ static void link_nv12_planes(struct intel_crtc_state *crtc_state,
crtc_state->rel_data_rate[y_plane->id] = crtc_state->rel_data_rate_y[uv_plane->id];
/* Copy parameters to Y plane */
- intel_plane_copy_hw_state(y_plane_state, uv_plane_state);
+ intel_plane_y_copy_hw_state(y_plane_state, uv_plane_state);
y_plane_state->uapi.src = uv_plane_state->uapi.src;
y_plane_state->uapi.dst = uv_plane_state->uapi.dst;
diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h
index a6338bba72d9..31a6229aea73 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.h
+++ b/drivers/gpu/drm/i915/display/intel_plane.h
@@ -35,11 +35,10 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state,
unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state,
int color_plane);
-void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
+void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state,
+ struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state,
struct intel_crtc *crtc);
-void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
- const struct intel_plane_state *from_plane_state);
void intel_plane_async_flip(struct intel_dsb *dsb,
struct intel_plane *plane,
const struct intel_crtc_state *crtc_state,
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index e138982dc91f..92af21d823a3 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -88,22 +88,6 @@
* issues the self-refresh re-enable code is done from a work queue, which
* must be correctly synchronized/cancelled when shutting down the pipe."
*
- * DC3CO (DC3 clock off)
- *
- * On top of PSR2, GEN12 adds a intermediate power savings state that turns
- * clock off automatically during PSR2 idle state.
- * The smaller overhead of DC3co entry/exit vs. the overhead of PSR2 deep sleep
- * entry/exit allows the HW to enter a low-power state even when page flipping
- * periodically (for instance a 30fps video playback scenario).
- *
- * Every time a flips occurs PSR2 will get out of deep sleep state(if it was),
- * so DC3CO is enabled and tgl_dc3co_disable_work is schedule to run after 6
- * frames, if no other flip occurs and the function above is executed, DC3CO is
- * disabled and PSR2 is configured to enter deep sleep, resetting again in case
- * of another flip.
- * Front buffer modifications do not trigger DC3CO activation on purpose as it
- * would bring a lot of complexity and most of the moderns systems will only
- * use page flips.
*/
/*
@@ -1098,10 +1082,11 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp)
u32 psr_val = 0;
u8 idle_frames;
- /* Wa_16025596647 */
- if ((DISPLAY_VER(display) == 20 ||
- IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) &&
- is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used)
+ /* DC3CO / Wa_16025596647 */
+ if (intel_dp->psr.dc3co_allowed ||
+ ((DISPLAY_VER(display) == 20 ||
+ IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) &&
+ is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used))
idle_frames = 0;
else
idle_frames = psr_compute_idle_frames(intel_dp);
@@ -1220,108 +1205,6 @@ static void psr2_program_idle_frames(struct intel_dp *intel_dp,
EDP_PSR2_IDLE_FRAMES(idle_frames));
}
-static void tgl_psr2_enable_dc3co(struct intel_dp *intel_dp)
-{
- struct intel_display *display = to_intel_display(intel_dp);
-
- psr2_program_idle_frames(intel_dp, 0);
- intel_display_power_set_target_dc_state(display, DC_STATE_EN_DC3CO);
-}
-
-static void tgl_psr2_disable_dc3co(struct intel_dp *intel_dp)
-{
- struct intel_display *display = to_intel_display(intel_dp);
-
- intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
- psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp));
-}
-
-static void tgl_dc3co_disable_work(struct work_struct *work)
-{
- struct intel_dp *intel_dp =
- container_of(work, typeof(*intel_dp), psr.dc3co_work.work);
-
- mutex_lock(&intel_dp->psr.lock);
- /* If delayed work is pending, it is not idle */
- if (delayed_work_pending(&intel_dp->psr.dc3co_work))
- goto unlock;
-
- tgl_psr2_disable_dc3co(intel_dp);
-unlock:
- mutex_unlock(&intel_dp->psr.lock);
-}
-
-static void tgl_disallow_dc3co_on_psr2_exit(struct intel_dp *intel_dp)
-{
- if (!intel_dp->psr.dc3co_exitline)
- return;
-
- cancel_delayed_work(&intel_dp->psr.dc3co_work);
- /* Before PSR2 exit disallow dc3co*/
- tgl_psr2_disable_dc3co(intel_dp);
-}
-
-static bool
-dc3co_is_pipe_port_compatible(struct intel_dp *intel_dp,
- struct intel_crtc_state *crtc_state)
-{
- struct intel_display *display = to_intel_display(intel_dp);
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
- enum port port = dig_port->base.port;
-
- if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14)
- return pipe <= PIPE_B && port <= PORT_B;
- else
- return pipe == PIPE_A && port == PORT_A;
-}
-
-static void
-tgl_dc3co_exitline_compute_config(struct intel_dp *intel_dp,
- struct intel_crtc_state *crtc_state)
-{
- struct intel_display *display = to_intel_display(intel_dp);
- const u32 crtc_vdisplay = crtc_state->uapi.adjusted_mode.crtc_vdisplay;
- struct i915_power_domains *power_domains = &display->power.domains;
- u32 exit_scanlines;
-
- /*
- * FIXME: Due to the changed sequence of activating/deactivating DC3CO,
- * disable DC3CO until the changed dc3co activating/deactivating sequence
- * is applied. B.Specs:49196
- */
- return;
-
- /*
- * DMC's DC3CO exit mechanism has an issue with Selective Fecth
- * TODO: when the issue is addressed, this restriction should be removed.
- */
- if (crtc_state->enable_psr2_sel_fetch)
- return;
-
- if (!(power_domains->allowed_dc_mask & DC_STATE_EN_DC3CO))
- return;
-
- if (!dc3co_is_pipe_port_compatible(intel_dp, crtc_state))
- return;
-
- /* Wa_16011303918:adl-p */
- if (intel_display_wa(display, INTEL_DISPLAY_WA_16011303918))
- return;
-
- /*
- * DC3CO Exit time 200us B.Spec 49196
- * PSR2 transcoder Early Exit scanlines = ROUNDUP(200 / line time) + 1
- */
- exit_scanlines =
- intel_usecs_to_scanlines(&crtc_state->uapi.adjusted_mode, 200) + 1;
-
- if (drm_WARN_ON(display->drm, exit_scanlines > crtc_vdisplay))
- return;
-
- crtc_state->dc3co_exitline = crtc_vdisplay - exit_scanlines;
-}
-
static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state)
{
@@ -1522,9 +1405,6 @@ int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state
needs_panel_replay)
return 0;
- if (intel_vrr_always_use_vrr_tg(display))
- return 0;
-
return 1;
}
@@ -1697,8 +1577,6 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
return false;
}
- tgl_dc3co_exitline_compute_config(intel_dp, crtc_state);
-
return true;
}
@@ -1893,6 +1771,50 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp,
!crtc_state->has_sel_update);
}
+static void psr2_dc3co_disable_locked(struct intel_dp *intel_dp)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+
+ if (intel_dp->psr.dc3co_allowed) {
+ intel_dp->psr.dc3co_allowed = false;
+ intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
+ psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp));
+ }
+}
+
+static void psr2_dc3co_disable_work(struct work_struct *work)
+{
+ struct intel_dp *intel_dp =
+ container_of(work, typeof(*intel_dp), psr.dc3co_work.work);
+
+ mutex_lock(&intel_dp->psr.lock);
+ psr2_dc3co_disable_locked(intel_dp);
+ mutex_unlock(&intel_dp->psr.lock);
+}
+
+static void
+psr2_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+
+ if (!intel_dp->psr.dc3co_allowed)
+ return;
+
+ if (!intel_dp->psr.sel_update_enabled ||
+ !intel_dp->psr.active)
+ return;
+ /*
+ * At every frontbuffer flush flip event modified delay of delayed work,
+ * when delayed work schedules that means display has been idle.
+ */
+ if (!(frontbuffer_bits &
+ INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe)))
+ return;
+
+ mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
+ intel_dp->psr.dc3co_exit_delay);
+}
+
static
void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state)
@@ -2013,12 +1935,6 @@ void intel_psr_get_config(struct intel_encoder *encoder,
}
pipe_config->enable_psr2_su_region_et = intel_dp->psr.su_region_et_enabled;
-
- if (DISPLAY_VER(display) >= 12) {
- val = intel_de_read(display,
- TRANS_EXITLINE(display, cpu_transcoder));
- pipe_config->dc3co_exitline = REG_FIELD_GET(EXITLINE_MASK, val);
- }
unlock:
mutex_unlock(&intel_dp->psr.lock);
}
@@ -2146,16 +2062,6 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp,
psr_irq_control(intel_dp);
- /*
- * TODO: if future platforms supports DC3CO in more than one
- * transcoder, EXITLINE will need to be unset when disabling PSR
- */
- if (intel_dp->psr.dc3co_exitline)
- intel_de_rmw(display,
- TRANS_EXITLINE(display, cpu_transcoder),
- EXITLINE_MASK,
- intel_dp->psr.dc3co_exitline << EXITLINE_SHIFT | EXITLINE_ENABLE);
-
if (HAS_PSR_HW_TRACKING(display) && HAS_PSR2_SEL_FETCH(display))
intel_de_rmw(display, CHICKEN_PAR1_1, IGNORE_PSR2_HW_TRACKING,
intel_dp->psr.psr2_sel_fetch_enabled ?
@@ -2255,7 +2161,6 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp,
/* DC5/DC6 requires at least 6 idle frames */
val = usecs_to_jiffies(intel_get_frame_time_us(crtc_state) * 6);
intel_dp->psr.dc3co_exit_delay = val;
- intel_dp->psr.dc3co_exitline = crtc_state->dc3co_exitline;
intel_dp->psr.psr2_sel_fetch_enabled = crtc_state->enable_psr2_sel_fetch;
intel_dp->psr.su_region_et_enabled = crtc_state->enable_psr2_su_region_et;
intel_dp->psr.psr2_sel_fetch_cff_enabled = false;
@@ -2334,8 +2239,6 @@ static void intel_psr_exit(struct intel_dp *intel_dp)
intel_de_rmw(display, TRANS_DP2_CTL(intel_dp->psr.transcoder),
TRANS_DP2_PANEL_REPLAY_ENABLE, 0);
} else if (intel_dp->psr.sel_update_enabled) {
- tgl_disallow_dc3co_on_psr2_exit(intel_dp);
-
val = intel_de_rmw(display,
EDP_PSR2_CTL(display, cpu_transcoder),
EDP_PSR2_ENABLE, 0);
@@ -2358,6 +2261,27 @@ static void intel_psr_exit(struct intel_dp *intel_dp)
intel_dp->psr.active = false;
}
+bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+ enum transcoder cpu_transcoder;
+ bool in_deep_sleep = false;
+ u32 val;
+
+ mutex_lock(&intel_dp->psr.lock);
+
+ if (!intel_dp->psr.enabled || !intel_dp->psr.sel_update_enabled)
+ goto out;
+
+ cpu_transcoder = intel_dp->psr.transcoder;
+ val = intel_de_read(display, EDP_PSR2_STATUS(display, cpu_transcoder));
+ in_deep_sleep = (val & EDP_PSR2_STATUS_STATE_MASK) ==
+ EDP_PSR2_STATUS_STATE_DEEP_SLEEP;
+out:
+ mutex_unlock(&intel_dp->psr.lock);
+ return in_deep_sleep;
+}
+
static void intel_psr_wait_exit_locked(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -2448,6 +2372,8 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp)
intel_dp->psr.psr2_sel_fetch_cff_enabled = false;
intel_dp->psr.active_non_psr_pipes = 0;
intel_dp->psr.pkg_c_latency_used = 0;
+ cancel_delayed_work(&intel_dp->psr.dc3co_work);
+ intel_dp->psr.dc3co_allowed = false;
}
/**
@@ -2535,8 +2461,13 @@ void intel_psr_resume(struct intel_dp *intel_dp)
goto out;
}
- if (--intel_dp->psr.pause_counter == 0)
+ if (--intel_dp->psr.pause_counter == 0) {
intel_psr_activate(intel_dp);
+ /* re-arm cancelled dc3co work from pause */
+ if (intel_dp->psr.dc3co_allowed)
+ mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
+ intel_dp->psr.dc3co_exit_delay);
+ }
out:
mutex_unlock(&psr->lock);
@@ -3240,10 +3171,13 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
const struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
struct intel_encoder *encoder;
+ bool dc3co_allowed;
if (!crtc_state->has_psr)
return;
+ dc3co_allowed = intel_display_power_dc3co_allowed(display);
+
verify_panel_replay_dsc_state(crtc_state);
for_each_intel_encoder_mask_with_psr(state->base.dev, encoder,
@@ -3271,6 +3205,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
keep_disabled = true;
}
+ intel_dp->psr.dc3co_allowed = dc3co_allowed;
+
if (!psr->enabled && !keep_disabled)
intel_psr_enable_locked(intel_dp, crtc_state);
else if (psr->enabled && !crtc_state->wm_level_disabled)
@@ -3287,6 +3223,11 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
*/
intel_dp->psr.busy_frontbuffer_bits = 0;
+ if (intel_dp->psr.dc3co_allowed) {
+ mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
+ intel_dp->psr.dc3co_exit_delay);
+ }
+
mutex_unlock(&psr->lock);
}
}
@@ -3659,34 +3600,6 @@ void intel_psr_invalidate(struct intel_display *display,
mutex_unlock(&intel_dp->psr.lock);
}
}
-/*
- * When we will be completely rely on PSR2 S/W tracking in future,
- * intel_psr_flush() will invalidate and flush the PSR for ORIGIN_FLIP
- * event also therefore tgl_dc3co_flush_locked() require to be changed
- * accordingly in future.
- */
-static void
-tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits,
- enum fb_op_origin origin)
-{
- struct intel_display *display = to_intel_display(intel_dp);
-
- if (!intel_dp->psr.dc3co_exitline || !intel_dp->psr.sel_update_enabled ||
- !intel_dp->psr.active)
- return;
-
- /*
- * At every frontbuffer flush flip event modified delay of delayed work,
- * when delayed work schedules that means display has been idle.
- */
- if (!(frontbuffer_bits &
- INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe)))
- return;
-
- tgl_psr2_enable_dc3co(intel_dp);
- mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
- intel_dp->psr.dc3co_exit_delay);
-}
static void _psr_flush_handle(struct intel_dp *intel_dp)
{
@@ -3773,7 +3686,7 @@ void intel_psr_flush(struct intel_display *display,
if (origin == ORIGIN_FLIP ||
(origin == ORIGIN_CURSOR_UPDATE &&
!intel_dp->psr.psr2_sel_fetch_enabled)) {
- tgl_dc3co_flush_locked(intel_dp, frontbuffer_bits, origin);
+ psr2_dc3co_flush_locked(intel_dp, frontbuffer_bits);
goto unlock;
}
@@ -3832,7 +3745,7 @@ void intel_psr_init(struct intel_dp *intel_dp)
intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link;
INIT_WORK(&intel_dp->psr.work, intel_psr_work);
- INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work);
+ INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, psr2_dc3co_disable_work);
mutex_init(&intel_dp->psr.lock);
}
diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h
index 29723e63888f..d545fdaa0de7 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.h
+++ b/drivers/gpu/drm/i915/display/intel_psr.h
@@ -87,5 +87,6 @@ void intel_psr_compute_config_late(struct intel_dp *intel_dp,
int intel_psr_min_guardband(struct intel_crtc_state *crtc_state);
bool intel_psr_use_trans_push(const struct intel_crtc_state *crtc_state);
bool intel_psr_pr_async_video_timing_supported(struct intel_dp *intel_dp);
+bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp);
#endif /* __INTEL_PSR_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_psr_regs.h b/drivers/gpu/drm/i915/display/intel_psr_regs.h
index 8afbf5a38335..16a9e3af198d 100644
--- a/drivers/gpu/drm/i915/display/intel_psr_regs.h
+++ b/drivers/gpu/drm/i915/display/intel_psr_regs.h
@@ -268,6 +268,7 @@
#define _PR_ALPM_CTL_A 0x60948
#define PR_ALPM_CTL(dev_priv, tran) _MMIO_TRANS2(dev_priv, tran, _PR_ALPM_CTL_A)
+#define PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL BIT(7)
#define PR_ALPM_CTL_ALLOW_LINK_OFF_BETWEEN_AS_SDP_AND_SU BIT(6)
#define PR_ALPM_CTL_RFB_UPDATE_CONTROL BIT(5)
#define PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE BIT(4)
diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c
index d83d350959d8..3075ef04df56 100644
--- a/drivers/gpu/drm/i915/display/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/display/intel_sdvo.c
@@ -1354,7 +1354,8 @@ static bool intel_sdvo_has_audio(struct intel_encoder *encoder,
return intel_conn_state->force_audio == HDMI_AUDIO_ON;
}
-static int intel_sdvo_compute_config(struct intel_encoder *encoder,
+static int intel_sdvo_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -1398,8 +1399,8 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder,
const struct drm_display_mode *fixed_mode;
int ret;
- ret = intel_panel_compute_config(&intel_sdvo_connector->base,
- adjusted_mode);
+ ret = intel_panel_compute_config(state, pipe_config,
+ &intel_sdvo_connector->base);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c
index 0a926c6f25f4..840e1dcdc2d0 100644
--- a/drivers/gpu/drm/i915/display/intel_tv.c
+++ b/drivers/gpu/drm/i915/display/intel_tv.c
@@ -1187,13 +1187,12 @@ static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode,
}
static int
-intel_tv_compute_config(struct intel_encoder *encoder,
+intel_tv_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
- struct intel_atomic_state *state =
- to_intel_atomic_state(pipe_config->uapi.state);
struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
struct intel_tv_connector_state *tv_conn_state =
to_intel_tv_connector_state(conn_state);
diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c
index aa587be908f1..bffbdee76ee1 100644
--- a/drivers/gpu/drm/i915/display/intel_vrr.c
+++ b/drivers/gpu/drm/i915/display/intel_vrr.c
@@ -7,8 +7,10 @@
#include <drm/drm_print.h>
#include "intel_alpm.h"
+#include "intel_cmtg.h"
#include "intel_crtc.h"
#include "intel_de.h"
+#include "intel_display_limits.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_dmc.h"
@@ -322,19 +324,19 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state)
return intel_vrr_fixed_rr_hw_vtotal(crtc_state);
}
-void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state)
+void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state,
+ enum transcoder transcoder)
{
struct intel_display *display = to_intel_display(crtc_state);
- enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
if (!intel_vrr_possible(crtc_state))
return;
- intel_de_write(display, TRANS_VRR_VMIN(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VRR_VMIN(display, transcoder),
intel_vrr_fixed_rr_hw_vmin(crtc_state) - 1);
- intel_de_write(display, TRANS_VRR_VMAX(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VRR_VMAX(display, transcoder),
intel_vrr_fixed_rr_hw_vmax(crtc_state) - 1);
- intel_de_write(display, TRANS_VRR_FLIPLINE(display, cpu_transcoder),
+ intel_de_write(display, TRANS_VRR_FLIPLINE(display, transcoder),
intel_vrr_fixed_rr_hw_flipline(crtc_state) - 1);
}
@@ -649,7 +651,8 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
lower_32_bits(crtc_state->cmrr.cmrr_n));
}
- intel_vrr_set_fixed_rr_timings(crtc_state);
+ intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder);
+ intel_cmtg_set_vrr_timings(crtc_state);
if (!intel_vrr_always_use_vrr_tg(display))
intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
@@ -934,6 +937,8 @@ static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state,
vrr_ctl |= VRR_CTL_CMRR_ENABLE;
intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl);
+
+ intel_cmtg_set_vrr_ctl(crtc_state);
}
static void intel_vrr_tg_disable(const struct intel_crtc_state *old_crtc_state)
@@ -978,7 +983,7 @@ void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state)
intel_vrr_tg_disable(old_crtc_state);
intel_vrr_disable_dc_balancing(old_crtc_state);
- intel_vrr_set_fixed_rr_timings(old_crtc_state);
+ intel_vrr_set_fixed_rr_timings(old_crtc_state, old_crtc_state->cpu_transcoder);
}
void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state)
@@ -1101,16 +1106,6 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state)
crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display);
}
- /*
- * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal
- * bits are not filled. Since for these platforms TRAN_VMIN is always
- * filled with crtc_vtotal, use TRAN_VRR_VMIN to get the vtotal for
- * adjusted_mode.
- */
- if (intel_vrr_always_use_vrr_tg(display))
- crtc_state->hw.adjusted_mode.crtc_vtotal =
- intel_vrr_vmin_vtotal(crtc_state);
-
if (HAS_AS_SDP(display)) {
trans_vrr_vsync =
intel_de_read(display,
diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h
index 4f16ca4af91f..55e9c429f579 100644
--- a/drivers/gpu/drm/i915/display/intel_vrr.h
+++ b/drivers/gpu/drm/i915/display/intel_vrr.h
@@ -15,6 +15,7 @@ struct intel_crtc;
struct intel_crtc_state;
struct intel_dsb;
struct intel_display;
+enum transcoder;
bool intel_vrr_is_capable(struct intel_connector *connector);
bool intel_vrr_is_in_range(struct intel_connector *connector, int vrefresh);
@@ -42,7 +43,8 @@ int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state);
bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state);
void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state);
void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state);
-void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state);
+void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state,
+ enum transcoder transcoder);
void intel_vrr_dcb_reset(const struct intel_crtc_state *old_crtc_state,
struct intel_crtc *crtc);
bool intel_vrr_always_use_vrr_tg(struct intel_display *display);
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
index 877eab75f19a..8829f365592e 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
@@ -266,7 +266,8 @@ static void band_gap_reset(struct intel_display *display)
vlv_flisdsi_put(display);
}
-static int intel_dsi_compute_config(struct intel_encoder *encoder,
+static int intel_dsi_compute_config(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -280,7 +281,7 @@ static int intel_dsi_compute_config(struct intel_encoder *encoder,
pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- ret = intel_panel_compute_config(intel_connector, adjusted_mode);
+ ret = intel_panel_compute_config(state, pipe_config, intel_connector);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 8878539c10ed..2c5d20e4dbaf 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -17,8 +17,6 @@
#include "i915_vma_types.h"
enum intel_region_id;
-struct drm_scanout_buffer;
-struct intel_panic;
#define obj_to_i915(obj__) to_i915((obj__)->base.dev)
@@ -693,11 +691,6 @@ i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
int i915_gem_object_truncate(struct drm_i915_gem_object *obj);
-struct intel_panic *i915_gem_object_alloc_panic(void);
-int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
- struct drm_gem_object *_obj, bool panic_tiling);
-void i915_gem_object_panic_finish(struct intel_panic *panic);
-
/**
* i915_gem_object_pin_map - return a contiguous mapping of the entire object
* @obj: the object to map into kernel address space
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
index df35bdb755e4..59e3d4de7d3c 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
@@ -6,11 +6,8 @@
#include <linux/vmalloc.h>
#include <drm/drm_cache.h>
-#include <drm/drm_panic.h>
#include <drm/drm_print.h>
-#include "display/intel_fb.h"
-#include "display/intel_display_types.h"
#include "gt/intel_gt.h"
#include "gt/intel_tlb.h"
@@ -359,131 +356,6 @@ static void *i915_gem_object_map_pfn(struct drm_i915_gem_object *obj,
return vaddr ?: ERR_PTR(-ENOMEM);
}
-struct intel_panic {
- struct page **pages;
- int page;
- void *vaddr;
-};
-
-static void i915_panic_kunmap(struct intel_panic *panic)
-{
- if (panic->vaddr) {
- drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
- kunmap_local(panic->vaddr);
- panic->vaddr = NULL;
- }
-}
-
-static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj)
-{
- unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i;
- struct page *page;
- struct page **pages;
- struct sgt_iter iter;
-
- /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */
- pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC);
- if (!pages)
- return NULL;
-
- i = 0;
- for_each_sgt_page(page, iter, obj->mm.pages)
- pages[i++] = page;
- return pages;
-}
-
-static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
- unsigned int y, u32 color)
-{
- struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
- unsigned int offset = fb->panic_tiling(sb->width, x, y);
-
- iosys_map_wr(&sb->map[0], offset, u32, color);
-}
-
-/*
- * The scanout buffer pages are not mapped, so for each pixel,
- * use kmap_local_page_try_from_panic() to map the page, and write the pixel.
- * Try to keep the map from the previous pixel, to avoid too much map/unmap.
- */
-static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
- unsigned int y, u32 color)
-{
- unsigned int new_page;
- unsigned int offset;
- struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
- struct intel_panic *panic = fb->panic;
-
- if (fb->panic_tiling)
- offset = fb->panic_tiling(sb->width, x, y);
- else
- offset = y * sb->pitch[0] + x * sb->format->cpp[0];
-
- new_page = offset >> PAGE_SHIFT;
- offset = offset % PAGE_SIZE;
- if (new_page != panic->page) {
- i915_panic_kunmap(panic);
- panic->page = new_page;
- panic->vaddr =
- kmap_local_page_try_from_panic(panic->pages[panic->page]);
- }
- if (panic->vaddr) {
- u32 *pix = panic->vaddr + offset;
- *pix = color;
- }
-}
-
-struct intel_panic *i915_gem_object_alloc_panic(void)
-{
- struct intel_panic *panic;
-
- panic = kzalloc_obj(*panic);
-
- return panic;
-}
-
-/*
- * Setup the gem framebuffer for drm_panic access.
- * Use current vaddr if it exists, or setup a list of pages.
- * pfn is not supported yet.
- */
-int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
- struct drm_gem_object *_obj, bool panic_tiling)
-{
- enum i915_map_type has_type;
- struct drm_i915_gem_object *obj = to_intel_bo(_obj);
- void *ptr;
-
- ptr = page_unpack_bits(obj->mm.mapping, &has_type);
- if (ptr) {
- if (i915_gem_object_has_iomem(obj))
- iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr);
- else
- iosys_map_set_vaddr(&sb->map[0], ptr);
-
- if (panic_tiling)
- sb->set_pixel = i915_gem_object_panic_map_set_pixel;
- return 0;
- }
- if (i915_gem_object_has_struct_page(obj)) {
- panic->pages = i915_gem_object_panic_pages(obj);
- if (!panic->pages)
- return -ENOMEM;
- panic->page = -1;
- sb->set_pixel = i915_gem_object_panic_page_set_pixel;
- return 0;
- }
- return -EOPNOTSUPP;
-}
-
-void i915_gem_object_panic_finish(struct intel_panic *panic)
-{
- i915_panic_kunmap(panic);
- panic->page = -1;
- kfree(panic->pages);
- panic->pages = NULL;
-}
-
/* get, pin, and map the pages of the object into kernel space */
void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
enum i915_map_type type)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c
new file mode 100644
index 000000000000..91389d36f101
--- /dev/null
+++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: MIT
+/* Copyright © 2026 Intel Corporation */
+
+#include <drm/drm_cache.h>
+#include <drm/drm_panic.h>
+#include <drm/intel/display_parent_interface.h>
+
+#include "i915_gem_object.h"
+#include "i915_gem_panic.h"
+
+struct intel_panic {
+ struct page **pages;
+ int page;
+ void *vaddr;
+
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width);
+};
+
+static void i915_panic_kunmap(struct intel_panic *panic)
+{
+ if (panic->vaddr) {
+ drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
+ kunmap_local(panic->vaddr);
+ panic->vaddr = NULL;
+ }
+}
+
+static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj)
+{
+ unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i;
+ struct page *page;
+ struct page **pages;
+ struct sgt_iter iter;
+
+ /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */
+ pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC);
+ if (!pages)
+ return NULL;
+
+ i = 0;
+ for_each_sgt_page(page, iter, obj->mm.pages)
+ pages[i++] = page;
+ return pages;
+}
+
+static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
+ unsigned int y, u32 color)
+{
+ struct intel_panic *panic = sb->private;
+ unsigned int offset = panic->tiling(sb->width, x, y);
+
+ iosys_map_wr(&sb->map[0], offset, u32, color);
+}
+
+/*
+ * The scanout buffer pages are not mapped, so for each pixel,
+ * use kmap_local_page_try_from_panic() to map the page, and write the pixel.
+ * Try to keep the map from the previous pixel, to avoid too much map/unmap.
+ */
+static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
+ unsigned int y, u32 color)
+{
+ struct intel_panic *panic = sb->private;
+ unsigned int new_page;
+ unsigned int offset;
+
+ if (panic->tiling)
+ offset = panic->tiling(sb->width, x, y);
+ else
+ offset = y * sb->pitch[0] + x * sb->format->cpp[0];
+
+ new_page = offset >> PAGE_SHIFT;
+ offset = offset % PAGE_SIZE;
+ if (new_page != panic->page) {
+ i915_panic_kunmap(panic);
+ panic->page = new_page;
+ panic->vaddr =
+ kmap_local_page_try_from_panic(panic->pages[panic->page]);
+ }
+ if (panic->vaddr) {
+ u32 *pix = panic->vaddr + offset;
+ *pix = color;
+ }
+}
+
+static struct intel_panic *i915_gem_object_alloc_panic(void)
+{
+ struct intel_panic *panic;
+
+ panic = kzalloc_obj(*panic);
+
+ return panic;
+}
+
+/*
+ * Setup the gem framebuffer for drm_panic access.
+ * Use current vaddr if it exists, or setup a list of pages.
+ * pfn is not supported yet.
+ */
+static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
+ struct drm_gem_object *_obj,
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width))
+{
+ enum i915_map_type has_type;
+ struct drm_i915_gem_object *obj = to_intel_bo(_obj);
+ void *ptr;
+
+ sb->private = panic;
+
+ ptr = page_unpack_bits(obj->mm.mapping, &has_type);
+ if (ptr) {
+ if (i915_gem_object_has_iomem(obj))
+ iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr);
+ else
+ iosys_map_set_vaddr(&sb->map[0], ptr);
+
+ if (tiling) {
+ panic->tiling = tiling;
+ sb->set_pixel = i915_gem_object_panic_map_set_pixel;
+ }
+ return 0;
+ }
+ if (i915_gem_object_has_struct_page(obj)) {
+ panic->pages = i915_gem_object_panic_pages(obj);
+ if (!panic->pages)
+ return -ENOMEM;
+ panic->page = -1;
+ panic->tiling = tiling;
+ sb->set_pixel = i915_gem_object_panic_page_set_pixel;
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+static void i915_gem_object_panic_finish(struct intel_panic *panic)
+{
+ i915_panic_kunmap(panic);
+ panic->page = -1;
+ kfree(panic->pages);
+ panic->pages = NULL;
+}
+
+const struct intel_display_panic_interface i915_display_panic_interface = {
+ .alloc = i915_gem_object_alloc_panic,
+ .setup = i915_gem_object_panic_setup,
+ .finish = i915_gem_object_panic_finish,
+};
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.h b/drivers/gpu/drm/i915/gem/i915_gem_panic.h
new file mode 100644
index 000000000000..82c3aca6f1f3
--- /dev/null
+++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2026 Intel Corporation */
+
+#ifndef __I915_GEM_PANIC_H__
+#define __I915_GEM_PANIC_H__
+
+#include <linux/types.h>
+
+extern const struct intel_display_panic_interface i915_display_panic_interface;
+
+#endif /* __I915_GEM_PANIC_H__ */
diff --git a/drivers/gpu/drm/i915/gt/intel_gsc.c b/drivers/gpu/drm/i915/gt/intel_gsc.c
index 050d909fb4f8..1c06bf76568a 100644
--- a/drivers/gpu/drm/i915/gt/intel_gsc.c
+++ b/drivers/gpu/drm/i915/gt/intel_gsc.c
@@ -286,7 +286,7 @@ static void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id)
ret = generic_handle_irq_safe(gt->gsc.intf[intf_id].irq);
if (ret)
- gt_err_ratelimited(gt, "error handling GSC irq: %d\n", ret);
+ gt_err_ratelimited(gt, "GSC: irq handling failed (%pe)\n", ERR_PTR(ret));
}
void intel_gsc_irq_handler(struct intel_gt *gt, u32 iir)
diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c
index ca5b54466a65..dc9ef98ff51b 100644
--- a/drivers/gpu/drm/i915/gvt/edid.c
+++ b/drivers/gpu/drm/i915/gvt/edid.c
@@ -90,13 +90,13 @@ static inline int cnp_get_port_from_gmbus0(u32 gmbus0)
int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK;
int port = -EINVAL;
- if (port_select == GMBUS_PIN_1_BXT)
+ if (port_select == GMBUS_PIN_1)
port = PORT_B;
- else if (port_select == GMBUS_PIN_2_BXT)
+ else if (port_select == GMBUS_PIN_2)
port = PORT_C;
- else if (port_select == GMBUS_PIN_3_BXT)
+ else if (port_select == GMBUS_PIN_3)
port = PORT_D;
- else if (port_select == GMBUS_PIN_4_CNP)
+ else if (port_select == GMBUS_PIN_4)
port = PORT_E;
return port;
}
@@ -106,11 +106,11 @@ static inline int bxt_get_port_from_gmbus0(u32 gmbus0)
int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK;
int port = -EINVAL;
- if (port_select == GMBUS_PIN_1_BXT)
+ if (port_select == GMBUS_PIN_1)
port = PORT_B;
- else if (port_select == GMBUS_PIN_2_BXT)
+ else if (port_select == GMBUS_PIN_2)
port = PORT_C;
- else if (port_select == GMBUS_PIN_3_BXT)
+ else if (port_select == GMBUS_PIN_3)
port = PORT_D;
return port;
}
diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c
index 58081b52461a..45f2dcc5130e 100644
--- a/drivers/gpu/drm/i915/i915_driver.c
+++ b/drivers/gpu/drm/i915/i915_driver.c
@@ -51,7 +51,6 @@
#include <drm/intel/intel_pcode_regs.h>
#include "display/i9xx_display_sr.h"
-#include "display/intel_bw.h"
#include "display/intel_cdclk.h"
#include "display/intel_crtc.h"
#include "display/intel_display_device.h"
@@ -60,8 +59,6 @@
#include "display/intel_dmc.h"
#include "display/intel_dp.h"
#include "display/intel_dpt.h"
-#include "display/intel_dram.h"
-#include "display/intel_encoder.h"
#include "display/intel_fbdev.h"
#include "display/intel_gmbus.h"
#include "display/intel_hotplug.h"
@@ -79,6 +76,7 @@
#include "gem/i915_gem_ioctls.h"
#include "gem/i915_gem_mman.h"
#include "gem/i915_gem_object_frontbuffer.h"
+#include "gem/i915_gem_panic.h"
#include "gem/i915_gem_pm.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_pm.h"
@@ -111,7 +109,6 @@
#include "i915_irq.h"
#include "i915_memcpy.h"
#include "i915_overlay.h"
-#include "i915_panic.h"
#include "i915_perf.h"
#include "i915_query.h"
#include "i915_reg.h"
@@ -470,7 +467,6 @@ static int i915_pcode_init(struct drm_i915_private *i915)
*/
static int i915_driver_hw_probe(struct drm_i915_private *dev_priv)
{
- struct intel_display *display = dev_priv->display;
struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
int ret;
@@ -564,28 +560,14 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv)
drm_dbg(&dev_priv->drm, "can't enable MSI");
}
- intel_opregion_setup(display);
-
ret = i915_pcode_init(dev_priv);
if (ret)
- goto err_opregion;
-
- /*
- * Fill the dram structure to get the system dram info. This will be
- * used for memory latency calculation.
- */
- ret = intel_dram_detect(display);
- if (ret)
- goto err_opregion;
-
- intel_bw_init_hw(display);
+ goto err_msi;
return 0;
-err_opregion:
- intel_opregion_cleanup(display);
- if (pdev->msi_enabled)
- pci_disable_msi(pdev);
+err_msi:
+ pci_disable_msi(pdev);
err_mem_regions:
intel_memory_regions_driver_release(dev_priv);
err_ggtt:
@@ -604,15 +586,11 @@ ALLOW_ERROR_INJECTION(i915_driver_hw_probe, ERRNO);
*/
static void i915_driver_hw_remove(struct drm_i915_private *dev_priv)
{
- struct intel_display *display = dev_priv->display;
struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
i915_perf_fini(dev_priv);
- intel_opregion_cleanup(display);
-
- if (pdev->msi_enabled)
- pci_disable_msi(pdev);
+ pci_disable_msi(pdev);
}
/**
@@ -803,7 +781,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent)
const struct intel_device_info *match_info =
(struct intel_device_info *)ent->driver_data;
struct drm_i915_private *i915;
- struct intel_display *display;
i915 = devm_drm_dev_alloc(&pdev->dev, &i915_drm_driver,
struct drm_i915_private, drm);
@@ -818,12 +795,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Set up device info and initial runtime info. */
intel_device_info_driver_create(i915, pdev->device, match_info);
- display = intel_display_device_probe(pdev, &parent);
- if (IS_ERR(display))
- return ERR_CAST(display);
-
- i915->display = display;
-
return i915;
}
@@ -856,7 +827,13 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return PTR_ERR(i915);
}
- display = i915->display;
+ display = intel_display_device_probe(pdev, &parent);
+ if (IS_ERR(display)) {
+ ret = PTR_ERR(display);
+ goto out_pci_disable;
+ }
+
+ i915->display = display;
ret = i915_driver_early_probe(i915);
if (ret < 0)
@@ -971,7 +948,6 @@ void i915_driver_remove(struct drm_i915_private *i915)
intel_display_driver_remove(display);
intel_irq_uninstall(i915);
- intel_hpd_cancel_work(display);
intel_display_driver_remove_noirq(display);
@@ -1043,43 +1019,24 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
disable_rpm_wakeref_asserts(&i915->runtime_pm);
intel_runtime_pm_disable(&i915->runtime_pm);
- intel_display_power_disable(display);
- drm_client_dev_suspend(&i915->drm);
- if (intel_display_device_present(display)) {
- drm_kms_helper_poll_disable(&i915->drm);
- intel_display_driver_disable_user_access(display);
-
- drm_atomic_helper_shutdown(&i915->drm);
- }
-
- intel_dp_mst_suspend(display);
+ intel_display_driver_shutdown(display);
intel_irq_suspend(i915);
- intel_hpd_cancel_work(display);
-
- if (intel_display_device_present(display))
- intel_display_driver_suspend_access(display);
-
- intel_encoder_suspend_all(display);
- intel_encoder_shutdown_all(display);
intel_dmc_suspend(display);
i915_gem_suspend(i915);
/*
- * The only requirement is to reboot with display DC states disabled,
- * for now leaving all display power wells in the INIT power domain
- * enabled.
- *
* TODO:
* - unify the pci_driver::shutdown sequence here with the
* pci_driver.driver.pm.poweroff,poweroff_late sequence.
* - unify the driver remove and system/runtime suspend sequences with
* the above unified shutdown/poweroff sequence.
*/
- intel_display_power_driver_remove(display);
+
+ intel_display_driver_shutdown_late(display);
enable_rpm_wakeref_asserts(&i915->runtime_pm);
intel_runtime_pm_driver_last_release(&i915->runtime_pm);
@@ -1108,10 +1065,10 @@ static int i915_drm_prepare(struct drm_device *dev)
intel_pxp_suspend_prepare(i915->pxp);
/*
- * NB intel_display_driver_suspend() may issue new requests after we've
- * ostensibly marked the GPU as ready-to-sleep here. We need to
- * split out that work and pull it forward so that after point,
- * the GPU is not woken again.
+ * NB intel_display_driver_pm_suspend() may issue new requests after
+ * we've ostensibly marked the GPU as ready-to-sleep here. We need to
+ * split out that work and pull it forward so that after point, the GPU
+ * is not woken again.
*/
return i915_gem_backup_suspend(i915);
}
@@ -1124,24 +1081,9 @@ static int i915_drm_suspend(struct drm_device *dev)
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
- /* We do a lot of poking in a lot of registers, make sure they work
- * properly. */
- intel_display_power_disable(display);
- drm_client_dev_suspend(dev);
- if (intel_display_device_present(display)) {
- drm_kms_helper_poll_disable(dev);
- intel_display_driver_disable_user_access(display);
- }
-
- intel_display_driver_suspend(display);
+ intel_display_driver_pm_suspend(display);
intel_irq_suspend(dev_priv);
- intel_hpd_cancel_work(display);
-
- if (intel_display_device_present(display))
- intel_display_driver_suspend_access(display);
-
- intel_encoder_suspend_all(display);
/* Must be called before GGTT is suspended. */
intel_dpt_suspend(display);
@@ -1181,12 +1123,12 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
for_each_gt(gt, dev_priv, i)
intel_uncore_suspend(gt->uncore);
- intel_display_power_suspend_late(display, s2idle);
+ intel_display_driver_pm_suspend_late(display, s2idle);
ret = vlv_suspend_complete(dev_priv);
if (ret) {
drm_err(&dev_priv->drm, "Suspend complete failed: %d\n", ret);
- intel_display_power_resume_early(display);
+ intel_display_driver_pm_resume_early(display);
}
enable_rpm_wakeref_asserts(rpm);
@@ -1309,24 +1251,7 @@ static int i915_drm_resume(struct drm_device *dev)
intel_clock_gating_init(&dev_priv->drm);
- if (intel_display_device_present(display))
- intel_display_driver_resume_access(display);
-
- intel_hpd_init(display);
-
- intel_display_driver_resume(display);
-
- if (intel_display_device_present(display)) {
- intel_display_driver_enable_user_access(display);
- drm_kms_helper_poll_enable(dev);
- }
- intel_hpd_poll_disable(display);
-
- intel_opregion_resume(display);
-
- drm_client_dev_resume(dev);
-
- intel_display_power_enable(display);
+ intel_display_driver_pm_resume(display);
intel_gvt_resume(dev_priv);
@@ -1362,7 +1287,7 @@ static int i915_drm_resume_early(struct drm_device *dev)
for_each_gt(gt, dev_priv, i)
intel_gt_resume_early(gt);
- intel_display_power_resume_early(display);
+ intel_display_driver_pm_resume_early(display);
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
diff --git a/drivers/gpu/drm/i915/i915_panic.c b/drivers/gpu/drm/i915/i915_panic.c
deleted file mode 100644
index 728be077e8e8..000000000000
--- a/drivers/gpu/drm/i915/i915_panic.c
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: MIT
-/* Copyright © 2025 Intel Corporation */
-
-#include <drm/drm_panic.h>
-#include <drm/intel/display_parent_interface.h>
-
-#include "display/intel_display_types.h"
-#include "display/intel_fb.h"
-#include "gem/i915_gem_object.h"
-
-#include "i915_panic.h"
-
-static struct intel_panic *intel_panic_alloc(void)
-{
- return i915_gem_object_alloc_panic();
-}
-
-static int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
-{
- struct intel_framebuffer *fb = sb->private;
- struct drm_gem_object *obj = intel_fb_bo(&fb->base);
-
- return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling);
-}
-
-static void intel_panic_finish(struct intel_panic *panic)
-{
- return i915_gem_object_panic_finish(panic);
-}
-
-const struct intel_display_panic_interface i915_display_panic_interface = {
- .alloc = intel_panic_alloc,
- .setup = intel_panic_setup,
- .finish = intel_panic_finish,
-};
diff --git a/drivers/gpu/drm/i915/i915_panic.h b/drivers/gpu/drm/i915/i915_panic.h
deleted file mode 100644
index 743d8c861c42..000000000000
--- a/drivers/gpu/drm/i915/i915_panic.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Copyright © 2025 Intel Corporation */
-
-#ifndef __I915_PANIC_H__
-#define __I915_PANIC_H__
-
-extern const struct intel_display_panic_interface i915_display_panic_interface;
-
-#endif /* __I915_PANIC_H__ */
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 8e7b146880f4..e5a04253e73b 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -278,6 +278,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_dp_aux.o \
i915-display/intel_dp_aux_backlight.o \
i915-display/intel_dp_hdcp.o \
+ i915-display/intel_dp_link_caps.o \
i915-display/intel_dp_link_training.o \
i915-display/intel_dp_mst.o \
i915-display/intel_dp_test.o \
diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c
index 810d93fefcbc..92a4573db28a 100644
--- a/drivers/gpu/drm/xe/display/xe_display.c
+++ b/drivers/gpu/drm/xe/display/xe_display.c
@@ -19,7 +19,6 @@
#include "intel_acpi.h"
#include "intel_audio.h"
-#include "intel_bw.h"
#include "intel_display.h"
#include "intel_display_core.h"
#include "intel_display_device.h"
@@ -29,8 +28,6 @@
#include "intel_dmc.h"
#include "intel_dmc_wl.h"
#include "intel_dp.h"
-#include "intel_dram.h"
-#include "intel_encoder.h"
#include "intel_fbdev.h"
#include "intel_hdcp.h"
#include "intel_hotplug.h"
@@ -84,10 +81,8 @@ static void xe_display_fini_early(void *arg)
if (!xe->info.probe_display)
return;
- intel_hpd_cancel_work(display);
- intel_display_driver_remove_nogem(display);
intel_display_driver_remove_noirq(display);
- intel_opregion_cleanup(display);
+ intel_display_driver_remove_nogem(display);
intel_display_power_cleanup(display);
}
@@ -113,22 +108,9 @@ int xe_display_init_early(struct xe_device *xe)
return 0;
}
- /* Early display init.. */
- intel_opregion_setup(display);
-
- /*
- * Fill the dram structure to get the system dram info. This will be
- * used for memory latency calculation.
- */
- err = intel_dram_detect(display);
- if (err)
- goto err_opregion;
-
- intel_bw_init_hw(display);
-
err = intel_display_driver_probe_noirq(display);
if (err)
- goto err_opregion;
+ return err;
err = intel_display_driver_probe_nogem(display);
if (err)
@@ -138,8 +120,7 @@ int xe_display_init_early(struct xe_device *xe)
err_noirq:
intel_display_driver_remove_noirq(display);
intel_display_power_cleanup(display);
-err_opregion:
- intel_opregion_cleanup(display);
+
return err;
}
@@ -191,6 +172,30 @@ void xe_display_unregister(struct xe_device *xe)
intel_display_driver_unregister(display);
}
+void xe_display_shutdown(struct xe_device *xe)
+{
+ struct intel_display *display = xe->display;
+
+ if (!xe->info.probe_display)
+ return;
+
+ intel_display_driver_shutdown(display);
+
+ intel_opregion_suspend(display, PCI_D3cold);
+
+ intel_dmc_suspend(display);
+}
+
+void xe_display_shutdown_late(struct xe_device *xe)
+{
+ struct intel_display *display = xe->display;
+
+ if (!xe->info.probe_display)
+ return;
+
+ intel_display_driver_shutdown_late(display);
+}
+
/* IRQ-related functions */
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl)
@@ -244,57 +249,62 @@ static bool suspend_to_idle(void)
return false;
}
-static void xe_display_enable_d3cold(struct xe_device *xe)
+void xe_display_pm_suspend(struct xe_device *xe)
{
struct intel_display *display = xe->display;
+ bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
- /*
- * We do a lot of poking in a lot of registers, make sure they work
- * properly.
- */
- intel_display_power_disable(display);
-
- intel_display_flush_cleanup_work(display);
+ intel_display_driver_pm_suspend(display);
- intel_opregion_suspend(display, PCI_D3cold);
+ intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold);
intel_dmc_suspend(display);
+}
- if (intel_display_device_present(display))
- intel_hpd_poll_enable(display);
+void xe_display_pm_suspend_late(struct xe_device *xe)
+{
+ struct intel_display *display = xe->display;
+ bool s2idle = suspend_to_idle();
+
+ if (!xe->info.probe_display)
+ return;
+
+ intel_display_driver_pm_suspend_late(display, s2idle);
}
-static void xe_display_disable_d3cold(struct xe_device *xe)
+void xe_display_pm_resume_early(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
- intel_dmc_resume(display);
+ intel_display_driver_pm_resume_early(display);
+}
- if (intel_display_device_present(display))
- drm_mode_config_reset(&xe->drm);
+void xe_display_pm_resume(struct xe_device *xe)
+{
+ struct intel_display *display = xe->display;
- intel_display_driver_init_hw(display);
+ if (!xe->info.probe_display)
+ return;
- intel_hpd_init(display);
+ intel_dmc_resume(display);
if (intel_display_device_present(display))
- intel_hpd_poll_disable(display);
+ drm_mode_config_reset(&xe->drm);
- intel_opregion_resume(display);
+ intel_display_driver_init_hw(display);
- intel_display_power_enable(display);
+ intel_display_driver_pm_resume(display);
}
-void xe_display_pm_suspend(struct xe_device *xe)
+static void xe_display_enable_d3cold(struct xe_device *xe)
{
struct intel_display *display = xe->display;
- bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
@@ -304,60 +314,39 @@ void xe_display_pm_suspend(struct xe_device *xe)
* properly.
*/
intel_display_power_disable(display);
- drm_client_dev_suspend(&xe->drm);
-
- if (intel_display_device_present(display)) {
- drm_kms_helper_poll_disable(&xe->drm);
- intel_display_driver_disable_user_access(display);
- intel_display_driver_suspend(display);
- }
intel_display_flush_cleanup_work(display);
- intel_encoder_block_all_hpds(display);
-
- intel_hpd_cancel_work(display);
-
- if (intel_display_device_present(display)) {
- intel_display_driver_suspend_access(display);
- intel_encoder_suspend_all(display);
- }
-
- intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold);
+ intel_opregion_suspend(display, PCI_D3cold);
intel_dmc_suspend(display);
+
+ if (intel_display_device_present(display))
+ intel_hpd_poll_enable(display);
}
-void xe_display_pm_shutdown(struct xe_device *xe)
+static void xe_display_disable_d3cold(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
- intel_display_power_disable(display);
- drm_client_dev_suspend(&xe->drm);
+ intel_dmc_resume(display);
- if (intel_display_device_present(display)) {
- drm_kms_helper_poll_disable(&xe->drm);
- intel_display_driver_disable_user_access(display);
- intel_display_driver_suspend(display);
- }
+ if (intel_display_device_present(display))
+ drm_mode_config_reset(&xe->drm);
- intel_display_flush_cleanup_work(display);
- intel_dp_mst_suspend(display);
- intel_encoder_block_all_hpds(display);
- intel_hpd_cancel_work(display);
+ intel_display_driver_init_hw(display);
- if (intel_display_device_present(display))
- intel_display_driver_suspend_access(display);
+ intel_hpd_init(display);
- intel_encoder_suspend_all(display);
- intel_encoder_shutdown_all(display);
+ if (intel_display_device_present(display))
+ intel_hpd_poll_disable(display);
- intel_opregion_suspend(display, PCI_D3cold);
+ intel_opregion_resume(display);
- intel_dmc_suspend(display);
+ intel_display_power_enable(display);
}
void xe_display_pm_runtime_suspend(struct xe_device *xe)
@@ -375,17 +364,6 @@ void xe_display_pm_runtime_suspend(struct xe_device *xe)
intel_hpd_poll_enable(display);
}
-void xe_display_pm_suspend_late(struct xe_device *xe)
-{
- struct intel_display *display = xe->display;
- bool s2idle = suspend_to_idle();
-
- if (!xe->info.probe_display)
- return;
-
- intel_display_power_suspend_late(display, s2idle);
-}
-
void xe_display_pm_runtime_suspend_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
@@ -404,68 +382,6 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe)
intel_dmc_wl_flush_release_work(display);
}
-void xe_display_pm_shutdown_late(struct xe_device *xe)
-{
- struct intel_display *display = xe->display;
-
- if (!xe->info.probe_display)
- return;
-
- /*
- * The only requirement is to reboot with display DC states disabled,
- * for now leaving all display power wells in the INIT power domain
- * enabled.
- */
- intel_display_power_driver_remove(display);
-}
-
-void xe_display_pm_resume_early(struct xe_device *xe)
-{
- struct intel_display *display = xe->display;
-
- if (!xe->info.probe_display)
- return;
-
- intel_display_power_resume_early(display);
-}
-
-void xe_display_pm_resume(struct xe_device *xe)
-{
- struct intel_display *display = xe->display;
-
- if (!xe->info.probe_display)
- return;
-
- intel_dmc_resume(display);
-
- if (intel_display_device_present(display))
- drm_mode_config_reset(&xe->drm);
-
- intel_display_driver_init_hw(display);
-
- if (intel_display_device_present(display))
- intel_display_driver_resume_access(display);
-
- intel_hpd_init(display);
-
- intel_encoder_unblock_all_hpds(display);
-
- if (intel_display_device_present(display)) {
- intel_display_driver_resume(display);
- drm_kms_helper_poll_enable(&xe->drm);
- intel_display_driver_enable_user_access(display);
- }
-
- if (intel_display_device_present(display))
- intel_hpd_poll_disable(display);
-
- intel_opregion_resume(display);
-
- drm_client_dev_resume(&xe->drm);
-
- intel_display_power_enable(display);
-}
-
void xe_display_pm_runtime_resume(struct xe_device *xe)
{
struct intel_display *display = xe->display;
diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h
index 60291cb154df..e5f9aed93206 100644
--- a/drivers/gpu/drm/xe/display/xe_display.h
+++ b/drivers/gpu/drm/xe/display/xe_display.h
@@ -29,15 +29,16 @@ int xe_display_init(struct xe_device *xe);
void xe_display_register(struct xe_device *xe);
void xe_display_unregister(struct xe_device *xe);
+void xe_display_shutdown(struct xe_device *xe);
+void xe_display_shutdown_late(struct xe_device *xe);
+
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl);
void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir);
void xe_display_irq_reset(struct xe_device *xe);
void xe_display_irq_postinstall(struct xe_device *xe);
void xe_display_pm_suspend(struct xe_device *xe);
-void xe_display_pm_shutdown(struct xe_device *xe);
void xe_display_pm_suspend_late(struct xe_device *xe);
-void xe_display_pm_shutdown_late(struct xe_device *xe);
void xe_display_pm_resume_early(struct xe_device *xe);
void xe_display_pm_resume(struct xe_device *xe);
void xe_display_pm_runtime_suspend(struct xe_device *xe);
@@ -65,15 +66,16 @@ static inline int xe_display_init(struct xe_device *xe) { return 0; }
static inline void xe_display_register(struct xe_device *xe) {}
static inline void xe_display_unregister(struct xe_device *xe) {}
+static inline void xe_display_shutdown(struct xe_device *xe) {}
+static inline void xe_display_shutdown_late(struct xe_device *xe) {}
+
static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {}
static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {}
static inline void xe_display_irq_reset(struct xe_device *xe) {}
static inline void xe_display_irq_postinstall(struct xe_device *xe) {}
static inline void xe_display_pm_suspend(struct xe_device *xe) {}
-static inline void xe_display_pm_shutdown(struct xe_device *xe) {}
static inline void xe_display_pm_suspend_late(struct xe_device *xe) {}
-static inline void xe_display_pm_shutdown_late(struct xe_device *xe) {}
static inline void xe_display_pm_resume_early(struct xe_device *xe) {}
static inline void xe_display_pm_resume(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {}
diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c
index bebb21d617f0..12c6fb99015d 100644
--- a/drivers/gpu/drm/xe/display/xe_panic.c
+++ b/drivers/gpu/drm/xe/display/xe_panic.c
@@ -5,8 +5,6 @@
#include <drm/drm_panic.h>
#include <drm/intel/display_parent_interface.h>
-#include "intel_display_types.h"
-#include "intel_fb.h"
#include "xe_bo.h"
#include "xe_panic.h"
#include "xe_res_cursor.h"
@@ -16,6 +14,9 @@ struct intel_panic {
struct iosys_map vmap;
int page;
+
+ struct xe_bo *bo;
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width);
};
static void xe_panic_kunmap(struct intel_panic *panic)
@@ -36,14 +37,13 @@ static void xe_panic_kunmap(struct intel_panic *panic)
static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
unsigned int y, u32 color)
{
- struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
- struct intel_panic *panic = fb->panic;
- struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
+ struct intel_panic *panic = sb->private;
+ struct xe_bo *bo = panic->bo;
unsigned int new_page;
unsigned int offset;
- if (fb->panic_tiling)
- offset = fb->panic_tiling(sb->width, x, y);
+ if (panic->tiling)
+ offset = panic->tiling(sb->width, x, y);
else
offset = y * sb->pitch[0] + x * sb->format->cpp[0];
@@ -84,16 +84,22 @@ static struct intel_panic *xe_panic_alloc(void)
return panic;
}
-static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
+static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
+ struct drm_gem_object *obj,
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width))
{
- struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
- struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
+ struct xe_bo *bo = gem_to_xe_bo(obj);
if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo))
return -ENODEV;
panic->page = -1;
+ panic->bo = bo;
+ panic->tiling = tiling;
+
+ sb->private = panic;
sb->set_pixel = xe_panic_page_set_pixel;
+
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 4efd2bc8c02e..b60a651a3c9b 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -1129,14 +1129,14 @@ void xe_device_shutdown(struct xe_device *xe)
drm_dbg(&xe->drm, "Shutting down device\n");
- xe_display_pm_shutdown(xe);
+ xe_display_shutdown(xe);
xe_irq_suspend(xe);
for_each_gt(gt, xe, id)
xe_gt_shutdown(gt);
- xe_display_pm_shutdown_late(xe);
+ xe_display_shutdown_late(xe);
if (!xe_driver_flr_disabled(xe)) {
/* BOOM! */
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index b9bb92e4b029..6e3eccc3c349 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -193,6 +193,7 @@ enum drm_mode_status {
#define DRM_MODE_MATCH_FLAGS (1 << 2)
#define DRM_MODE_MATCH_3D_FLAGS (1 << 3)
#define DRM_MODE_MATCH_ASPECT_RATIO (1 << 4)
+#define DRM_MODE_MATCH_TIMINGS_VRR (1 << 5)
/**
* struct drm_display_mode - DRM kernel-internal display mode structure
diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h
index 39991afeb173..de395df9ca30 100644
--- a/include/drm/intel/display_parent_interface.h
+++ b/include/drm/intel/display_parent_interface.h
@@ -167,7 +167,9 @@ struct intel_display_overlay_interface {
struct intel_display_panic_interface {
struct intel_panic *(*alloc)(void);
- int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb);
+ int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb,
+ struct drm_gem_object *obj,
+ unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width));
void (*finish)(struct intel_panic *panic);
};
diff --git a/include/drm/intel/mchbar_regs.h b/include/drm/intel/mchbar_regs.h
index ca0d421be16c..66498ca5e40b 100644
--- a/include/drm/intel/mchbar_regs.h
+++ b/include/drm/intel/mchbar_regs.h
@@ -6,8 +6,6 @@
#ifndef __INTEL_MCHBAR_REGS__
#define __INTEL_MCHBAR_REGS__
-#include "i915_reg_defs.h"
-
/*
* MCHBAR mirror.
*