]> git.baikalelectronics.ru Git - kernel.git/commitdiff
drm/amd/display: Fix incorrectly pruned modes with deep color
authorStylon Wang <stylon.wang@amd.com>
Thu, 30 Apr 2020 08:40:09 +0000 (16:40 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 21 May 2020 16:48:43 +0000 (12:48 -0400)
[Why]
When "max bpc" is set to enable deep color, some modes are removed from
the list if they fail validation on max bpc. These modes should be kept
if they validates fine with lower bpc.

[How]
- Retry with lower bpc in mode validation.
- Same in atomic commit to apply working bpc, not necessarily max bpc.

Signed-off-by: Stylon Wang <stylon.wang@amd.com>
Reviewed-by: Nicholas Kazlauskas <Nicholas.Kazlauskas@amd.com>
Acked-by: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c

index 69014d24431f7b67f1cf43d5e425d190deec668f..dfb6fbaf01e9eb95c055e02f972635f08878b7c7 100644 (file)
@@ -3840,8 +3840,7 @@ static void update_stream_scaling_settings(const struct drm_display_mode *mode,
 
 static enum dc_color_depth
 convert_color_depth_from_display_info(const struct drm_connector *connector,
-                                     const struct drm_connector_state *state,
-                                     bool is_y420)
+                                     bool is_y420, int requested_bpc)
 {
        uint8_t bpc;
 
@@ -3861,10 +3860,7 @@ convert_color_depth_from_display_info(const struct drm_connector *connector,
                bpc = bpc ? bpc : 8;
        }
 
-       if (!state)
-               state = connector->state;
-
-       if (state) {
+       if (requested_bpc > 0) {
                /*
                 * Cap display bpc based on the user requested value.
                 *
@@ -3873,7 +3869,7 @@ convert_color_depth_from_display_info(const struct drm_connector *connector,
                 * or if this was called outside of atomic check, so it
                 * can't be used directly.
                 */
-               bpc = min(bpc, state->max_requested_bpc);
+               bpc = min_t(u8, bpc, requested_bpc);
 
                /* Round down to the nearest even number. */
                bpc = bpc - (bpc & 1);
@@ -3995,7 +3991,8 @@ static void fill_stream_properties_from_drm_display_mode(
        const struct drm_display_mode *mode_in,
        const struct drm_connector *connector,
        const struct drm_connector_state *connector_state,
-       const struct dc_stream_state *old_stream)
+       const struct dc_stream_state *old_stream,
+       int requested_bpc)
 {
        struct dc_crtc_timing *timing_out = &stream->timing;
        const struct drm_display_info *info = &connector->display_info;
@@ -4025,8 +4022,9 @@ static void fill_stream_properties_from_drm_display_mode(
 
        timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
        timing_out->display_color_depth = convert_color_depth_from_display_info(
-               connector, connector_state,
-               (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420));
+               connector,
+               (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420),
+               requested_bpc);
        timing_out->scan_type = SCANNING_TYPE_NODATA;
        timing_out->hdmi_vic = 0;
 
@@ -4232,7 +4230,8 @@ static struct dc_stream_state *
 create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
                       const struct drm_display_mode *drm_mode,
                       const struct dm_connector_state *dm_state,
-                      const struct dc_stream_state *old_stream)
+                      const struct dc_stream_state *old_stream,
+                      int requested_bpc)
 {
        struct drm_display_mode *preferred_mode = NULL;
        struct drm_connector *drm_connector;
@@ -4317,10 +4316,10 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
        */
        if (!scale || mode_refresh != preferred_refresh)
                fill_stream_properties_from_drm_display_mode(stream,
-                       &mode, &aconnector->base, con_state, NULL);
+                       &mode, &aconnector->base, con_state, NULL, requested_bpc);
        else
                fill_stream_properties_from_drm_display_mode(stream,
-                       &mode, &aconnector->base, con_state, old_stream);
+                       &mode, &aconnector->base, con_state, old_stream, requested_bpc);
 
        stream->timing.flags.DSC = 0;
 
@@ -4839,16 +4838,54 @@ static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector)
        create_eml_sink(aconnector);
 }
 
+static struct dc_stream_state *
+create_validate_stream_for_sink(struct amdgpu_dm_connector *aconnector,
+                               const struct drm_display_mode *drm_mode,
+                               const struct dm_connector_state *dm_state,
+                               const struct dc_stream_state *old_stream)
+{
+       struct drm_connector *connector = &aconnector->base;
+       struct amdgpu_device *adev = connector->dev->dev_private;
+       struct dc_stream_state *stream;
+       int requested_bpc = connector->state ? connector->state->max_requested_bpc : 8;
+       enum dc_status dc_result = DC_OK;
+
+       do {
+               stream = create_stream_for_sink(aconnector, drm_mode,
+                                               dm_state, old_stream,
+                                               requested_bpc);
+               if (stream == NULL) {
+                       DRM_ERROR("Failed to create stream for sink!\n");
+                       break;
+               }
+
+               dc_result = dc_validate_stream(adev->dm.dc, stream);
+
+               if (dc_result != DC_OK) {
+                       DRM_DEBUG_KMS("Mode %dx%d (clk %d) failed DC validation with error %d\n",
+                                     drm_mode->hdisplay,
+                                     drm_mode->vdisplay,
+                                     drm_mode->clock,
+                                     dc_result);
+
+                       dc_stream_release(stream);
+                       stream = NULL;
+                       requested_bpc -= 2; /* lower bpc to retry validation */
+               }
+
+       } while (stream == NULL && requested_bpc >= 6);
+
+       return stream;
+}
+
 enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
                                   struct drm_display_mode *mode)
 {
        int result = MODE_ERROR;
        struct dc_sink *dc_sink;
-       struct amdgpu_device *adev = connector->dev->dev_private;
        /* TODO: Unhardcode stream count */
        struct dc_stream_state *stream;
        struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-       enum dc_status dc_result = DC_OK;
 
        if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
                        (mode->flags & DRM_MODE_FLAG_DBLSCAN))
@@ -4869,24 +4906,11 @@ enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connec
                goto fail;
        }
 
-       stream = create_stream_for_sink(aconnector, mode, NULL, NULL);
-       if (stream == NULL) {
-               DRM_ERROR("Failed to create stream for sink!\n");
-               goto fail;
-       }
-
-       dc_result = dc_validate_stream(adev->dm.dc, stream);
-
-       if (dc_result == DC_OK)
+       stream = create_validate_stream_for_sink(aconnector, mode, NULL, NULL);
+       if (stream) {
+               dc_stream_release(stream);
                result = MODE_OK;
-       else
-               DRM_DEBUG_KMS("Mode %dx%d (clk %d) failed DC validation with error %d\n",
-                             mode->hdisplay,
-                             mode->vdisplay,
-                             mode->clock,
-                             dc_result);
-
-       dc_stream_release(stream);
+       }
 
 fail:
        /* TODO: error handling*/
@@ -5209,10 +5233,12 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
                return 0;
 
        if (!state->duplicated) {
+               int max_bpc = conn_state->max_requested_bpc;
                is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) &&
                                aconnector->force_yuv420_output;
-               color_depth = convert_color_depth_from_display_info(connector, conn_state,
-                                                                   is_y420);
+               color_depth = convert_color_depth_from_display_info(connector,
+                                                                   is_y420,
+                                                                   max_bpc);
                bpp = convert_dc_color_depth_into_bpc(color_depth) * 3;
                clock = adjusted_mode->clock;
                dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp, false);
@@ -7642,10 +7668,10 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
                if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
                        goto skip_modeset;
 
-               new_stream = create_stream_for_sink(aconnector,
-                                                    &new_crtc_state->mode,
-                                                   dm_new_conn_state,
-                                                   dm_old_crtc_state->stream);
+               new_stream = create_validate_stream_for_sink(aconnector,
+                                                            &new_crtc_state->mode,
+                                                            dm_new_conn_state,
+                                                            dm_old_crtc_state->stream);
 
                /*
                 * we can have no stream on ACTION_SET if a display