drm/amd/display: Add Freesync HDMI support to DM with DMUB
authorStylon Wang <stylon.wang@amd.com>
Sat, 29 May 2021 06:19:20 +0000 (14:19 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 8 Jul 2021 19:14:55 +0000 (15:14 -0400)
[Why]
Changes in DM needed to support Freesync HDMI on DMUB.

[How]
Change implementation to parse CEA blocks in case of DMUB-enabled ASICs.

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>
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

index 01e1062..f69be12 100644 (file)
@@ -10549,13 +10549,68 @@ static bool is_dp_capable_without_timing_msa(struct dc *dc,
        return capable;
 }
 
-static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
+static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
+               unsigned int offset,
+               unsigned int total_length,
+               uint8_t *data,
+               unsigned int length,
+               struct amdgpu_hdmi_vsdb_info *vsdb)
+{
+       bool res;
+       union dmub_rb_cmd cmd;
+       struct dmub_cmd_send_edid_cea *input;
+       struct dmub_cmd_edid_cea_output *output;
+
+       if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
+               return false;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       input = &cmd.edid_cea.data.input;
+
+       cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
+       cmd.edid_cea.header.sub_type = 0;
+       cmd.edid_cea.header.payload_bytes =
+               sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
+       input->offset = offset;
+       input->length = length;
+       input->total_length = total_length;
+       memcpy(input->payload, data, length);
+
+       res = dc_dmub_srv_cmd_with_reply_data(dm->dc->ctx->dmub_srv, &cmd);
+       if (!res) {
+               DRM_ERROR("EDID CEA parser failed\n");
+               return false;
+       }
+
+       output = &cmd.edid_cea.data.output;
+
+       if (output->type == DMUB_CMD__EDID_CEA_ACK) {
+               if (!output->ack.success) {
+                       DRM_ERROR("EDID CEA ack failed at offset %d\n",
+                                       output->ack.offset);
+               }
+       } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
+               if (!output->amd_vsdb.vsdb_found)
+                       return false;
+
+               vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
+               vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
+               vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
+               vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
+       } else {
+               DRM_ERROR("Unknown EDID CEA parser results\n");
+               return false;
+       }
+
+       return true;
+}
+
+static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
                uint8_t *edid_ext, int len,
                struct amdgpu_hdmi_vsdb_info *vsdb_info)
 {
        int i;
-       struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
-       struct dc *dc = adev->dm.dc;
 
        /* send extension block to DMCU for parsing */
        for (i = 0; i < len; i += 8) {
@@ -10563,14 +10618,14 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
                int offset;
 
                /* send 8 bytes a time */
-               if (!dc_edid_parser_send_cea(dc, i, len, &edid_ext[i], 8))
+               if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8))
                        return false;
 
                if (i+8 == len) {
                        /* EDID block sent completed, expect result */
                        int version, min_rate, max_rate;
 
-                       res = dc_edid_parser_recv_amd_vsdb(dc, &version, &min_rate, &max_rate);
+                       res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
                        if (res) {
                                /* amd vsdb found */
                                vsdb_info->freesync_supported = 1;
@@ -10584,7 +10639,7 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
                }
 
                /* check for ack*/
-               res = dc_edid_parser_recv_cea_ack(dc, &offset);
+               res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
                if (!res)
                        return false;
        }
@@ -10592,6 +10647,34 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
        return false;
 }
 
+static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
+               uint8_t *edid_ext, int len,
+               struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+       int i;
+
+       /* send extension block to DMCU for parsing */
+       for (i = 0; i < len; i += 8) {
+               /* send 8 bytes a time */
+               if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
+                       return false;
+       }
+
+       return vsdb_info->freesync_supported;
+}
+
+static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
+               uint8_t *edid_ext, int len,
+               struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+       struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
+
+       if (adev->dm.dmub_srv)
+               return parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
+       else
+               return parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
+}
+
 static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
                struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
 {