From: Stylon Wang <stylon.w...@amd.com>

[Why]
Add necessary support for Freesync HDMI in Linux DM

[How]
- Support Freesync HDMI by calling DC interace
- Report Freesync capability to vrr_range debugfs from DRM
- Depends on coming DMCU/DMUB firmware to enable feature

Signed-off-by: Stylon Wang <stylon.w...@amd.com>
Reviewed-by: Nicholas Kazlauskas <nicholas.kazlaus...@amd.com>
Acked-by: Qingqing Zhuo <qingqing.z...@amd.com>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 175 ++++++++++++++----
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |   8 +
 2 files changed, 144 insertions(+), 39 deletions(-)

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 626a8cc92d65..c55ee0a24c26 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -34,6 +34,7 @@
 #include "dc/inc/hw/dmcu.h"
 #include "dc/inc/hw/abm.h"
 #include "dc/dc_dmub_srv.h"
+#include "dc/dc_edid_parser.h"
 #include "amdgpu_dm_trace.h"
 
 #include "vid.h"
@@ -6995,6 +6996,12 @@ static void amdgpu_dm_connector_ddc_get_modes(struct 
drm_connector *connector,
                 */
                drm_mode_sort(&connector->probed_modes);
                amdgpu_dm_get_native_mode(connector);
+
+               /* Freesync capabilities are reset by calling
+                * drm_add_edid_modes() and need to be
+                * restored here.
+                */
+               amdgpu_dm_update_freesync_caps(connector, edid);
        } else {
                amdgpu_dm_connector->num_modes = 0;
        }
@@ -9718,11 +9725,84 @@ static bool is_dp_capable_without_timing_msa(struct dc 
*dc,
 
        return capable;
 }
+
+static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
+               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) {
+               bool res;
+               int offset;
+
+               /* send 8 bytes a time */
+               if (!dc_edid_parser_send_cea(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);
+                       if (res) {
+                               /* amd vsdb found */
+                               vsdb_info->freesync_supported = 1;
+                               vsdb_info->amd_vsdb_version = version;
+                               vsdb_info->min_refresh_rate_hz = min_rate;
+                               vsdb_info->max_refresh_rate_hz = max_rate;
+                               return true;
+                       }
+                       /* not amd vsdb */
+                       return false;
+               }
+
+               /* check for ack*/
+               res = dc_edid_parser_recv_cea_ack(dc, &offset);
+               if (!res)
+                       return false;
+       }
+
+       return false;
+}
+
+static bool parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+               struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+       uint8_t *edid_ext = NULL;
+       int i;
+       bool valid_vsdb_found = false;
+
+       /*----- drm_find_cea_extension() -----*/
+       /* No EDID or EDID extensions */
+       if (edid == NULL || edid->extensions == 0)
+               return false;
+
+       /* Find CEA extension */
+       for (i = 0; i < edid->extensions; i++) {
+               edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1);
+               if (edid_ext[0] == CEA_EXT)
+                       break;
+       }
+
+       if (i == edid->extensions)
+               return false;
+
+       /*----- cea_db_offsets() -----*/
+       if (edid_ext[0] != CEA_EXT)
+               return false;
+
+       valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, 
vsdb_info);
+       return valid_vsdb_found;
+}
+
 void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
                                        struct edid *edid)
 {
        int i;
-       bool edid_check_required;
        struct detailed_timing *timing;
        struct detailed_non_pixel *data;
        struct detailed_data_monitor_range *range;
@@ -9733,6 +9813,8 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector 
*connector,
        struct drm_device *dev = connector->dev;
        struct amdgpu_device *adev = drm_to_adev(dev);
        bool freesync_capable = false;
+       struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
+       bool hdmi_valid_vsdb_found = false;
 
        if (!connector->state) {
                DRM_ERROR("%s - Connector has no state", __func__);
@@ -9751,60 +9833,75 @@ void amdgpu_dm_update_freesync_caps(struct 
drm_connector *connector,
 
        dm_con_state = to_dm_connector_state(connector->state);
 
-       edid_check_required = false;
        if (!amdgpu_dm_connector->dc_sink) {
                DRM_ERROR("dc_sink NULL, could not add free_sync module.\n");
                goto update;
        }
        if (!adev->dm.freesync_module)
                goto update;
-       /*
-        * if edid non zero restrict freesync only for dp and edp
-        */
-       if (edid) {
-               if (amdgpu_dm_connector->dc_sink->sink_signal == 
SIGNAL_TYPE_DISPLAY_PORT
-                       || amdgpu_dm_connector->dc_sink->sink_signal == 
SIGNAL_TYPE_EDP) {
+
+
+       if (amdgpu_dm_connector->dc_sink->sink_signal == 
SIGNAL_TYPE_DISPLAY_PORT
+               || amdgpu_dm_connector->dc_sink->sink_signal == 
SIGNAL_TYPE_EDP) {
+               bool edid_check_required = false;
+
+               if (edid) {
                        edid_check_required = is_dp_capable_without_timing_msa(
                                                adev->dm.dc,
                                                amdgpu_dm_connector);
                }
-       }
-       if (edid_check_required == true && (edid->version > 1 ||
-          (edid->version == 1 && edid->revision > 1))) {
-               for (i = 0; i < 4; i++) {
 
-                       timing  = &edid->detailed_timings[i];
-                       data    = &timing->data.other_data;
-                       range   = &data->data.range;
-                       /*
-                        * Check if monitor has continuous frequency mode
-                        */
-                       if (data->type != EDID_DETAIL_MONITOR_RANGE)
-                               continue;
-                       /*
-                        * Check for flag range limits only. If flag == 1 then
-                        * no additional timing information provided.
-                        * Default GTF, GTF Secondary curve and CVT are not
-                        * supported
-                        */
-                       if (range->flags != 1)
-                               continue;
+               if (edid_check_required == true && (edid->version > 1 ||
+                  (edid->version == 1 && edid->revision > 1))) {
+                       for (i = 0; i < 4; i++) {
+
+                               timing  = &edid->detailed_timings[i];
+                               data    = &timing->data.other_data;
+                               range   = &data->data.range;
+                               /*
+                                * Check if monitor has continuous frequency 
mode
+                                */
+                               if (data->type != EDID_DETAIL_MONITOR_RANGE)
+                                       continue;
+                               /*
+                                * Check for flag range limits only. If flag == 
1 then
+                                * no additional timing information provided.
+                                * Default GTF, GTF Secondary curve and CVT are 
not
+                                * supported
+                                */
+                               if (range->flags != 1)
+                                       continue;
 
-                       amdgpu_dm_connector->min_vfreq = range->min_vfreq;
-                       amdgpu_dm_connector->max_vfreq = range->max_vfreq;
-                       amdgpu_dm_connector->pixel_clock_mhz =
-                               range->pixel_clock_mhz * 10;
+                               amdgpu_dm_connector->min_vfreq = 
range->min_vfreq;
+                               amdgpu_dm_connector->max_vfreq = 
range->max_vfreq;
+                               amdgpu_dm_connector->pixel_clock_mhz =
+                                       range->pixel_clock_mhz * 10;
 
-                       connector->display_info.monitor_range.min_vfreq = 
range->min_vfreq;
-                       connector->display_info.monitor_range.max_vfreq = 
range->max_vfreq;
+                               connector->display_info.monitor_range.min_vfreq 
= range->min_vfreq;
+                               connector->display_info.monitor_range.max_vfreq 
= range->max_vfreq;
 
-                       break;
-               }
+                               break;
+                       }
 
-               if (amdgpu_dm_connector->max_vfreq -
-                   amdgpu_dm_connector->min_vfreq > 10) {
+                       if (amdgpu_dm_connector->max_vfreq -
+                           amdgpu_dm_connector->min_vfreq > 10) {
 
-                       freesync_capable = true;
+                               freesync_capable = true;
+                       }
+               }
+       } else if (edid && amdgpu_dm_connector->dc_sink->sink_signal == 
SIGNAL_TYPE_HDMI_TYPE_A) {
+               hdmi_valid_vsdb_found = 
parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
+               if (hdmi_valid_vsdb_found && vsdb_info.freesync_supported) {
+                       timing  = &edid->detailed_timings[i];
+                       data    = &timing->data.other_data;
+
+                       amdgpu_dm_connector->min_vfreq = 
vsdb_info.min_refresh_rate_hz;
+                       amdgpu_dm_connector->max_vfreq = 
vsdb_info.max_refresh_rate_hz;
+                       if (amdgpu_dm_connector->max_vfreq - 
amdgpu_dm_connector->min_vfreq > 10)
+                               freesync_capable = true;
+
+                       connector->display_info.monitor_range.min_vfreq = 
vsdb_info.min_refresh_rate_hz;
+                       connector->display_info.monitor_range.max_vfreq = 
vsdb_info.max_refresh_rate_hz;
                }
        }
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index 38bc0f88b29c..5f9950fd216c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -475,6 +475,14 @@ struct dm_connector_state {
        uint64_t pbn;
 };
 
+struct amdgpu_hdmi_vsdb_info {
+       unsigned int amd_vsdb_version;          /* VSDB version, should be used 
to determine which VSIF to send */
+       bool freesync_supported;                /* FreeSync Supported */
+       unsigned int min_refresh_rate_hz;       /* FreeSync Minimum Refresh 
Rate in Hz */
+       unsigned int max_refresh_rate_hz;       /* FreeSync Maximum Refresh 
Rate in Hz */
+};
+
+
 #define to_dm_connector_state(x)\
        container_of((x), struct dm_connector_state, base)
 
-- 
2.17.1

_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

Reply via email to