Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 153 additions & 51 deletions drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
if (amdgpu_dc_debug_mask & DC_SKIP_DETECTION_LT)
adev->dm.dc->debug.skip_detection_link_training = true;

if (amdgpu_dc_debug_mask & DC_OVERRIDE_PCON_VRR_ID_CHECK)
adev->dm.dc->debug.override_pcon_vrr_id_check = true;

adev->dm.dc->debug.visual_confirm = amdgpu_dc_visual_confirm;

/* TODO: Remove after DP2 receiver gets proper support of Cable ID feature */
Expand Down Expand Up @@ -7279,7 +7282,7 @@ create_stream_for_sink(struct drm_connector *connector,
update_stream_signal(stream, sink);

if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket);
mod_build_hf_vsif_infopacket(stream, &stream->hfvsif_infopacket);

if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
Expand Down Expand Up @@ -9359,7 +9362,8 @@ static void update_freesync_state_on_stream(

aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context;

if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) {
if (aconn && (aconn->as_type == ADAPTIVE_SYNC_TYPE_PCON_ALLOWED ||
aconn->vsdb_info.replay_mode)) {
pack_sdp_v1_3 = aconn->pack_sdp_v1_3;

if (aconn->vsdb_info.amd_vsdb_version == 1)
Expand All @@ -9373,6 +9377,9 @@ static void update_freesync_state_on_stream(
&new_stream->adaptive_sync_infopacket);
}

if (aconn && aconn->as_type == ADAPTIVE_SYNC_TYPE_HDMI)
packet_type = PACKET_TYPE_VTEM;

mod_freesync_build_vrr_infopacket(
dm->freesync_module,
new_stream,
Expand Down Expand Up @@ -12861,8 +12868,8 @@ static void parse_edid_displayid_vrr(struct drm_connector *connector,
}
}

static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector,
const struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
static int parse_amd_vsdb_did(struct amdgpu_dm_connector *aconnector,
const struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
{
u8 *edid_ext = NULL;
int i;
Expand All @@ -12878,6 +12885,9 @@ static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector,
break;
}

if (i == edid->extensions)
return false;

while (j < EDID_LENGTH - sizeof(struct amd_vsdb_block)) {
struct amd_vsdb_block *amd_vsdb = (struct amd_vsdb_block *)&edid_ext[j];
unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | (amd_vsdb->ieee_id[1] << 8) | (amd_vsdb->ieee_id[0]);
Expand All @@ -12896,9 +12906,9 @@ static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector,
return false;
}

static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
const struct edid *edid,
struct amdgpu_hdmi_vsdb_info *vsdb_info)
static int parse_amd_vsdb_cea(struct amdgpu_dm_connector *aconnector,
const struct edid *edid,
struct amdgpu_hdmi_vsdb_info *vsdb_info)
{
u8 *edid_ext = NULL;
int i;
Expand Down Expand Up @@ -12928,6 +12938,82 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
return valid_vsdb_found ? i : -ENODEV;
}

static bool is_monitor_range_invalid(struct drm_connector *conn)
{
return conn->display_info.monitor_range.min_vfreq == 0 ||
conn->display_info.monitor_range.max_vfreq == 0;
}

/**
* Returns true if (max_vfreq - min_vfreq) > 10
*/
static bool is_freesync_capable(struct drm_monitor_range_info *range)
{
return (range->max_vfreq - range->min_vfreq) > 10;
}

static void monitor_range_from_vsdb(struct drm_connector *conn,
struct amdgpu_hdmi_vsdb_info *vsdb)
{
struct drm_monitor_range_info *range = &conn->display_info.monitor_range;

range->min_vfreq = vsdb->min_refresh_rate_hz;
range->max_vfreq = vsdb->max_refresh_rate_hz;
}

/*
* Get VRR range from HDMI VRR info in EDID. If VRRmax == 0,
* try getting upper bound from AMD vsdb (if passed).
*
* @conn: drm_connector with HDMI VRR info
* @vsdb: AMD vsdb from CAE. Can be NULL if not found.
*/
static void monitor_range_from_hdmi(struct drm_connector *conn,
const struct amdgpu_hdmi_vsdb_info *vsdb)
{
struct drm_monitor_range_info *range = &conn->display_info.monitor_range;
struct drm_hdmi_vrr_cap *caps = &conn->display_info.hdmi.vrr_cap;
u16 vrr_max = caps->vrr_max;

/* Try getting upper vrr bound from AMD vsdb */
if (vrr_max == 0 && vsdb)
vrr_max = vsdb->max_refresh_rate_hz;

/* Use max possible BRR value as a last resort */
if (vrr_max == 0)
vrr_max = VTEM_BRR_MAX;

range->min_vfreq = caps->vrr_min;
range->max_vfreq = vrr_max;
}

/**
* Returns true if connector is capable of freesync
* Optionally, can fetch the range from AMD vsdb
*/
static bool copy_range_to_amdgpu_connector(struct drm_connector *conn)
{
struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn);
struct drm_monitor_range_info *range = &conn->display_info.monitor_range;

aconn->min_vfreq = range->min_vfreq;
aconn->max_vfreq = range->max_vfreq;

return is_freesync_capable(range);
}

/**
* Returns true if range from AMD vsdb is bigger
*/
static bool compare_ranges(struct drm_connector *conn,
struct amdgpu_hdmi_vsdb_info *vsdb)
{
struct drm_monitor_range_info *range = &conn->display_info.monitor_range;

return (vsdb->max_refresh_rate_hz - vsdb->min_refresh_rate_hz) >
(range->max_vfreq - range->min_vfreq);
}

/**
* amdgpu_dm_update_freesync_caps - Update Freesync capabilities
*
Expand All @@ -12942,16 +13028,21 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
const struct drm_edid *drm_edid)
{
int i = 0;
struct amdgpu_dm_connector *amdgpu_dm_connector =
to_amdgpu_dm_connector(connector);
struct dm_connector_state *dm_con_state = NULL;
struct dc_sink *sink;
struct amdgpu_device *adev = drm_to_adev(connector->dev);
struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
struct amdgpu_hdmi_vsdb_info vsdb_did = {0};
struct dpcd_caps dpcd_caps = {0};
struct drm_hdmi_vrr_cap *hdmi_vrr;
const struct edid *edid;
bool freesync_capable = false;
enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;
bool valid_vsdb_cea = false;
bool vsdb_freesync = false;
bool pcon_allowed = false;
bool is_pcon = false;

if (!connector->state) {
drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__);
Expand Down Expand Up @@ -12979,63 +13070,74 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version))
goto update;

/* Gather all data */
edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
valid_vsdb_cea = parse_amd_vsdb_cea(amdgpu_dm_connector, edid, &vsdb_info) >= 0;
vsdb_freesync = valid_vsdb_cea && vsdb_info.freesync_supported;
hdmi_vrr = &connector->display_info.hdmi.vrr_cap;

if (amdgpu_dm_connector->dc_link) {
dpcd_caps = amdgpu_dm_connector->dc_link->dpcd_caps;
is_pcon = dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER;
pcon_allowed = dm_helpers_is_vrr_pcon_allowed(
amdgpu_dm_connector->dc_link, connector->dev);
}

/* Some eDP panels only have the refresh rate range info in DisplayID */
if ((connector->display_info.monitor_range.min_vfreq == 0 ||
connector->display_info.monitor_range.max_vfreq == 0))
if (is_monitor_range_invalid(connector))
parse_edid_displayid_vrr(connector, edid);

if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
sink->sink_signal == SIGNAL_TYPE_EDP)) {
if (amdgpu_dm_connector->dc_link &&
amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
freesync_capable = true;
}
/* DP & eDP excluding PCONs */
if ((sink->sink_signal == SIGNAL_TYPE_EDP ||
sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) && !is_pcon) {
/*
* Many monitors expose AMD vsdb in CAE even for DP and their
* monitor ranges do not contain Range Limits Only flag
*/
if (valid_vsdb_cea && is_monitor_range_invalid(connector))
monitor_range_from_vsdb(connector, &vsdb_info);

parse_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
/* Use bigger range if found in AMD vsdb */
if (valid_vsdb_cea && compare_ranges(connector, &vsdb_info))
monitor_range_from_vsdb(connector, &vsdb_info);

if (vsdb_info.replay_mode) {
amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
}
if (dpcd_caps.allow_invalid_MSA_timing_param)
freesync_capable = copy_range_to_amdgpu_connector(connector);

} else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
if (i >= 0 && vsdb_info.freesync_supported) {
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;
/* eDP */
if (edid)
parse_amd_vsdb_did(amdgpu_dm_connector, edid, &vsdb_did);

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;
if (vsdb_did.replay_mode) {
amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_did.replay_mode;
amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_did.amd_vsdb_version;
amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
}
}

if (amdgpu_dm_connector->dc_link)
as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
/* HDMI */
} else if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
/* Prefer HDMI VRR */
if (hdmi_vrr->supported) {
amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_HDMI;
monitor_range_from_hdmi(connector, valid_vsdb_cea ? &vsdb_info : NULL);
} else if (vsdb_freesync)
monitor_range_from_vsdb(connector, &vsdb_info);

if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
if (i >= 0 && vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
freesync_capable = copy_range_to_amdgpu_connector(connector);

amdgpu_dm_connector->pack_sdp_v1_3 = true;
amdgpu_dm_connector->as_type = as_type;
/* DP -> HDMI PCON */
} else if (pcon_allowed) {
/* Prefer HDMI VRR */
if (hdmi_vrr->supported)
monitor_range_from_hdmi(connector, valid_vsdb_cea ? &vsdb_info : NULL);
else if (vsdb_freesync) {
amdgpu_dm_connector->vsdb_info = vsdb_info;

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;
monitor_range_from_vsdb(connector, &vsdb_info);
}

amdgpu_dm_connector->pack_sdp_v1_3 = true;
amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_PCON_ALLOWED;
freesync_capable = copy_range_to_amdgpu_connector(connector);
}

update:
Expand Down
49 changes: 23 additions & 26 deletions drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
edid_caps->display_name,
AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);

edid_caps->edid_hdmi = connector->display_info.is_hdmi;
if (connector->display_info.is_hdmi) {
edid_caps->edid_hdmi = true;
edid_caps->allm = connector->display_info.hdmi.allm;
edid_caps->fva = connector->display_info.hdmi.vrr_cap.fva;
edid_caps->hdmi_vrr = connector->display_info.hdmi.vrr_cap.supported;
}

apply_edid_quirks(dev, edid_buf, edid_caps);

Expand Down Expand Up @@ -1366,40 +1371,32 @@ void dm_helpers_dp_mst_update_branch_bandwidth(
// TODO
}

static bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id)
bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link, const struct drm_device *dev)
{
bool ret_val = false;
if (link->dpcd_caps.dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER)
return false;

switch (branch_dev_id) {
if (!link->dpcd_caps.allow_invalid_MSA_timing_param)
return false;

if (!link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT)
return false;

switch (link->dpcd_caps.branch_dev_id) {
case DP_BRANCH_DEVICE_ID_0060AD:
case DP_BRANCH_DEVICE_ID_00E04C:
case DP_BRANCH_DEVICE_ID_90CC24:
ret_val = true;
break;
default:
break;
case DP_BRANCH_DEVICE_ID_2B02F0:
return true;
}

return ret_val;
}

enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link)
{
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;

switch (dpcd_caps->dongle_type) {
case DISPLAY_DONGLE_DP_HDMI_CONVERTER:
if (dpcd_caps->adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT == true &&
dpcd_caps->allow_invalid_MSA_timing_param == true &&
dm_is_freesync_pcon_whitelist(dpcd_caps->branch_dev_id))
as_type = FREESYNC_TYPE_PCON_IN_WHITELIST;
break;
default:
break;
if (link->dc->debug.override_pcon_vrr_id_check) {
drm_info(dev, "Overriding VRR PCON check for ID: 0x%06x\n",
link->dpcd_caps.branch_dev_id);
return true;
}

return as_type;
return false;
}

bool dm_helpers_is_fullscreen(struct dc_context *ctx, struct dc_stream_state *stream)
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/amd/display/dc/core/dc_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -4409,6 +4409,7 @@ static void set_avi_info_frame(
unsigned int rid = pipe_ctx->stream->timing.rid;
unsigned int fr_ind = pipe_ctx->stream->timing.fr_index;
enum dc_timing_3d_format format;
bool allm;

if (stream->avi_infopacket.valid) {
*info_packet = stream->avi_infopacket;
Expand Down Expand Up @@ -4563,8 +4564,9 @@ static void set_avi_info_frame(
if (pipe_ctx->stream->timing.hdmi_vic != 0)
vic = 0;
format = stream->timing.timing_3d_format;
allm = stream->link->local_sink->edid_caps.allm;
/*todo, add 3DStereo support*/
if (format != TIMING_3D_FORMAT_NONE) {
if ((format != TIMING_3D_FORMAT_NONE) || allm) {
// Based on HDMI specs hdmi vic needs to be converted to cea vic when 3D is enabled
switch (pipe_ctx->stream->timing.hdmi_vic) {
case 1:
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/amd/display/dc/dc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,7 @@ struct dc_debug_options {
bool scl_reset_length10;
bool hdmi20_disable;
bool skip_detection_link_training;
bool override_pcon_vrr_id_check;
uint32_t edid_read_retry_times;
unsigned int force_odm_combine; //bit vector based on otg inst
unsigned int seamless_boot_odm_combine;
Expand Down
Loading