drm/amd/display: Support "Broadcast RGB" drm property
authorYan Li <yan.li@amd.com>
Tue, 7 Jan 2025 14:28:16 +0000 (09:28 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 13 Feb 2025 02:02:57 +0000 (21:02 -0500)
[WHY]
The source device outputs a full RGB signal, but TV may
be set to use limited RGB. The mismatch in color
range leads to a degradation in image quality.
Display driver should have the ability to switch
between the full and limited RGB to match TV's settings.

[HOW]
Add support of the linux DRM "Broadcast RGB" property, which
indicates the Quantization Range (Full vs Limited) used.
User space can set this property to be "Automatic", "Full"
or "Limited 16:235" to adjust the output color range.

Reviewed-by: Jerry Zuo <jerry.zuo@amd.com>
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Yan Li <yan.li@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/dc/core/dc.c

index 508e5a6aa8df3797846021b9432b9a6197c5abf8..4210147747c96bc1ff7f9ed0bada51b34a79fca9 100644 (file)
@@ -6110,6 +6110,8 @@ get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
        default:
                if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
                        color_space = COLOR_SPACE_SRGB;
+                       if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
+                               color_space = COLOR_SPACE_SRGB_LIMITED;
                /*
                 * 27030khz is the separation point between HDTV and SDTV
                 * according to HDMI spec, we use YCbCr709 and YCbCr601
@@ -8265,6 +8267,10 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
                                dm->ddev->mode_config.scaling_mode_property,
                                DRM_MODE_SCALE_NONE);
 
+       if (connector_type == DRM_MODE_CONNECTOR_HDMIA
+               || (connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root))
+               drm_connector_attach_broadcast_rgb_property(&aconnector->base);
+
        drm_object_attach_property(&aconnector->base.base,
                                adev->mode_info.underscan_property,
                                UNDERSCAN_OFF);
@@ -10035,7 +10041,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
                struct dc_stream_update stream_update;
                struct dc_info_packet hdr_packet;
                struct dc_stream_status *status = NULL;
-               bool abm_changed, hdr_changed, scaling_changed;
+               bool abm_changed, hdr_changed, scaling_changed, output_color_space_changed = false;
 
                memset(&stream_update, 0, sizeof(stream_update));
 
@@ -10054,13 +10060,18 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
                scaling_changed = is_scaling_state_different(dm_new_con_state,
                                                             dm_old_con_state);
 
+               if ((new_con_state->hdmi.broadcast_rgb != old_con_state->hdmi.broadcast_rgb) &&
+                       (dm_old_crtc_state->stream->output_color_space !=
+                               get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state)))
+                       output_color_space_changed = true;
+
                abm_changed = dm_new_crtc_state->abm_level !=
                              dm_old_crtc_state->abm_level;
 
                hdr_changed =
                        !drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state);
 
-               if (!scaling_changed && !abm_changed && !hdr_changed)
+               if (!scaling_changed && !abm_changed && !hdr_changed && !output_color_space_changed)
                        continue;
 
                stream_update.stream = dm_new_crtc_state->stream;
@@ -10072,6 +10083,13 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
                        stream_update.dst = dm_new_crtc_state->stream->dst;
                }
 
+               if (output_color_space_changed) {
+                       dm_new_crtc_state->stream->output_color_space
+                               = get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state);
+
+                       stream_update.output_color_space = &dm_new_crtc_state->stream->output_color_space;
+               }
+
                if (abm_changed) {
                        dm_new_crtc_state->stream->abm_level = dm_new_crtc_state->abm_level;
 
index f84e795e35f586f826479cdfec1762ad2ba04471..edf7e3b52b28d13b720e8fc23d0dbbc0f99d2bc3 100644 (file)
@@ -2834,10 +2834,13 @@ static enum surface_update_type check_update_surfaces_for_stream(
                if (stream_update->sharpening_required)
                        su_flags->bits.sharpening_required = 1;
 
+               if (stream_update->output_color_space)
+                       su_flags->bits.out_csc = 1;
+
                if (su_flags->raw != 0)
                        overall_type = UPDATE_TYPE_FULL;
 
-               if (stream_update->output_csc_transform || stream_update->output_color_space)
+               if (stream_update->output_csc_transform)
                        su_flags->bits.out_csc = 1;
 
                /* Output transfer function changes do not require bandwidth recalculation,