/* * Copyright 2022 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD *
*/
/* FILE POLICY AND INTENDED USAGE: * This file manages link detection states and receiver states by using various * link protocols. It also provides helper functions to interpret certain * capabilities or status based on the states it manages or retrieve them * directly from connected receivers.
*/
#define LINK_INFO(...) \
DC_LOG_HW_HOTPLUG( \
__VA_ARGS__) /* * Some receivers fail to train on first try and are good * on subsequent tries. 2 retries should be plenty. If we * don't have a successful training then we don't expect to * ever get one.
*/ #define LINK_TRAINING_MAX_VERIFY_RETRY 2
switch (sink_signal) { case SIGNAL_TYPE_DVI_SINGLE_LINK: case SIGNAL_TYPE_DVI_DUAL_LINK: case SIGNAL_TYPE_HDMI_TYPE_A: case SIGNAL_TYPE_LVDS: case SIGNAL_TYPE_RGB:
transaction_type = DDC_TRANSACTION_TYPE_I2C; break;
case SIGNAL_TYPE_DISPLAY_PORT: case SIGNAL_TYPE_EDP:
transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; break;
case SIGNAL_TYPE_DISPLAY_PORT_MST: /* MST does not use I2COverAux, but there is the * SPECIAL use case for "immediate dwnstrm device * access" (EPR#370830).
*/
transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; break;
default: break;
}
return transaction_type;
}
staticenum signal_type get_basic_signal_type(struct graphics_object_id encoder, struct graphics_object_id downstream)
{ if (downstream.type == OBJECT_TYPE_CONNECTOR) { switch (downstream.id) { case CONNECTOR_ID_SINGLE_LINK_DVII: switch (encoder.id) { case ENCODER_ID_INTERNAL_DAC1: case ENCODER_ID_INTERNAL_KLDSCP_DAC1: case ENCODER_ID_INTERNAL_DAC2: case ENCODER_ID_INTERNAL_KLDSCP_DAC2: return SIGNAL_TYPE_RGB; default: return SIGNAL_TYPE_DVI_SINGLE_LINK;
} break; case CONNECTOR_ID_DUAL_LINK_DVII:
{ switch (encoder.id) { case ENCODER_ID_INTERNAL_DAC1: case ENCODER_ID_INTERNAL_KLDSCP_DAC1: case ENCODER_ID_INTERNAL_DAC2: case ENCODER_ID_INTERNAL_KLDSCP_DAC2: return SIGNAL_TYPE_RGB; default: return SIGNAL_TYPE_DVI_DUAL_LINK;
}
} break; case CONNECTOR_ID_SINGLE_LINK_DVID: return SIGNAL_TYPE_DVI_SINGLE_LINK; case CONNECTOR_ID_DUAL_LINK_DVID: return SIGNAL_TYPE_DVI_DUAL_LINK; case CONNECTOR_ID_VGA: return SIGNAL_TYPE_RGB; case CONNECTOR_ID_HDMI_TYPE_A: return SIGNAL_TYPE_HDMI_TYPE_A; case CONNECTOR_ID_LVDS: return SIGNAL_TYPE_LVDS; case CONNECTOR_ID_DISPLAY_PORT: case CONNECTOR_ID_USBC: return SIGNAL_TYPE_DISPLAY_PORT; case CONNECTOR_ID_EDP: return SIGNAL_TYPE_EDP; default: return SIGNAL_TYPE_NONE;
}
} elseif (downstream.type == OBJECT_TYPE_ENCODER) { switch (downstream.id) { case ENCODER_ID_EXTERNAL_NUTMEG: case ENCODER_ID_EXTERNAL_TRAVIS: return SIGNAL_TYPE_DISPLAY_PORT; default: return SIGNAL_TYPE_NONE;
}
}
if (link->is_dig_mapping_flexible)
enc_id = (struct graphics_object_id){.id = ENCODER_ID_UNKNOWN}; else
enc_id = link->link_enc->id;
result = get_basic_signal_type(enc_id, link->link_id);
/* Use basic signal type for link without physical connector. */ if (link->ep_type != DISPLAY_ENDPOINT_PHY) return result;
/* Internal digital encoder will detect only dongles * that require digital signal
*/
/* Detection mechanism is different * for different native connectors. * LVDS connector supports only LVDS signal; * PCIE is a bus slot, the actual connector needs to be detected first; * eDP connector supports only eDP signal; * HDMI should check straps for audio
*/
/* PCIE detects the actual connector on add-on board */ if (link->link_id.id == CONNECTOR_ID_PCIE) { /* ZAZTODO implement PCIE add-on card detection */
}
switch (link->link_id.id) { case CONNECTOR_ID_HDMI_TYPE_A: { /* check audio support: * if native HDMI is not supported, switch to DVI
*/ struct audio_support *aud_support =
&link->dc->res_pool->audio_support;
if (!aud_support->hdmi_audio_native) if (link->link_id.id == CONNECTOR_ID_HDMI_TYPE_A)
result = SIGNAL_TYPE_DVI_SINGLE_LINK;
} break; case CONNECTOR_ID_DISPLAY_PORT: case CONNECTOR_ID_USBC: { /* DP HPD short pulse. Passive DP dongle will not * have short pulse
*/ if (reason != DETECT_REASON_HPDRX) { /* Check whether DP signal detected: if not - * we assume signal is DVI; it could be corrected * to HDMI after dongle detection
*/ if (!dm_helpers_is_dp_sink_present(link))
result = SIGNAL_TYPE_DVI_SINGLE_LINK;
}
} break; default: break;
}
switch (dongle_type) { case DISPLAY_DONGLE_DP_HDMI_DONGLE: if (audio_support->hdmi_audio_on_dongle)
signal = SIGNAL_TYPE_HDMI_TYPE_A; else
signal = SIGNAL_TYPE_DVI_SINGLE_LINK; break; case DISPLAY_DONGLE_DP_DVI_DONGLE:
signal = SIGNAL_TYPE_DVI_SINGLE_LINK; break; case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: if (audio_support->hdmi_audio_native)
signal = SIGNAL_TYPE_HDMI_TYPE_A; else
signal = SIGNAL_TYPE_DVI_SINGLE_LINK; break; default:
signal = SIGNAL_TYPE_NONE; break;
}
/* Check signature */ for (i = 0; i < sizeof(dongle_signature->id); ++i) { /* If its not the right signature,
* skip mismatch in subversion byte.*/ if (dongle_signature->id[i] !=
dp_hdmi_dongle_signature_str[i] && i != 3) {
if (is_type2_dongle) {
is_valid_hdmi_signature = false; break;
}
}
}
if (is_type2_dongle) {
uint32_t max_tmds_clk =
type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK];
} staticvoid read_current_link_settings_on_detect(struct dc_link *link)
{ union lane_count_set lane_count_set = {0};
uint8_t link_bw_set = 0;
uint8_t link_rate_set = 0;
uint32_t read_dpcd_retry_cnt = 10; enum dc_status status = DC_ERROR_UNEXPECTED; int i; union max_down_spread max_down_spread = {0};
// Read DPCD 00101h to find out the number of lanes currently set for (i = 0; i < read_dpcd_retry_cnt; i++) {
status = core_link_read_dpcd(link,
DP_LANE_COUNT_SET,
&lane_count_set.raw, sizeof(lane_count_set)); /* First DPCD read after VDD ON can fail if the particular board * does not have HPD pin wired correctly. So if DPCD read fails, * which it should never happen, retry a few times. Target worst * case scenario of 80 ms.
*/ if (status == DC_OK) {
link->cur_link_settings.lane_count =
lane_count_set.bits.LANE_COUNT_SET; break;
}
msleep(8);
}
// Read DPCD 00100h to find if standard link rates are set
core_link_read_dpcd(link, DP_LINK_BW_SET,
&link_bw_set, sizeof(link_bw_set));
if (link_bw_set == 0) { if (link->connector_signal == SIGNAL_TYPE_EDP) { /* If standard link rates are not being used, * Read DPCD 00115h to find the edp link rate set used
*/
core_link_read_dpcd(link, DP_LINK_RATE_SET,
&link_rate_set, sizeof(link_rate_set));
// edp_supported_link_rates_count = 0 for DP if (link_rate_set < link->dpcd_caps.edp_supported_link_rates_count) {
link->cur_link_settings.link_rate =
link->dpcd_caps.edp_supported_link_rates[link_rate_set];
link->cur_link_settings.link_rate_set = link_rate_set;
link->cur_link_settings.use_link_rate_set = true;
}
} else { // Link Rate not found. Seamless boot may not work.
ASSERT(false);
}
} else {
link->cur_link_settings.link_rate = link_bw_set;
link->cur_link_settings.use_link_rate_set = false;
} // Read DPCD 00003h to find the max down spread.
core_link_read_dpcd(link, DP_MAX_DOWNSPREAD,
&max_down_spread.raw, sizeof(max_down_spread));
link->cur_link_settings.link_spread =
max_down_spread.bits.MAX_DOWN_SPREAD ?
LINK_SPREAD_05_DOWNSPREAD_30KHZ : LINK_SPREAD_DISABLED;
}
/** * something is terribly wrong if time out is > 200ms. (5Hz) * 500 microseconds * 400 tries us 200 ms
**/ unsignedint sleep_time_in_microseconds = 500; unsignedint tries_allowed = 400; bool is_in_alt_mode; unsignedlonglong enter_timestamp; unsignedlonglong finish_timestamp; unsignedlonglong time_taken_in_ns; int tries_taken;
DC_LOGGER_INIT(link->ctx->logger);
/** * this function will only exist if we are on dcn21 (is_in_alt_mode is a * function pointer, so checking to see if it is equal to 0 is the same * as checking to see if it is null
**/ if (!link->link_enc->funcs->is_in_alt_mode) returntrue;
is_in_alt_mode = link->link_enc->funcs->is_in_alt_mode(link->link_enc);
DC_LOG_DC("DP Alt mode state on HPD: %d\n", is_in_alt_mode);
if (is_in_alt_mode) returntrue;
enter_timestamp = dm_get_timestamp(link->ctx);
for (tries_taken = 0; tries_taken < tries_allowed; tries_taken++) {
udelay(sleep_time_in_microseconds); /* ask the link if alt mode is enabled, if so return ok */ if (link->link_enc->funcs->is_in_alt_mode(link->link_enc)) {
finish_timestamp = dm_get_timestamp(link->ctx);
time_taken_in_ns =
dm_get_elapse_time_in_ns(link->ctx,
finish_timestamp,
enter_timestamp);
DC_LOG_WARNING("Alt mode entered finished after %llu ms\n",
div_u64(time_taken_in_ns, 1000000)); returntrue;
}
}
finish_timestamp = dm_get_timestamp(link->ctx);
time_taken_in_ns = dm_get_elapse_time_in_ns(link->ctx, finish_timestamp,
enter_timestamp);
DC_LOG_WARNING("Alt mode has timed out after %llu ms\n",
div_u64(time_taken_in_ns, 1000000)); returnfalse;
}
staticvoid apply_dpia_mst_dsc_always_on_wa(struct dc_link *link)
{ /* Apply work around for tunneled MST on certain USB4 docks. Always use DSC if dock * reports DSC support.
*/ if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
link->type == dc_connection_mst_branch &&
link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_90CC24 &&
link->dpcd_caps.branch_hw_revision == DP_BRANCH_HW_REV_20 &&
link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT &&
!link->dc->debug.dpia_debug.bits.disable_mst_dsc_work_around)
link->wa_flags.dpia_mst_dsc_always_on = true;
staticvoid revert_dpia_mst_dsc_always_on_wa(struct dc_link *link)
{ /* Disable work around which keeps DSC on for tunneled MST on certain USB4 docks. */ if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA)
link->wa_flags.dpia_mst_dsc_always_on = false;
}
staticbool should_prepare_phy_clocks_for_link_verification(conststruct dc *dc, enum dc_detect_reason reason)
{ int i; bool can_apply_seamless_boot = false;
for (i = 0; i < dc->current_state->stream_count; i++) { if (dc->current_state->streams[i]->apply_seamless_boot_optimization) {
can_apply_seamless_boot = true; break;
}
}
if (should_prepare_phy_clocks)
restore_phy_clocks_for_destructive_link_verification(link->dc);
}
staticvoid verify_link_capability_non_destructive(struct dc_link *link)
{ if (dc_is_dp_signal(link->local_sink->sink_signal)) { if (dc_is_embedded_signal(link->local_sink->sink_signal) ||
link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) /* TODO - should we check link encoder's max link caps here? * How do we know which link encoder to check from?
*/
link->verified_link_cap = link->reported_link_cap; else
link->verified_link_cap = dp_get_max_link_cap(link);
}
}
/* wa HPD high coming too early*/ if (link->ep_type == DISPLAY_ENDPOINT_PHY &&
link->link_enc->features.flags.bits.DP_IS_USB_C == 1) {
/* if alt mode times out, return false */ if (!wait_for_entering_dp_alt_mode(link)) returnfalse;
}
if (!detect_dp(link, &sink_caps, reason)) {
if (prev_sink)
dc_sink_release(prev_sink); returnfalse;
}
/* Active SST downstream branch device unplug*/ if (link->type == dc_connection_sst_branch &&
link->dpcd_caps.sink_count.bits.SINK_COUNT == 0) { if (prev_sink) /* Downstream unplug */
dc_sink_release(prev_sink); returntrue;
}
/* disable audio for non DP to HDMI active sst converter */ if (link->type == dc_connection_sst_branch &&
is_dp_active_dongle(link) &&
(link->dpcd_caps.dongle_type !=
DISPLAY_DONGLE_DP_HDMI_CONVERTER))
converter_disable_audio = true;
/* limited link rate to HBR3 for DPIA until we implement USB4 V2 */ if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
link->reported_link_cap.link_rate > LINK_RATE_HIGH3)
link->reported_link_cap.link_rate = LINK_RATE_HIGH3;
if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling
&& link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc
&& link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support) { if (link_dpia_enable_usb4_dp_bw_alloc_mode(link) == false)
link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc = false;
} break;
}
switch (edid_status) { case EDID_BAD_CHECKSUM:
DC_LOG_ERROR("EDID checksum invalid.\n"); break; case EDID_PARTIAL_VALID:
DC_LOG_ERROR("Partial EDID valid, abandon invalid blocks.\n"); break; case EDID_NO_RESPONSE:
DC_LOG_ERROR("No EDID read.\n"); /* * Abort detection for non-DP connectors if we have * no EDID * * DP needs to report as connected if HDP is high * even if we have no EDID in order to go to * fail-safe mode
*/ if (dc_is_hdmi_signal(link->connector_signal) ||
dc_is_dvi_signal(link->connector_signal)) { if (prev_sink)
dc_sink_release(prev_sink);
returnfalse;
}
if (link->type == dc_connection_sst_branch &&
link->dpcd_caps.dongle_type ==
DISPLAY_DONGLE_DP_VGA_CONVERTER &&
reason == DETECT_REASON_HPDRX) { /* Abort detection for DP-VGA adapters when EDID * can't be read and detection reason is VGA-side * hotplug
*/ if (prev_sink)
dc_sink_release(prev_sink);
link_disconnect_sink(link);
returntrue;
}
break; default: break;
}
// Check if edid is the same if ((prev_sink) &&
(edid_status == EDID_THE_SAME || edid_status == EDID_OK))
same_edid = is_same_edid(&prev_sink->dc_edid,
&sink->dc_edid);
if (sink->edid_caps.panel_patch.skip_scdc_overwrite)
link->ctx->dc->debug.hdmi20_disable = true;
if (sink->edid_caps.panel_patch.remove_sink_ext_caps)
link->dpcd_sink_ext_caps.raw = 0;
if (dc_is_hdmi_signal(link->connector_signal))
read_scdc_caps(link->ddc, link->local_sink);
if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT &&
sink_caps.transaction_type ==
DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { /* * TODO debug why certain monitors don't like * two link trainings
*/
query_hdcp_capability(sink->sink_signal, link);
} else { // If edid is the same, then discard new sink and revert back to original sink if (same_edid) {
link_disconnect_remap(prev_sink, link);
sink = prev_sink;
prev_sink = NULL;
}
query_hdcp_capability(sink->sink_signal, link);
}
for (i = 0; i < sink->edid_caps.audio_mode_count; i++) {
DC_LOG_DETECTION_EDID_PARSER("%s: mode number = %d, " "format_code = %d, " "channel_count = %d, " "sample_rate = %d, " "sample_size = %d\n",
__func__,
i,
sink->edid_caps.audio_modes[i].format_code,
sink->edid_caps.audio_modes[i].channel_count,
sink->edid_caps.audio_modes[i].sample_rate,
sink->edid_caps.audio_modes[i].sample_size);
}
if (link->connector_signal == SIGNAL_TYPE_EDP) { // Init dc_panel_config by HW config if (dc_ctx->dc->res_pool->funcs->get_panel_config_defaults)
dc_ctx->dc->res_pool->funcs->get_panel_config_defaults(&link->panel_config); // Pickup base DM settings
dm_helpers_init_panel_settings(dc_ctx, &link->panel_config, sink); // Override dc_panel_config if system has specific settings
dm_helpers_override_panel_settings(dc_ctx, &link->panel_config);
//sink only can use supported link rate table, we are foreced to enable it if (link->reported_link_cap.link_rate == LINK_RATE_UNKNOWN)
link->panel_config.ilr.optimize_edp_link_rate = true;
link->reported_link_cap.link_rate = get_max_edp_link_rate(link);
}
} else { /* From Connected-to-Disconnected. */
link->type = dc_connection_none;
sink_caps.signal = SIGNAL_TYPE_NONE;
memset(&link->hdcp_caps, 0, sizeof(struct hdcp_caps)); /* When we unplug a passive DP-HDMI dongle connection, dongle_max_pix_clk * is not cleared. If we emulate a DP signal on this connection, it thinks * the dongle is still there and limits the number of modes we can emulate. * Clear dongle_max_pix_clk on disconnect to fix this
*/
link->dongle_max_pix_clk = 0;
LINK_INFO("link=%d, dc_sink_in=%p is now %s prev_sink=%p edid same=%d\n",
link->link_index, sink,
(sink_caps.signal ==
SIGNAL_TYPE_NONE ? "Disconnected" : "Connected"),
prev_sink, same_edid);
if (prev_sink)
dc_sink_release(prev_sink);
returntrue;
}
/* * link_detect_connection_type() - Determine if there is a sink connected * * @type: Returned connection type * Does not detect downstream devices, such as MST sinks * or display connected through active dongles
*/ bool link_detect_connection_type(struct dc_link *link, enum dc_connection_type *type)
{
uint32_t is_hpd_high = 0;
if (link->connector_signal == SIGNAL_TYPE_LVDS) {
*type = dc_connection_single; returntrue;
}
if (link->connector_signal == SIGNAL_TYPE_EDP) { /*in case it is not on*/ if (!link->dc->config.edp_no_power_sequencing)
link->dc->hwss.edp_power_control(link, true);
link->dc->hwss.edp_wait_for_hpd_ready(link, true);
}
/* Link may not have physical HPD pin. */ if (link->ep_type != DISPLAY_ENDPOINT_PHY) { if (link->is_hpd_pending || !dpia_query_hpd_status(link))
*type = dc_connection_none; else
*type = dc_connection_single;
returntrue;
}
if (!query_hpd_status(link, &is_hpd_high)) goto hpd_gpio_failure;
if (is_hpd_high) {
*type = dc_connection_single; /* TODO: need to do the actual detection */
} else {
*type = dc_connection_none; if (link->connector_signal == SIGNAL_TYPE_EDP) { /* eDP is not connected, power down it */ if (!link->dc->config.edp_no_power_sequencing)
link->dc->hwss.edp_power_control(link, false);
}
}
switch (signal) { case SIGNAL_TYPE_DISPLAY_PORT: case SIGNAL_TYPE_DISPLAY_PORT_MST:
ret = link->hdcp_caps.bcaps.bits.HDCP_CAPABLE; break; case SIGNAL_TYPE_DVI_SINGLE_LINK: case SIGNAL_TYPE_DVI_DUAL_LINK: case SIGNAL_TYPE_HDMI_TYPE_A: /* HDMI doesn't tell us its HDCP(1.4) capability, so assume to always be capable, * we can poll for bksv but some displays have an issue with this. Since its so rare * for a display to not be 1.4 capable, this assumtion is ok
*/
ret = true; break; default: break;
} return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.