Public Reviewed-by: Roman Li <[email protected]>
> -----Original Message----- > From: amd-gfx <[email protected]> On Behalf Of Adrian > Betschart > Sent: Friday, September 25, 2026 3:48 AM > To: [email protected] > Cc: Wentland, Harry <[email protected]>; Li, Sun peng (Leo) > <[email protected]>; Rodrigo Siqueira <[email protected]>; Deucher, > Alexander <[email protected]>; Koenig, Christian > <[email protected]>; David Airlie <[email protected]>; Simona Vetter > <[email protected]>; [email protected]; > [email protected] > Subject: [PATCH] drm/amd/display: Don't replace a sink mode that shares the > native totals > > decide_crtc_timing_for_drm_display_mode() copies the native mode's CRTC timing > into the requested mode when clock, htotal and vtotal match. > That is meant for the modes amdgpu inserts itself: > amdgpu_dm_create_common_mode() duplicates the native mode and changes only > hdisplay and vdisplay, so those are sent with the native timing and scaled. > > A sink can also offer a real mode with the same clock and totals as its > native mode. > CTA-861 VIC 102 (4096x2160@60) and VIC 97 > (3840x2160@60) both use 594 MHz and 4400x2250. With 3840x2160@60 as the > preferred mode, a 4096x2160@60 request goes out with the 3840x2160 timing and > the framebuffer scaled into it (dst 3840x2025 at y=67), while the AVI > InfoFrame still > carries VIC 102. A JVC projector then reports 3840x2160 and shows a corrupted > right edge. 4096x2160@24, whose totals differ, is sent and displayed > correctly. > > Inserted modes keep the native sync positions; a real mode with its own active > width does not. Compare those too, so only inserted modes are patched. > > Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") > Signed-off-by: Adrian Betschart <[email protected]> > Assisted-by: Claude Code:claude-opus-5-5 > --- > Tested on an RX 7600 (7.2.4 backport) with a JVC projector behind an HDFury > VRROOM: 4096x2160@60 now arrives as 4096x2160 with a clean right edge; > 3840x2160@60 and 7680x4320@60 are unchanged. KUnit > amdgpu_dm_connector*: 360/360. > > .../display/amdgpu_dm/amdgpu_dm_connector.c | 7 +++- > .../tests/amdgpu_dm_connector_test.c | 42 +++++++++++++++++++ > 2 files changed, 48 insertions(+), 1 deletion(-) > > 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 7ebf7216b499..7db85bcebecd 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 > @@ -996,10 +996,15 @@ decide_crtc_timing_for_drm_display_mode(struct > drm_display_mode *drm_mode, > const struct drm_display_mode > *native_mode, > bool scale_enabled) > { > + /* Inserted modes copy the native sync; a sink mode with equal totals > +may not */ > if (scale_enabled || ( > native_mode->clock == drm_mode->clock && > native_mode->htotal == drm_mode->htotal && > - native_mode->vtotal == drm_mode->vtotal)) { > + native_mode->vtotal == drm_mode->vtotal && > + native_mode->hsync_start == drm_mode->hsync_start && > + native_mode->hsync_end == drm_mode->hsync_end && > + native_mode->vsync_start == drm_mode->vsync_start && > + native_mode->vsync_end == drm_mode->vsync_end)) { > if (native_mode->crtc_clock) > copy_crtc_timing_for_drm_display_mode(native_mode, > drm_mode); > } else { > diff --git > a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c > b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c > index bd55d943e4e5..04e0afd7aa71 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_te > +++ st.c > @@ -1468,6 +1468,46 @@ static void > dm_test_decide_crtc_timing_no_crtc_clock(struct kunit *test) > KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 0); } > > +static void dm_test_decide_crtc_timing_sink_mode_same_totals(struct > +kunit *test) { > + struct drm_display_mode native_mode = { > + DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, > 3840, 4016, 4104, > + 4400, 0, 2160, 2168, 2178, 2250, 0, > + DRM_MODE_FLAG_PHSYNC | > DRM_MODE_FLAG_PVSYNC) }; > + struct drm_display_mode drm_mode = { > + DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, > 4096, 4184, 4272, > + 4400, 0, 2160, 2168, 2178, 2250, 0, > + DRM_MODE_FLAG_PHSYNC | > DRM_MODE_FLAG_PVSYNC) }; > + > + drm_mode_set_crtcinfo(&native_mode, 0); > + drm_mode_set_crtcinfo(&drm_mode, 0); > + > + decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, > +false); > + > + KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 4096); > + KUNIT_EXPECT_EQ(test, drm_mode.crtc_hsync_start, 4184); } > + > +static void dm_test_decide_crtc_timing_inserted_mode(struct kunit > +*test) { > + struct drm_display_mode native_mode = { > + DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, > 3840, 4016, 4104, > + 4400, 0, 2160, 2168, 2178, 2250, 0, > + DRM_MODE_FLAG_PHSYNC | > DRM_MODE_FLAG_PVSYNC) }; > + struct drm_display_mode drm_mode; > + > + drm_mode_set_crtcinfo(&native_mode, 0); > + drm_mode = native_mode; > + drm_mode.hdisplay = 1920; > + drm_mode.vdisplay = 1080; > + drm_mode_set_crtcinfo(&drm_mode, 0); > + > + decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, > +false); > + > + KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 3840); > + KUNIT_EXPECT_EQ(test, drm_mode.crtc_vdisplay, 2160); } > + > /* Tests for amdgpu_dm_connector_funcs_reset() */ > > static const struct drm_connector_funcs dm_test_connector_funcs = { @@ - > 10342,6 +10382,8 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { > KUNIT_CASE(dm_test_decide_crtc_timing_matching_mode), > KUNIT_CASE(dm_test_decide_crtc_timing_no_copy), > KUNIT_CASE(dm_test_decide_crtc_timing_no_crtc_clock), > + KUNIT_CASE(dm_test_decide_crtc_timing_sink_mode_same_totals), > + KUNIT_CASE(dm_test_decide_crtc_timing_inserted_mode), > /* amdgpu_dm_connector_funcs_reset */ > KUNIT_CASE(dm_test_funcs_reset_sets_defaults), > KUNIT_CASE(dm_test_funcs_reset_edp_abm_level), > -- > 2.55.0
