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: Stylon Wang <stylon.wang@amd.com> Reviewed-by: Nicholas Kazlauskas <Nicholas.Kazlauskas@amd.com> Acked-by: Qingqing Zhuo <Qingqing.Zhuo@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
00e9d4c0ab
commit
f9b4f20c47
2 changed files with 143 additions and 38 deletions
|
@ -34,6 +34,7 @@
|
||||||
#include "dc/inc/hw/dmcu.h"
|
#include "dc/inc/hw/dmcu.h"
|
||||||
#include "dc/inc/hw/abm.h"
|
#include "dc/inc/hw/abm.h"
|
||||||
#include "dc/dc_dmub_srv.h"
|
#include "dc/dc_dmub_srv.h"
|
||||||
|
#include "dc/dc_edid_parser.h"
|
||||||
#include "amdgpu_dm_trace.h"
|
#include "amdgpu_dm_trace.h"
|
||||||
|
|
||||||
#include "vid.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);
|
drm_mode_sort(&connector->probed_modes);
|
||||||
amdgpu_dm_get_native_mode(connector);
|
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 {
|
} else {
|
||||||
amdgpu_dm_connector->num_modes = 0;
|
amdgpu_dm_connector->num_modes = 0;
|
||||||
}
|
}
|
||||||
|
@ -9627,11 +9634,84 @@ static bool is_dp_capable_without_timing_msa(struct dc *dc,
|
||||||
|
|
||||||
return capable;
|
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,
|
void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
|
||||||
struct edid *edid)
|
struct edid *edid)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
bool edid_check_required;
|
|
||||||
struct detailed_timing *timing;
|
struct detailed_timing *timing;
|
||||||
struct detailed_non_pixel *data;
|
struct detailed_non_pixel *data;
|
||||||
struct detailed_data_monitor_range *range;
|
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 drm_device *dev = connector->dev;
|
||||||
struct amdgpu_device *adev = drm_to_adev(dev);
|
struct amdgpu_device *adev = drm_to_adev(dev);
|
||||||
bool freesync_capable = false;
|
bool freesync_capable = false;
|
||||||
|
struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
|
||||||
|
bool hdmi_valid_vsdb_found = false;
|
||||||
|
|
||||||
if (!connector->state) {
|
if (!connector->state) {
|
||||||
DRM_ERROR("%s - Connector has no state", __func__);
|
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);
|
dm_con_state = to_dm_connector_state(connector->state);
|
||||||
|
|
||||||
edid_check_required = false;
|
|
||||||
if (!amdgpu_dm_connector->dc_sink) {
|
if (!amdgpu_dm_connector->dc_sink) {
|
||||||
DRM_ERROR("dc_sink NULL, could not add free_sync module.\n");
|
DRM_ERROR("dc_sink NULL, could not add free_sync module.\n");
|
||||||
goto update;
|
goto update;
|
||||||
}
|
}
|
||||||
if (!adev->dm.freesync_module)
|
if (!adev->dm.freesync_module)
|
||||||
goto update;
|
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
|
if (amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT
|
||||||
|| amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_EDP) {
|
|| 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(
|
edid_check_required = is_dp_capable_without_timing_msa(
|
||||||
adev->dm.dc,
|
adev->dm.dc,
|
||||||
amdgpu_dm_connector);
|
amdgpu_dm_connector);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (edid_check_required == true && (edid->version > 1 ||
|
if (edid_check_required == true && (edid->version > 1 ||
|
||||||
(edid->version == 1 && edid->revision > 1))) {
|
(edid->version == 1 && edid->revision > 1))) {
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
|
@ -9716,6 +9798,21 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update:
|
update:
|
||||||
if (dm_con_state)
|
if (dm_con_state)
|
||||||
|
|
|
@ -474,6 +474,14 @@ struct dm_connector_state {
|
||||||
uint64_t pbn;
|
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)\
|
#define to_dm_connector_state(x)\
|
||||||
container_of((x), struct dm_connector_state, base)
|
container_of((x), struct dm_connector_state, base)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue