[PATCH v2 1/2] drm/amd/display: Fall back to the native mode, not the first, on eDP/LVDS

From: Hugo Sales

Date: Sat Oct 24 2026 - 12:34:08 EST


Currently, create_stream_for_sink() takes the native timing for a scaled
mode from the preferred mode in connector->modes. After fbdev emulation
probes with size limits below the native resolution, as it does when
video= sets a smaller mode, that mode is no longer in the list

If that probe is the last one before a suspend, for example after an
HDMI hotplug, the resume finds no preferred mode, and the panel is
driven with the scaled mode's timing instead of its own

amdgpu_dm_get_native_mode() copies the preferred mode to the encoder on
every probe, before the size limits apply. When connector->modes has no
preferred mode, take a mode of the native size from the list, or that
copy if the limits pruned them all. The list comes first because
mode_valid may have rejected the copy's timing and kept another mode of
the same size. Limit this to eDP and LVDS, where amdgpu turns scaling on
by itself. On other connectors mode_valid can also leave the preferred
mode out, and then the first valid mode is the right fallback

Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5983
Signed-off-by: Hugo Sales <hugo@xxxxxxx>
---
.../display/amdgpu_dm/amdgpu_dm_connector.c | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index ded8e21897f1c..a13e3b641fd27 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -257,6 +257,8 @@ STATIC_IFN_KUNIT void update_subconnector_property(struct amdgpu_dm_connector *a
EXPORT_IF_KUNIT(update_subconnector_property);

STATIC_IFN_KUNIT int amdgpu_dm_connector_get_modes(struct drm_connector *connector);
+STATIC_IFN_KUNIT struct drm_encoder *amdgpu_dm_connector_to_encoder(
+ struct drm_connector *connector);

STATIC_IFN_KUNIT void amdgpu_dm_fbc_init(struct drm_connector *connector)
{
@@ -1545,6 +1547,51 @@ create_stream_for_sink(struct drm_connector *connector,
break;
}
}
+
+ /*
+ * After a probe with size limits smaller than the preferred mode,
+ * connector->modes no longer has that mode, and the first mode left
+ * would become the stream timing. fbdev emulation probes with limits
+ * that video= can set below the native size. Only eDP and LVDS panels
+ * get a fallback here, since they take only their native size. On other
+ * connectors mode_valid can also reject the preferred mode, and then
+ * the first valid mode is the right one
+ *
+ * Take a mode of the native size from connector->modes if one is left,
+ * since mode_valid may have rejected the preferred timing itself. The
+ * probe sorts the list, so the first one has the highest refresh rate
+ * left. If the limits pruned them all, use the copy of the preferred
+ * mode that amdgpu_dm_get_native_mode() keeps in the encoder. It is
+ * made on every probe, before the limits apply
+ */
+ if (!preferred_mode && aconnector && aconnector->drm_edid &&
+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {
+ struct drm_encoder *encoder = amdgpu_dm_connector_to_encoder(connector);
+ const struct drm_display_mode *enc_native =
+ encoder ? &to_amdgpu_encoder(encoder)->native_mode : NULL;
+
+ if (enc_native && enc_native->clock) {
+ list_for_each_entry(connector_mode, &connector->modes, head) {
+ if (connector_mode->hdisplay == enc_native->hdisplay &&
+ connector_mode->vdisplay == enc_native->vdisplay) {
+ preferred_mode = connector_mode;
+ break;
+ }
+ }
+
+ /*
+ * saved_mode is only used for FreeSync video modes, and
+ * that path doesn't read preferred_mode
+ */
+ if (!preferred_mode) {
+ drm_mode_init(&saved_mode, enc_native);
+ drm_mode_set_crtcinfo(&saved_mode, CRTC_INTERLACE_HALVE_V);
+ preferred_mode = &saved_mode;
+ }
+ }
+ }
+
if (!preferred_mode)
preferred_mode = list_first_entry_or_null(
&connector->modes,
--
2.55.0