Commit f9b4f20c authored by Stylon Wang's avatar Stylon Wang Committed by Alex Deucher
Browse files

drm/amd/display: Add Freesync HDMI support to DM



[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: default avatarStylon Wang <stylon.wang@amd.com>
Reviewed-by: default avatarNicholas Kazlauskas <Nicholas.Kazlauskas@amd.com>
Acked-by: default avatarQingqing Zhuo <Qingqing.Zhuo@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 00e9d4c0
Loading
Loading
Loading
Loading
+136 −39
Original line number Diff line number Diff line
@@ -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"
@@ -6908,6 +6909,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;
	}
@@ -9627,11 +9634,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;
@@ -9642,6 +9722,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__);
@@ -9660,24 +9742,24 @@ 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) {
		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++) {
@@ -9716,6 +9798,21 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
				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;
		}
	}

update:
	if (dm_con_state)
+8 −0
Original line number Diff line number Diff line
@@ -474,6 +474,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)