Atomic DPMS can leave a connector assigned to an enabled but inactive
CRTC after the driver has removed its DC stream. A later atomic commit
can then change HDR_OUTPUT_METADATA or Broadcast RGB while leaving the
CRTC mode, routing and active state unchanged.

That property-only commit does not require a modeset, so it reaches the
connector stream-update path with a NULL stream. An HDR metadata change
passes the update bailout and reaches dc_stream_get_status(NULL), while
a Broadcast RGB change dereferences the stream earlier when comparing
its output color space.

Skip stream updates until a stream exists. The connector state remains
stored, and an enabling modeset applies it to the newly created stream.

The crash was reproduced on a Phoenix1 system with DCN 3.1.4 and Linux
7.2-rc5 using a one-shot AI-generated DRM atomic reproducer:

https://pastebin.com/KxT1BcSs

The reproducer first sets HDR_OUTPUT_METADATA on an active CRTC, then
sets CRTC ACTIVE=0 to remove the DC stream while keeping the connector
routed to the CRTC, and finally changes HDR_OUTPUT_METADATA from one
non-NULL blob to another. The last commit does not require a modeset and
reached dc_stream_get_status(NULL) from amdgpu_dm_atomic_commit_tail().
The same sequence completed without crashing with this fix applied.

Fixes: b232d4ed92ea ("drm/amd/display: Only force modesets when toggling HDR")
Cc: [email protected]
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: David Weber <[email protected]>
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 1820547b1dde..13101a6be437 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -11519,6 +11519,9 @@ static void amdgpu_dm_atomic_commit_tail(struct 
drm_atomic_commit *state)
 
                dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
                dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
+               /* DPMS-off leaves the connector routed to a streamless CRTC. */
+               if (!dm_new_crtc_state->stream)
+                       continue;
 
                scaling_changed = is_scaling_state_different(dm_new_con_state,
                                                             dm_old_con_state);
-- 
2.54.0

Reply via email to