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 6252afd1d087f..3dc4c3afe7bdf 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -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 */ @@ -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 || @@ -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) @@ -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, @@ -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; @@ -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]); @@ -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; @@ -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 * @@ -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__); @@ -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: diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index d0f770dd0a956..c6efca2ad9df5 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -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); @@ -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) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index bc5dedf5f60c3..b96e2f86f2ea0 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -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; @@ -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: diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 98f0b6b3c2136..86ed1c783158f 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -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; diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index b5aa03a3e39cf..012691adf72c1 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -206,9 +206,14 @@ struct dc_edid_caps { uint32_t max_tmds_clk_mhz; - /*HDMI 2.0 caps*/ + /* HDMI 2.0 caps */ bool lte_340mcsc_scramble; + /* HDMI 2.1 caps */ + bool allm; + bool fva; + bool hdmi_vrr; + bool edid_hdmi; bool hdr_supported; bool rr_capable; diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h index 9d160b39e8c5a..ea94c52d2b871 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h +++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h @@ -219,10 +219,10 @@ int dm_helpers_dmub_set_config_sync(struct dc_context *ctx, const struct dc_link *link, struct set_config_cmd_payload *payload, enum set_config_status *operation_result); -enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link); enum dc_edid_status dm_helpers_get_sbios_edid(struct dc_link *link, struct dc_edid *edid); +bool dm_helpers_is_vrr_pcon_allowed(const struct dc_link *link, const struct drm_device *dev); bool dm_helpers_is_fullscreen(struct dc_context *ctx, struct dc_stream_state *stream); bool dm_helpers_is_hdr_on(struct dc_context *ctx, struct dc_stream_state *stream); diff --git a/drivers/gpu/drm/amd/display/include/ddc_service_types.h b/drivers/gpu/drm/amd/display/include/ddc_service_types.h index 1c603b12957f6..e838f7c1269c4 100644 --- a/drivers/gpu/drm/amd/display/include/ddc_service_types.h +++ b/drivers/gpu/drm/amd/display/include/ddc_service_types.h @@ -36,6 +36,7 @@ #define DP_BRANCH_DEVICE_ID_006037 0x006037 #define DP_BRANCH_DEVICE_ID_001CF8 0x001CF8 #define DP_BRANCH_DEVICE_ID_0060AD 0x0060AD +#define DP_BRANCH_DEVICE_ID_2B02F0 0x2B02F0 /* Chrontel CH7218 */ #define DP_BRANCH_HW_REV_10 0x10 #define DP_BRANCH_HW_REV_20 0x20 diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 1aae46d703ba0..db197cf048e1b 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -27,6 +27,7 @@ #include "dc.h" #include "mod_freesync.h" #include "core_types.h" +#include "mod_info_packet.h" #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 @@ -955,6 +956,9 @@ void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, return; switch (packet_type) { + case PACKET_TYPE_VTEM: + mod_build_vtem_infopacket(stream, vrr, infopacket); + break; case PACKET_TYPE_FS_V3: build_vrr_infopacket_v3(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop); break; diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index 66dc9a19aebe5..2b4abfbd8488c 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -33,6 +33,8 @@ struct dc_stream_state; struct dc_info_packet; struct mod_vrr_params; +#define VTEM_BRR_MAX 1000 + void mod_build_vsc_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet, enum dc_color_space cs, @@ -41,12 +43,17 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet); +void mod_build_vtem_infopacket(const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket); + enum adaptive_sync_type { - ADAPTIVE_SYNC_TYPE_NONE = 0, - ADAPTIVE_SYNC_TYPE_DP = 1, - FREESYNC_TYPE_PCON_IN_WHITELIST = 2, - FREESYNC_TYPE_PCON_NOT_IN_WHITELIST = 3, - ADAPTIVE_SYNC_TYPE_EDP = 4, + ADAPTIVE_SYNC_TYPE_NONE = 0, + ADAPTIVE_SYNC_TYPE_DP = 1, + ADAPTIVE_SYNC_TYPE_PCON_ALLOWED = 2, + ADAPTIVE_SYNC_TYPE_PCON_NOT_ALLOWED = 3, + ADAPTIVE_SYNC_TYPE_EDP = 4, + ADAPTIVE_SYNC_TYPE_HDMI = 5, }; enum adaptive_sync_sdp_version { diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index b3d55cac35694..c9f639a2565d9 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -44,76 +44,59 @@ enum vsc_packet_revision { vsc_packet_rev5 = 5, }; +#define HDMI_INFOFRAME_TYPE_EMP 0x7F #define HDMI_INFOFRAME_TYPE_VENDOR 0x81 -#define HF_VSIF_VERSION 1 +#define HDMI_INFOFRAME_LENGTH_MASK 0x1F +#define HF_VSIF_VERSION 1 +#define HF_VSIF_3D_BIT 0 +#define HF_VSIF_ALLM_BIT 1 // VTEM Byte Offset -#define VTEM_PB0 0 -#define VTEM_PB1 1 -#define VTEM_PB2 2 -#define VTEM_PB3 3 -#define VTEM_PB4 4 -#define VTEM_PB5 5 -#define VTEM_PB6 6 - -#define VTEM_MD0 7 -#define VTEM_MD1 8 -#define VTEM_MD2 9 -#define VTEM_MD3 10 - - -// VTEM Byte Masks -//PB0 -#define MASK_VTEM_PB0__RESERVED0 0x01 -#define MASK_VTEM_PB0__SYNC 0x02 -#define MASK_VTEM_PB0__VFR 0x04 -#define MASK_VTEM_PB0__AFR 0x08 -#define MASK_VTEM_PB0__DS_TYPE 0x30 - //0: Periodic pseudo-static EM Data Set - //1: Periodic dynamic EM Data Set - //2: Unique EM Data Set - //3: Reserved -#define MASK_VTEM_PB0__END 0x40 -#define MASK_VTEM_PB0__NEW 0x80 - -//PB1 -#define MASK_VTEM_PB1__RESERVED1 0xFF - -//PB2 -#define MASK_VTEM_PB2__ORGANIZATION_ID 0xFF - //0: This is a Vendor Specific EM Data Set - //1: This EM Data Set is defined by This Specification (HDMI 2.1 r102.clean) - //2: This EM Data Set is defined by CTA-861-G - //3: This EM Data Set is defined by VESA -//PB3 -#define MASK_VTEM_PB3__DATA_SET_TAG_MSB 0xFF -//PB4 -#define MASK_VTEM_PB4__DATA_SET_TAG_LSB 0xFF -//PB5 -#define MASK_VTEM_PB5__DATA_SET_LENGTH_MSB 0xFF -//PB6 -#define MASK_VTEM_PB6__DATA_SET_LENGTH_LSB 0xFF - - - -//PB7-27 (20 bytes): -//PB7 = MD0 -#define MASK_VTEM_MD0__VRR_EN 0x01 -#define MASK_VTEM_MD0__M_CONST 0x02 -#define MASK_VTEM_MD0__QMS_EN 0x04 -#define MASK_VTEM_MD0__RESERVED2 0x08 -#define MASK_VTEM_MD0__FVA_FACTOR_M1 0xF0 - -//MD1 -#define MASK_VTEM_MD1__BASE_VFRONT 0xFF - -//MD2 -#define MASK_VTEM_MD2__BASE_REFRESH_RATE_98 0x03 -#define MASK_VTEM_MD2__RB 0x04 -#define MASK_VTEM_MD2__NEXT_TFR 0xF8 - -//MD3 -#define MASK_VTEM_MD3__BASE_REFRESH_RATE_07 0xFF +#define VTEM_ORG_ID 1 +#define VTEM_DATA_SET_TAG 1 +#define VTEM_DATA_SET_LENGTH 4 + +#define VTEM_M_CONST 0 +#define VTEM_FVA_FACTOR 0 + +#define VTEM_BRR_MASK_UPPER 0x03 +#define VTEM_BRR_MASK_LOWER 0xFF + +/* VTEM Byte Offset */ +#define VTEM_PB0 0 +#define VTEM_PB1 1 +#define VTEM_PB2 2 +#define VTEM_PB3 3 +#define VTEM_PB4 4 +#define VTEM_PB5 5 +#define VTEM_PB6 6 + +#define VTEM_MD0 7 +#define VTEM_MD1 8 +#define VTEM_MD2 9 +#define VTEM_MD3 10 + +/* Extended Metadata Packet */ +/* Header */ +#define EMP_LAST_BIT 6 +#define EMP_FIRST_BIT 7 +/* PB0 */ +#define EMP_SNC_BIT 1 +#define EMP_VFR_BIT 2 +#define EMP_AFR_BIT 3 +#define EMP_DST_BIT 4 +#define EMP_END_BIT 6 +#define EMP_NEW_BIT 7 +/* PB7 = MD0 */ +#define VTEM_VRR_BIT 0 +#define VTEM_M_CONST_BIT 1 +#define VTEM_FVA_BIT 4 +/* MD1 Base_Vfront */ +/* MD2 */ +#define VTEM_BRR_UPPER_BIT 0 +#define VTEM_RB_BIT 2 +/* MD3 BRR Lower */ + enum ColorimetryRGBDP { ColorimetryRGB_DP_sRGB = 0, @@ -441,9 +424,28 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, } } +static bool is_hdmi_vic_mode(const struct dc_stream_state *stream) +{ + bool allm = stream->link->local_sink->edid_caps.allm; + bool stereo = stream->view_format != VIEW_3D_FORMAT_NONE; + + if (stream->timing.hdmi_vic == 0) + return false; + + if (stream->timing.h_total < 3840 || + stream->timing.v_total < 2160) + return false; + + if (stereo || allm) + return false; + + return true; +} + /** * mod_build_hf_vsif_infopacket - Prepare HDMI Vendor Specific info frame. * Follows HDMI Spec to build up Vendor Specific info frame + * Conforms to h14b-vsif or hf-vsif based on the capabilities * * @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.) * @info_packet: output structure where to store VSIF @@ -451,63 +453,76 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet) { - unsigned int length = 5; bool hdmi_vic_mode = false; + bool allm = false; + bool stereo = false; uint8_t checksum = 0; - uint32_t i = 0; + uint8_t offset = 0; + uint8_t i = 0; + uint8_t length = 5; + uint32_t oui = HDMI_IEEE_OUI; enum dc_timing_3d_format format; info_packet->valid = false; - format = stream->timing.timing_3d_format; - if (stream->view_format == VIEW_3D_FORMAT_NONE) - format = TIMING_3D_FORMAT_NONE; - if (stream->timing.hdmi_vic != 0 - && stream->timing.h_total >= 3840 - && stream->timing.v_total >= 2160 - && format == TIMING_3D_FORMAT_NONE) - hdmi_vic_mode = true; + allm = stream->link->local_sink->edid_caps.allm; + format = stream->view_format == VIEW_3D_FORMAT_NONE ? + TIMING_3D_FORMAT_NONE : + stream->timing.timing_3d_format; + stereo = format != TIMING_3D_FORMAT_NONE; + hdmi_vic_mode = is_hdmi_vic_mode(stream); - if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode) + if (!stereo && !hdmi_vic_mode && !allm) return; - info_packet->sb[1] = 0x03; - info_packet->sb[2] = 0x0C; - info_packet->sb[3] = 0x00; + if (allm) + oui = HDMI_FORUM_IEEE_OUI; - if (format != TIMING_3D_FORMAT_NONE) - info_packet->sb[4] = (2 << 5); + info_packet->sb[1] = oui & 0xff; + info_packet->sb[2] = (oui >> 8) & 0xff; + info_packet->sb[3] = (oui >> 16) & 0xff; - else if (hdmi_vic_mode) - info_packet->sb[4] = (1 << 5); + if (oui == HDMI_FORUM_IEEE_OUI) { + offset = 2; + length += 2; + info_packet->sb[4] = HF_VSIF_VERSION; + info_packet->sb[5] = stereo << HF_VSIF_3D_BIT; + info_packet->sb[5] = allm << HF_VSIF_ALLM_BIT; + } - switch (format) { - case TIMING_3D_FORMAT_HW_FRAME_PACKING: - case TIMING_3D_FORMAT_SW_FRAME_PACKING: - info_packet->sb[5] = (0x0 << 4); - break; + if (stereo) { + info_packet->sb[4 + offset] = (2 << 5); - case TIMING_3D_FORMAT_SIDE_BY_SIDE: - case TIMING_3D_FORMAT_SBS_SW_PACKED: - info_packet->sb[5] = (0x8 << 4); - length = 6; - break; + switch (format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + info_packet->sb[5 + offset] = (0x0 << 4); + break; - case TIMING_3D_FORMAT_TOP_AND_BOTTOM: - case TIMING_3D_FORMAT_TB_SW_PACKED: - info_packet->sb[5] = (0x6 << 4); - break; + case TIMING_3D_FORMAT_SIDE_BY_SIDE: + case TIMING_3D_FORMAT_SBS_SW_PACKED: + info_packet->sb[5 + offset] = (0x8 << 4); + ++length; + break; - default: - break; - } + case TIMING_3D_FORMAT_TOP_AND_BOTTOM: + case TIMING_3D_FORMAT_TB_SW_PACKED: + info_packet->sb[5 + offset] = (0x6 << 4); + break; - if (hdmi_vic_mode) + default: + break; + } + + /* Doesn't need the offset as it can't be used with hf-vsif */ + } else if (hdmi_vic_mode) { + info_packet->sb[4] = (1 << 5); info_packet->sb[5] = stream->timing.hdmi_vic; + } info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR; info_packet->hb1 = 0x01; - info_packet->hb2 = (uint8_t) (length); + info_packet->hb2 = length & HDMI_INFOFRAME_LENGTH_MASK; checksum += info_packet->hb0; checksum += info_packet->hb1; @@ -521,6 +536,78 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, info_packet->valid = true; } +static void build_vtem_infopacket_header(struct dc_info_packet *infopacket) +{ + uint8_t pb0 = 0; + + /* might need logic in the future */ + pb0 |= 0 << EMP_SNC_BIT; + pb0 |= 1 << EMP_VFR_BIT; + pb0 |= 0 << EMP_AFR_BIT; + pb0 |= 0 << EMP_DST_BIT; + pb0 |= 0 << EMP_END_BIT; + pb0 |= 1 << EMP_NEW_BIT; + + infopacket->hb0 = HDMI_INFOFRAME_TYPE_EMP; + infopacket->hb1 = (1 << EMP_FIRST_BIT) | (1 << EMP_LAST_BIT); + infopacket->hb2 = 0; // sequence + + infopacket->sb[VTEM_PB0] = pb0; + infopacket->sb[VTEM_PB2] = VTEM_ORG_ID; + infopacket->sb[VTEM_PB4] = VTEM_DATA_SET_TAG; + infopacket->sb[VTEM_PB6] = VTEM_DATA_SET_LENGTH; +} + +static void build_vtem_infopacket_data(const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket) +{ + unsigned int brr = 0; + bool hdmi_vic_mode = false; + bool vrr_active = false; + bool rb = false; + + hdmi_vic_mode = is_hdmi_vic_mode(stream); + vrr_active = vrr->state == VRR_STATE_ACTIVE_VARIABLE || + vrr->state == VRR_STATE_ACTIVE_FIXED; + + infopacket->sb[VTEM_MD0] = VTEM_M_CONST << VTEM_M_CONST_BIT; + infopacket->sb[VTEM_MD0] |= VTEM_FVA_FACTOR << VTEM_FVA_BIT; + infopacket->sb[VTEM_MD0] |= vrr_active << VTEM_VRR_BIT; + + infopacket->sb[VTEM_MD1] = 0; + infopacket->sb[VTEM_MD2] = 0; + infopacket->sb[VTEM_MD3] = 0; + + if (hdmi_vic_mode || !vrr_active) + return; + /* + * Reduced Blanking standard defines a fixed value of + * 160 for hblank, further reduced to 80 in RB2 + */ + rb = (stream->timing.h_total - stream->timing.h_addressable) <= 160; + brr = mod_freesync_calc_nominal_field_rate(stream) / 1000000; + + if (brr > VTEM_BRR_MAX) { + infopacket->valid = false; + return; + } + + infopacket->sb[VTEM_MD1] = (uint8_t) stream->timing.v_front_porch; + infopacket->sb[VTEM_MD2] = rb << VTEM_RB_BIT; + infopacket->sb[VTEM_MD2] |= (brr & VTEM_BRR_MASK_UPPER) >> 8; + infopacket->sb[VTEM_MD3] = brr & VTEM_BRR_MASK_LOWER; +} + +void mod_build_vtem_infopacket(const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket) +{ + infopacket->valid = true; + build_vtem_infopacket_header(infopacket); + build_vtem_infopacket_data(stream, vrr, infopacket); +} + void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream, enum adaptive_sync_type asType, const struct AS_Df_params *param, @@ -535,12 +622,13 @@ void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream, if (stream != NULL) mod_build_adaptive_sync_infopacket_v2(stream, param, info_packet); break; - case FREESYNC_TYPE_PCON_IN_WHITELIST: + case ADAPTIVE_SYNC_TYPE_PCON_ALLOWED: case ADAPTIVE_SYNC_TYPE_EDP: mod_build_adaptive_sync_infopacket_v1(info_packet); break; case ADAPTIVE_SYNC_TYPE_NONE: - case FREESYNC_TYPE_PCON_NOT_IN_WHITELIST: + case ADAPTIVE_SYNC_TYPE_PCON_NOT_ALLOWED: + case ADAPTIVE_SYNC_TYPE_HDMI: default: break; } diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 75efda2969cfb..36de94634aadb 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -410,6 +410,12 @@ enum DC_DEBUG_MASK { * @DC_SKIP_DETECTION_LT: (0x200000) If set, skip detection link training */ DC_SKIP_DETECTION_LT = 0x200000, + + /** + * @DC_OVERRIDE_PCON_VRR_ID_CHECK: (0x400000) If set, always return true if checking for + * PCON VRR compatibility and print it's ID in kernel log. + */ + DC_OVERRIDE_PCON_VRR_ID_CHECK = 0x400000, }; enum amd_dpm_forced_level; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e2e85345aa9a4..5bdacd425908b 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6149,6 +6149,33 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector, hdmi->y420_dc_modes = dc_mask; } +static void drm_parse_hdmi_gaming_info(struct drm_hdmi_info *hdmi, const u8 *db) +{ + struct drm_hdmi_vrr_cap *vrr = &hdmi->vrr_cap; + + if (cea_db_payload_len(db) < 8) + return; + + hdmi->fapa_start_location = db[8] & DRM_EDID_FAPA_START_LOCATION; + hdmi->allm = db[8] & DRM_EDID_ALLM; + vrr->fva = db[8] & DRM_EDID_FVA; + vrr->cnmvrr = db[8] & DRM_EDID_CNMVRR; + vrr->cinema_vrr = db[8] & DRM_EDID_CINEMA_VRR; + vrr->mdelta = db[8] & DRM_EDID_MDELTA; + + if (cea_db_payload_len(db) < 9) + return; + + vrr->vrr_min = db[9] & DRM_EDID_VRR_MIN_MASK; + vrr->supported = (vrr->vrr_min > 0 && vrr->vrr_min <= 48); + + if (cea_db_payload_len(db) < 10) + return; + + vrr->vrr_max = (db[9] & DRM_EDID_VRR_MAX_UPPER_MASK) << 2 | db[10]; + vrr->supported &= (vrr->vrr_max == 0 || vrr->vrr_max >= 100); +} + static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc, const u8 *hf_scds) { @@ -6274,7 +6301,7 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, } drm_parse_ycbcr420_deep_color_info(connector, hf_scds); - + drm_parse_hdmi_gaming_info(&connector->display_info.hdmi, hf_scds); if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) { drm_parse_dsc_info(hdmi_dsc, hf_scds); dsc_support = true; @@ -6284,6 +6311,18 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, "[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n", connector->base.id, connector->name, max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support)); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] FAPA in blanking: %s, ALLM support: %s, Fast Vactive support: %s\n", + connector->base.id, connector->name, str_yes_no(hdmi->fapa_start_location), + str_yes_no(hdmi->allm), str_yes_no(hdmi->vrr_cap.fva)); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Negative M VRR support: %s, CinemaVRR support: %s, Mdelta: %d\n", + connector->base.id, connector->name, str_yes_no(hdmi->vrr_cap.cnmvrr), + str_yes_no(hdmi->vrr_cap.cinema_vrr), hdmi->vrr_cap.mdelta); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] VRRmin: %u, VRRmax: %u, VRR supported: %s\n", + connector->base.id, connector->name, hdmi->vrr_cap.vrr_min, + hdmi->vrr_cap.vrr_max, str_yes_no(hdmi->vrr_cap.supported)); } static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8f34f4b8183d8..dab9d5521f41b 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -254,6 +254,44 @@ struct drm_scdc { struct drm_scrambling scrambling; }; +/** + * struct drm_hdmi_vrr_cap - Information about VRR capabilities of a HDMI sink + * + * Describes the VRR support provided by HDMI 2.1 sink. The information is + * fetched fom additional HFVSDB blocks defined for HDMI 2.1. + */ +struct drm_hdmi_vrr_cap { + /** @fva: flag for Fast VActive (Quick Frame Transport) support */ + bool fva; + + /** @mcnmvrr: flag for Negative M VRR support */ + bool cnmvrr; + + /** @mcinema_vrr: flag for Cinema VRR support */ + bool cinema_vrr; + + /** @mdelta: flag for limited frame-to-frame compensation support */ + bool mdelta; + + /** + * @vrr_min : minimum supported variable refresh rate in Hz. + * Valid values only inide 1 - 48 range + */ + u16 vrr_min; + + /** + * @vrr_max : maximum supported variable refresh rate in Hz (optional). + * Valid values are either 0 (max based on video mode) or >= 100 + */ + u16 vrr_max; + + /** + * @supported: flag for vrr support based on checking for VRRmin and + * VRRmax values having correct values. + */ + bool supported; +}; + /** * struct drm_hdmi_dsc_cap - DSC capabilities of HDMI sink * @@ -330,6 +368,15 @@ struct drm_hdmi_info { /** @max_lanes: supported by sink */ u8 max_lanes; + /** @fapa_start_location: flag for the FAPA in blanking support */ + bool fapa_start_location; + + /** @allm: flag for Auto Low Latency Mode support by sink */ + bool allm; + + /** @vrr_cap: VRR capabilities of the sink */ + struct drm_hdmi_vrr_cap vrr_cap; + /** @dsc_cap: DSC capabilities of the sink */ struct drm_hdmi_dsc_cap dsc_cap; };