summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/display/dc/link
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/link')
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c39
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c113
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.c184
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.c622
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_factory.c148
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_validation.c302
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_validation.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c182
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c30
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c17
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c62
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c43
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c32
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c73
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c1204
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h53
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c1
25 files changed, 2798 insertions, 385 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/link/Makefile b/drivers/gpu/drm/amd/display/dc/link/Makefile
index 0f3670e30232..f558a20eceaa 100644
--- a/drivers/gpu/drm/amd/display/dc/link/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/link/Makefile
@@ -45,6 +45,7 @@ AMD_DISPLAY_FILES += $(AMD_DAL_LINK_ACCESSORIES)
LINK_HWSS = link_hwss_dio.o link_hwss_dpia.o link_hwss_hpo_dp.o \
link_hwss_dio_fixed_vs_pe_retimer.o link_hwss_hpo_fixed_vs_pe_retimer_dp.o \
link_hwss_virtual.o
+LINK_HWSS += link_hwss_hpo_frl.o
AMD_DAL_LINK_HWSS = $(addprefix $(AMDDALPATH)/dc/link/hwss/, \
$(LINK_HWSS))
@@ -58,6 +59,7 @@ link_dp_training.o link_dp_training_8b_10b.o link_dp_training_128b_132b.o \
link_dp_training_dpia.o link_dp_training_auxless.o \
link_dp_training_fixed_vs_pe_retimer.o link_dp_phy.o link_dp_capability.o \
link_edp_panel_control.o link_dp_panel_replay.o link_dp_irq_handler.o link_dp_dpia_bw.o
+LINK_PROTOCOLS += link_hdmi_frl.o
AMD_DAL_LINK_PROTOCOLS = $(addprefix $(AMDDALPATH)/dc/link/protocols/, \
$(LINK_PROTOCOLS))
diff --git a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
index 060460abc377..d3172dbdcdf0 100644
--- a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
+++ b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
@@ -145,6 +145,8 @@ static void dp_retrain_link_dp_test(struct dc_link *link,
// Set DPMS on with stream update
// Cache all streams on current link since dc_update_planes_and_stream might kill current_state
for (i = 0; i < MAX_PIPES; i++) {
+ if (state->streams[i] && state->streams[i]->is_phantom)
+ continue;
if (state->streams[i] && state->streams[i]->link && state->streams[i]->link == link)
streams_on_link[num_streams_on_link++] = state->streams[i];
}
@@ -199,7 +201,6 @@ static void dp_test_get_audio_test_data(struct dc_link *link, bool disable_video
unsigned int channel_count;
unsigned int channel = 0;
unsigned int modes = 0;
- unsigned int sampling_rate_in_hz = 0;
// get audio test mode and test pattern parameters
core_link_read_dpcd(
@@ -232,38 +233,10 @@ static void dp_test_get_audio_test_data(struct dc_link *link, bool disable_video
}
}
- // translate sampling rate
- switch (dpcd_test_mode.bits.sampling_rate) {
- case AUDIO_SAMPLING_RATE_32KHZ:
- sampling_rate_in_hz = 32000;
- break;
- case AUDIO_SAMPLING_RATE_44_1KHZ:
- sampling_rate_in_hz = 44100;
- break;
- case AUDIO_SAMPLING_RATE_48KHZ:
- sampling_rate_in_hz = 48000;
- break;
- case AUDIO_SAMPLING_RATE_88_2KHZ:
- sampling_rate_in_hz = 88200;
- break;
- case AUDIO_SAMPLING_RATE_96KHZ:
- sampling_rate_in_hz = 96000;
- break;
- case AUDIO_SAMPLING_RATE_176_4KHZ:
- sampling_rate_in_hz = 176400;
- break;
- case AUDIO_SAMPLING_RATE_192KHZ:
- sampling_rate_in_hz = 192000;
- break;
- default:
- sampling_rate_in_hz = 0;
- break;
- }
-
link->audio_test_data.flags.test_requested = 1;
link->audio_test_data.flags.disable_video = disable_video;
- link->audio_test_data.sampling_rate = sampling_rate_in_hz;
- link->audio_test_data.channel_count = channel_count;
+ link->audio_test_data.sampling_rate = (uint8_t)dpcd_test_mode.bits.sampling_rate;
+ link->audio_test_data.channel_count = (uint8_t)channel_count;
link->audio_test_data.pattern_type = test_pattern;
if (test_pattern == DP_TEST_PATTERN_AUDIO_SAWTOOTH) {
@@ -885,7 +858,7 @@ bool dp_set_test_pattern(
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
hw_locks.bits.lock_dig = 1;
- inst_flags.dig_inst = pipe_ctx->stream_res.tg->inst;
+ inst_flags.dig_inst = (uint8_t)pipe_ctx->stream_res.tg->inst;
dmub_hw_lock_mgr_cmd(link->ctx->dmub_srv,
true,
@@ -933,7 +906,7 @@ bool dp_set_test_pattern(
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
hw_locks.bits.lock_dig = 1;
- inst_flags.dig_inst = pipe_ctx->stream_res.tg->inst;
+ inst_flags.dig_inst = (uint8_t)pipe_ctx->stream_res.tg->inst;
dmub_hw_lock_mgr_cmd(link->ctx->dmub_srv,
false,
diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c
index dbbedeeed298..04df75114dd5 100644
--- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c
+++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c
@@ -63,7 +63,7 @@ void set_hpo_dp_hblank_min_symbol_width(struct pipe_ctx *pipe_ctx,
time_slot_in_ms = dc_fixpt_from_fraction(32 * 4, link_bw_in_kbps);
mtp_cnt_per_h_blank = dc_fixpt_div(h_blank_in_ms,
dc_fixpt_mul_int(time_slot_in_ms, 64));
- hblank_min_symbol_width = dc_fixpt_floor(
+ hblank_min_symbol_width = (uint16_t)dc_fixpt_floor(
dc_fixpt_mul(mtp_cnt_per_h_blank, throttled_vcp_size));
}
@@ -98,7 +98,7 @@ void setup_hpo_dp_stream_attribute(struct pipe_ctx *pipe_ctx)
&stream->timing,
stream->output_color_space,
stream->use_vsc_sdp_for_colorimetry,
- stream->timing.flags.DSC,
+ (stream->timing.flags.DSC != 0),
false);
link->dc->link_srv->dp_trace_source_sequence(link,
DPCD_SOURCE_SEQ_AFTER_DP_STREAM_ATTR);
diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
new file mode 100644
index 000000000000..a2c0fa21ee94
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
@@ -0,0 +1,113 @@
+/*
+ * 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
+ *
+ */
+#include "link_hwss_hpo_frl.h"
+#include "core_types.h"
+#include "link/hwss/link_hwss_virtual.h"
+
+static void setup_hpo_frl_stream_attribute(struct pipe_ctx *pipe_ctx)
+{
+ struct hpo_frl_stream_encoder *stream_enc = pipe_ctx->stream_res.hpo_frl_stream_enc;
+ struct dc_stream_state *stream = pipe_ctx->stream;
+ struct pipe_ctx *odm_pipe;
+ struct dc *dc = stream->link->ctx->dc;
+ struct dc_stream_state *temp_stream = &dc->scratch.temp_stream;
+ int odm_combine_num_segments = 1;
+
+ memcpy(temp_stream, stream, sizeof(struct dc_stream_state));
+
+ /* Modify patched_crtc_timing as required for padding */
+ if (pipe_ctx->dsc_padding_params.dsc_hactive_padding) {
+ temp_stream->timing.h_addressable = stream->timing.h_addressable + pipe_ctx->dsc_padding_params.dsc_hactive_padding;
+ temp_stream->timing.h_total = stream->timing.h_total + pipe_ctx->dsc_padding_params.dsc_htotal_padding;
+ }
+
+ /* get number of ODM combine input segments */
+ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
+ odm_combine_num_segments++;
+
+ stream_enc->funcs->hdmi_frl_set_stream_attribute(
+ stream_enc,
+ &temp_stream->timing,
+ &stream->link->frl_link_settings.borrow_params,
+ odm_combine_num_segments);
+}
+
+static void disable_hpo_frl_link_output(struct dc_link *link,
+ const struct link_resource *link_res,
+ enum signal_type signal)
+{
+ (void)link_res;
+ if (dc_is_hdmi_frl_signal(signal))
+ link->hpo_frl_link_enc->funcs->disable_link_encoder(link->hpo_frl_link_enc);
+ link->link_enc->funcs->disable_output(link->link_enc, signal);
+}
+
+static void setup_hpo_frl_audio_output(struct pipe_ctx *pipe_ctx,
+ struct audio_output *audio_output, uint32_t audio_inst)
+{
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_audio_setup(
+ pipe_ctx->stream_res.hpo_frl_stream_enc,
+ audio_inst,
+ &pipe_ctx->stream->audio_info,
+ &audio_output->crtc_info);
+}
+
+static void enable_hpo_frl_audio_packet(struct pipe_ctx *pipe_ctx)
+{
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->audio_mute_control(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, false);
+}
+
+static void disable_hpo_frl_audio_packet(struct pipe_ctx *pipe_ctx)
+{
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->audio_mute_control(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, true);
+
+ if (pipe_ctx->stream_res.audio)
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_audio_disable(
+ pipe_ctx->stream_res.hpo_frl_stream_enc);
+}
+
+static const struct link_hwss hpo_frl_link_hwss = {
+ .setup_stream_encoder = virtual_setup_stream_encoder,
+ .reset_stream_encoder = virtual_reset_stream_encoder,
+ .setup_stream_attribute = setup_hpo_frl_stream_attribute,
+ .disable_link_output = disable_hpo_frl_link_output,
+ .setup_audio_output = setup_hpo_frl_audio_output,
+ .enable_audio_packet = enable_hpo_frl_audio_packet,
+ .disable_audio_packet = disable_hpo_frl_audio_packet,
+};
+
+bool can_use_hpo_frl_link_hwss(const struct dc_link *link,
+ const struct link_resource *link_res)
+{
+ (void)link;
+ return link_res->hpo_frl_link_enc != NULL;
+}
+
+const struct link_hwss *get_hpo_frl_link_hwss(void)
+{
+ return &hpo_frl_link_hwss;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h
new file mode 100644
index 000000000000..ea8d9760132f
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h
@@ -0,0 +1,34 @@
+/*
+ * 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
+ *
+ */
+#ifndef __LINK_HWSS_HPO_FRL_H__
+#define __LINK_HWSS_HPO_FRL_H__
+
+#include "link_hwss.h"
+
+bool can_use_hpo_frl_link_hwss(const struct dc_link *link,
+ const struct link_resource *link_res);
+const struct link_hwss *get_hpo_frl_link_hwss(void);
+
+#endif /* __LINK_HWSS_HPO_FRL_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
index 794dd6a95918..22ad6a743038 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -41,6 +41,7 @@
#include "protocols/link_dp_dpia.h"
#include "protocols/link_dp_phy.h"
#include "protocols/link_dp_training.h"
+#include "protocols/link_hdmi_frl.h"
#include "protocols/link_dp_dpia_bw.h"
#include "accessories/link_dp_trace.h"
@@ -78,6 +79,7 @@ static enum ddc_transaction_type get_ddc_transaction_type(enum signal_type sink_
case SIGNAL_TYPE_DVI_SINGLE_LINK:
case SIGNAL_TYPE_DVI_DUAL_LINK:
case SIGNAL_TYPE_HDMI_TYPE_A:
+ case SIGNAL_TYPE_HDMI_FRL:
case SIGNAL_TYPE_LVDS:
case SIGNAL_TYPE_RGB:
transaction_type = DDC_TRANSACTION_TYPE_I2C;
@@ -290,12 +292,12 @@ static bool i2c_read(
struct i2c_payload payloads[2] = {
{
.write = true,
- .address = address,
+ .address = (uint8_t)address,
.length = 1,
.data = &offs_data },
{
.write = false,
- .address = address,
+ .address = (uint8_t)address,
.length = len,
.data = buffer } };
@@ -334,6 +336,34 @@ static void query_dp_dual_mode_adaptor(
sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK;
/* Read DP-HDMI dongle I2c (no response interpreted as DP-DVI dongle)*/
+ if (link->force_to_use_aux) {
+
+ if (!dm_helpers_submit_i2c_over_aux(
+ ddc,
+ DP_HDMI_DONGLE_ADDRESS,
+ 0,
+ type2_dongle_buf,
+ sizeof(type2_dongle_buf),
+ true)) {
+
+ /* Use same approach as below with native i2c - HDMI dongles can sometimes fail here without retrying*/
+ while (retry_count > 0) {
+ if (dm_helpers_submit_i2c_over_aux(
+ ddc,
+ DP_HDMI_DONGLE_ADDRESS,
+ 0,
+ type2_dongle_buf,
+ sizeof(type2_dongle_buf),
+ true))
+ break;
+ retry_count--;
+ }
+ /* In case I2C over Aux no response, it is interpreted as DISPLAY_DONGLE_NONE */
+ if (retry_count == 0) {
+ return;
+ }
+ }
+ } else {
if (!i2c_read(
ddc,
DP_HDMI_DONGLE_ADDRESS,
@@ -358,6 +388,7 @@ static void query_dp_dual_mode_adaptor(
return;
}
}
+ }
/* Check if Type 2 dongle.*/
if (type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_ID] == DP_ADAPTOR_TYPE2_ID)
@@ -495,6 +526,12 @@ static void query_hdcp_capability(enum signal_type signal, struct dc_link *link)
msg22.data = link->hdcp_caps.rx_caps.raw;
msg22.length = sizeof(link->hdcp_caps.rx_caps.raw);
msg22.msg_id = HDCP_MESSAGE_ID_RX_CAPS;
+ if (link->force_to_use_aux && (signal == SIGNAL_TYPE_HDMI_TYPE_A)) {
+ // case with passive dongle with i2c over aux
+ msg22.data = &link->hdcp_caps.rx_caps.fields.version;
+ msg22.length = sizeof(link->hdcp_caps.rx_caps.fields.version);
+ msg22.msg_id = HDCP_MESSAGE_ID_HDCP2VERSION;
+ }
} else {
msg22.data = &link->hdcp_caps.rx_caps.fields.version;
msg22.length = sizeof(link->hdcp_caps.rx_caps.fields.version);
@@ -524,7 +561,7 @@ static void read_current_link_settings_on_detect(struct dc_link *link)
uint8_t link_rate_set = 0;
uint32_t read_dpcd_retry_cnt = 10;
enum dc_status status = DC_ERROR_UNEXPECTED;
- int i;
+ unsigned int i;
union max_down_spread max_down_spread = {0};
// Read DPCD 00101h to find out the number of lanes currently set
@@ -595,7 +632,24 @@ static bool detect_dp(struct dc_link *link,
if (sink_caps->transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) {
sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT;
if (!detect_dp_sink_caps(link)) {
- return false;
+ if (link->force_to_use_aux) {
+ sink_caps->signal = dp_passive_dongle_detection(link->ddc, sink_caps, audio_support);
+ link->dpcd_caps.dongle_type = sink_caps->dongle_type;
+ link->dpcd_caps.is_dongle_type_one = sink_caps->is_dongle_type_one;
+ link->dpcd_caps.dpcd_rev.raw = 0;
+ /* Type 1 dongles do not work with I2C over Aux and also some of
+ * Type 2 dongles do not support I2C over Aux well, so for these
+ * cases when native Aux transactons fails and I2C over Aux fails
+ * report that nothing is connected, as we can't tell is it bad
+ * DP sink or bad passive dongle.
+ */
+ if (sink_caps->dongle_type == DISPLAY_DONGLE_NONE)
+ return false;
+ else
+ return true;
+ } else {
+ return false;
+ }
}
if (is_dp_branch_device(link))
@@ -621,7 +675,7 @@ static bool detect_dp(struct dc_link *link,
link->dpcd_caps.sink_count.bits.SINK_COUNT = 1;
/* NUTMEG requires that we use HBR, doesn't work with RBR. */
if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_00001A)
- link->preferred_link_setting.link_rate = LINK_RATE_HIGH;
+ link->wa_flags.dp_skip_rbr = true;
}
return true;
@@ -655,7 +709,7 @@ static bool wait_for_entering_dp_alt_mode(struct dc_link *link)
unsigned long long enter_timestamp;
unsigned long long finish_timestamp;
unsigned long long time_taken_in_ns;
- int tries_taken;
+ unsigned int tries_taken;
/**
* this function will only exist if we are on dcn21 (is_in_alt_mode is a
@@ -769,6 +823,60 @@ static bool should_prepare_phy_clocks_for_link_verification(const struct dc *dc,
return !can_apply_seamless_boot && reason != DETECT_REASON_BOOT;
}
+static bool is_hdmi_frl_in_use(struct dc_link *link)
+{
+ int i;
+ unsigned int hdmi_conn_count = 0;
+ unsigned int hdmi_stream_count = 0;
+ bool hdmi_frl_in_use = false;
+ bool incoming_link_identical = false;
+
+ /*Enumerate HDMI connector from all present links */
+ for (i = 0; i < link->dc->link_count; i++) {
+ if (link->dc->links[i] != NULL &&
+ dc_is_hdmi_signal(link->dc->links[i]->connector_signal))
+ hdmi_conn_count++;
+ }
+ /* If less than 2 HDMI Connector, assume HPO is always available*/
+ if (hdmi_conn_count < 2)
+ return false;
+
+ /*Enumerate existing HDMI stream count*/
+ for (i = 0; i < link->dc->current_state->stream_count; i++) {
+ if (dc_is_hdmi_signal(link->dc->current_state->streams[i]->signal))
+ hdmi_stream_count++;
+ if (link == link->dc->current_state->streams[i]->link &&
+ (dc_is_hdmi_frl_signal(link->dc->current_state->streams[i]->signal)))
+ incoming_link_identical = true;
+ }
+
+ if (hdmi_stream_count > 1 || (hdmi_stream_count == 1 && !incoming_link_identical)) {
+ for (i = 0; i < link->dc->current_state->stream_count; i++) {
+ if (dc_is_hdmi_frl_signal(
+ link->dc->current_state->streams[i]->signal)) {
+ hdmi_frl_in_use = true;
+ break;
+ }
+ }
+ }
+
+ /* Check if previous link already has been assigned with FRL*/
+ if (!hdmi_frl_in_use && !incoming_link_identical) {
+ for (i = 0; i < link->dc->link_count; i++) {
+ if (link->dc->links[i] != NULL &&
+ link->dc->links[i]->local_sink != NULL &&
+ link != link->dc->links[i] &&
+ dc_is_hdmi_frl_signal(
+ link->dc->links[i]->local_sink->sink_signal)) {
+ hdmi_frl_in_use = true;
+ break;
+ }
+ }
+ }
+
+ return hdmi_frl_in_use;
+}
+
static void prepare_phy_clocks_for_destructive_link_verification(const struct dc *dc)
{
dc_z10_restore(dc);
@@ -796,6 +904,16 @@ static void verify_link_capability_destructive(struct dc_link *link,
dp_verify_link_cap_with_retries(
link, &known_limit_link_setting,
LINK_TRAINING_MAX_VERIFY_RETRY);
+ } else if (dc_is_hdmi_signal(link->local_sink->sink_signal)) {
+ if (!is_hdmi_frl_in_use(link)) {
+ link_set_all_streams_dpms_off_for_link(link);
+ hdmi_frl_verify_link_cap(link, &link->frl_reported_link_cap);
+ link->local_sink->sink_signal = (link->frl_verified_link_cap.frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE)
+ ? SIGNAL_TYPE_HDMI_FRL : SIGNAL_TYPE_HDMI_TYPE_A;
+ } else {
+ link->local_sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ link->frl_verified_link_cap.frl_link_rate = HDMI_FRL_LINK_RATE_DISABLE;
+ }
} else {
ASSERT(0);
}
@@ -815,6 +933,13 @@ static void verify_link_capability_non_destructive(struct dc_link *link)
link->verified_link_cap = link->reported_link_cap;
else
link->verified_link_cap = dp_get_max_link_cap(link);
+ } else if (dc_is_hdmi_signal(link->local_sink->sink_signal)) {
+ link->verified_link_cap = link->reported_link_cap;
+
+ if (is_hdmi_frl_in_use(link)) {
+ link->local_sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ link->frl_verified_link_cap.frl_link_rate = HDMI_FRL_LINK_RATE_DISABLE;
+ }
}
}
@@ -850,6 +975,27 @@ static bool should_verify_link_capability_destructively(struct dc_link *link,
destrictive = false;
}
}
+ } else if (dc_is_hdmi_signal(link->local_sink->sink_signal) && link->link_enc &&
+ link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE &&
+ link->local_sink->edid_caps.max_frl_rate != 0) {
+ int i = 0;
+ struct pipe_ctx *pipes =
+ link->dc->current_state->res_ctx.pipe_ctx;
+
+ destrictive = true;
+ if (is_hdmi_frl_in_use(link)) {
+ destrictive = false;
+ } else if (link->local_sink->edid_caps.panel_patch.skip_frl_pre_training) {
+ for (i = 0; i < MAX_PIPES; i++) {
+ if (pipes[i].stream != NULL &&
+ pipes[i].stream->link == link) {
+ /*If link is already active, skip PHY programming*/
+ if (link->link_status.link_active) {
+ destrictive = false;
+ }
+ }
+ }
+ }
}
return destrictive;
@@ -1007,6 +1153,7 @@ static bool detect_link_and_local_sink(struct dc_link *link,
/* From Disconnected-to-Connected. */
switch (link->connector_signal) {
+ case SIGNAL_TYPE_HDMI_FRL:
case SIGNAL_TYPE_HDMI_TYPE_A: {
sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
if (aud_support->hdmi_audio_native)
@@ -1069,8 +1216,11 @@ static bool detect_link_and_local_sink(struct dc_link *link,
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))
+ if (!wait_for_entering_dp_alt_mode(link)) {
+ if (prev_sink)
+ dc_sink_release(prev_sink);
return false;
+ }
}
if (!detect_dp(link, &sink_caps, reason)) {
@@ -1234,6 +1384,13 @@ static bool detect_link_and_local_sink(struct dc_link *link,
if (dc_is_hdmi_signal(link->connector_signal))
read_scdc_caps(link->ddc, link->local_sink);
+ if (dc_is_hdmi_signal(link->connector_signal) && dc->debug.enable_hdmi_idcc) {
+ memset(&link->hdmi_cable_id, 0, sizeof(union hdmi_idcc_cable_id));
+ read_idcc_data(link->ddc, HDMI_IDCC_SCOPE_RW_CA,
+ link->hdmi_cable_id.raw, 0, 4);
+ }
+ if (sink->edid_caps.rr_capable)
+ hdmi_frl_write_read_request_enable(link->ddc);
/* When FreeSync is toggled through OSD,
* we see same EDID no matter what. Check MCCS caps
* to see if we should update FreeSync caps now.
@@ -1248,6 +1405,12 @@ static bool detect_link_and_local_sink(struct dc_link *link,
same_edid = false;
}
+ if (reason != DETECT_REASON_FALLBACK && dc_is_hdmi_signal(link->connector_signal) &&
+ link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE && sink->edid_caps.max_frl_rate != 0) {
+ hdmi_frl_retrieve_link_cap(link, link->local_sink);
+ }
+ if (reason == DETECT_REASON_FALLBACK && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)
+ same_edid = false;
if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT &&
sink_caps.transaction_type ==
DDC_TRANSACTION_TYPE_I2C_OVER_AUX) {
@@ -1262,6 +1425,8 @@ static bool detect_link_and_local_sink(struct dc_link *link,
link_disconnect_remap(prev_sink, link);
sink = prev_sink;
prev_sink = NULL;
+ if (reason == DETECT_REASON_FALLBACK && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)
+ sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
}
if (!sink->edid_caps.analog)
@@ -1529,6 +1694,7 @@ bool link_is_hdcp22(struct dc_link *link, enum signal_type signal)
case SIGNAL_TYPE_DVI_SINGLE_LINK:
case SIGNAL_TYPE_DVI_DUAL_LINK:
case SIGNAL_TYPE_HDMI_TYPE_A:
+ case SIGNAL_TYPE_HDMI_FRL:
ret = (link->hdcp_caps.rx_caps.fields.version == 0x4) ? 1:0;
break;
default:
@@ -1562,7 +1728,7 @@ static bool link_add_remote_sink_helper(struct dc_link *dc_link, struct dc_sink
struct dc_sink *link_add_remote_sink(
struct dc_link *link,
const uint8_t *edid,
- int len,
+ unsigned int len,
struct dc_sink_init_data *init_data)
{
struct dc_sink *dc_sink;
@@ -1619,7 +1785,7 @@ fail_add_sink:
void link_remove_remote_sink(struct dc_link *link, struct dc_sink *sink)
{
- int i;
+ unsigned int i;
if (!link->sink_count) {
BREAK_TO_DEBUGGER();
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.h b/drivers/gpu/drm/amd/display/dc/link/link_detection.h
index 1ab29476060b..e8d29fa1550d 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.h
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.h
@@ -32,7 +32,7 @@ bool link_detect_connection_type(struct dc_link *link,
struct dc_sink *link_add_remote_sink(
struct dc_link *link,
const uint8_t *edid,
- int len,
+ unsigned int len,
struct dc_sink_init_data *init_data);
void link_remove_remote_sink(struct dc_link *link, struct dc_sink *sink);
bool link_reset_cur_dp_mst_topology(struct dc_link *link);
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
index e12c25896364..48b086d15ab0 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
@@ -48,6 +48,7 @@
#include "protocols/link_edp_panel_control.h"
#include "protocols/link_dp_panel_replay.h"
#include "protocols/link_dp_dpia_bw.h"
+#include "link/protocols/link_hdmi_frl.h"
#include "dm_helpers.h"
#include "link_enc_cfg.h"
@@ -71,6 +72,10 @@
DC_LOG_RETIMER_REDRIVER( \
__VA_ARGS__)
+#define FRL_INFO(...) \
+ DC_LOG_HDMI_FRL_LTP( \
+ __VA_ARGS__)
+
#define MAX_MTP_SLOT_COUNT 64
#define LINK_TRAINING_ATTEMPTS 4
#define PEAK_FACTOR_X1000 1006
@@ -379,7 +384,7 @@ static bool write_i2c_retimer_vga(
for (size_t i = 0; i < ARRAY_SIZE(vga_data); i++) {
if (!write_i2c_retimer_offset_value(link, address, vga_data[i][0], vga_data[i][1])) {
- DC_LOG_ERROR("Set retimer failed, vga index: %zu\n", i);
+ DC_LOG_DEBUG("Set retimer failed, vga index: %zu\n", i);
return false;
}
}
@@ -400,7 +405,7 @@ static bool write_i2c_retimer_byte(
return true;
if (!write_i2c_retimer_offset_value(link, address, index, value)) {
- DC_LOG_ERROR("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value);
+ DC_LOG_DEBUG("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value);
return false;
}
@@ -416,14 +421,14 @@ static bool write_i2c_retimer_byte(
if (!link_query_ddc_data(
link->ddc, address, &offset, 1, &value, 1
)) {
- DC_LOG_ERROR("Set retimer failed, link_query_ddc_data\n");
+ DC_LOG_DEBUG("Set retimer failed, link_query_ddc_data\n");
return false;
}
}
value |= apply_rx_tx_change;
if (!write_i2c_retimer_offset_value(link, address, offset, value)) {
- DC_LOG_ERROR("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value);
+ DC_LOG_DEBUG("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value);
return false;
}
}
@@ -444,7 +449,7 @@ static bool write_i2c_retimer_setting(
uint8_t value = settings->reg_settings[i].i2c_reg_val;
if (!write_i2c_retimer_byte(link, address, index, value)) {
- DC_LOG_ERROR("Set retimer failed, index: %zu\n", i);
+ DC_LOG_DEBUG("Set retimer failed, index: %zu\n", i);
return false;
}
}
@@ -455,7 +460,7 @@ static bool write_i2c_retimer_setting(
uint8_t value = settings->reg_settings_6g[i].i2c_reg_val;
if (!write_i2c_retimer_byte(link, address, index, value)) {
- DC_LOG_ERROR("Set retimer failed, 6g index: %zu\n", i);
+ DC_LOG_DEBUG("Set retimer failed, 6g index: %zu\n", i);
return false;
}
}
@@ -487,7 +492,7 @@ static bool write_i2c_default_retimer_setting(
for (size_t i = 0; i < ARRAY_SIZE(data); i++) {
if (!write_i2c_retimer_offset_value(link, address, data[i][0], data[i][1])) {
- DC_LOG_ERROR("Set default retimer failed, index: %zu\n", i);
+ DC_LOG_DEBUG("Set default retimer failed, index: %zu\n", i);
return false;
}
}
@@ -519,23 +524,31 @@ static bool write_i2c_redriver_setting(
);
if (!success)
- DC_LOG_ERROR("Set redriver failed");
+ DC_LOG_DEBUG("Set redriver failed");
return success;
}
+static struct link_encoder *get_link_encoder(struct pipe_ctx *pipe_ctx)
+{
+ struct link_encoder *link_enc
+ = pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment
+ ? pipe_ctx->link_res.dio_link_enc
+ : link_enc_cfg_get_link_enc(pipe_ctx->stream->link);
+
+ ASSERT(link_enc);
+ return link_enc;
+}
+
static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
{
struct cp_psp *cp_psp = &pipe_ctx->stream->ctx->cp_psp;
- struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
+ struct link_encoder *link_enc = get_link_encoder(pipe_ctx);
struct cp_psp_stream_config config = {0};
enum dp_panel_mode panel_mode =
dp_get_panel_mode(pipe_ctx->stream->link);
if (cp_psp == NULL || cp_psp->funcs.update_stream_config == NULL)
return;
- if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment)
- link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link);
- ASSERT(link_enc);
if (link_enc == NULL)
return;
@@ -544,26 +557,35 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
/* dig front end */
config.dig_fe = (uint8_t) pipe_ctx->stream_res.stream_enc->stream_enc_inst;
+ if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+ config.dig_fe = (uint8_t)pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst;
/* stream encoder index */
- config.stream_enc_idx = pipe_ctx->stream_res.stream_enc->id - ENGINE_ID_DIGA;
+ config.stream_enc_idx = (uint8_t)(pipe_ctx->stream_res.stream_enc->id - ENGINE_ID_DIGA);
if (dp_is_128b_132b_signal(pipe_ctx))
config.stream_enc_idx =
- pipe_ctx->stream_res.hpo_dp_stream_enc->id - ENGINE_ID_HPO_DP_0;
+ (uint8_t)(pipe_ctx->stream_res.hpo_dp_stream_enc->id - ENGINE_ID_HPO_DP_0);
+ if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+ config.stream_enc_idx =
+ (uint8_t)(pipe_ctx->stream_res.hpo_frl_stream_enc->id - ENGINE_ID_HPO_0);
/* dig back end */
config.dig_be = pipe_ctx->stream->link->link_enc_hw_inst;
+ if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+ config.dig_be = (uint8_t)pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst;
/* link encoder index */
- config.link_enc_idx = link_enc->transmitter - TRANSMITTER_UNIPHY_A;
+ config.link_enc_idx = (uint8_t)(link_enc->transmitter - TRANSMITTER_UNIPHY_A);
if (dp_is_128b_132b_signal(pipe_ctx))
- config.link_enc_idx = pipe_ctx->link_res.hpo_dp_link_enc->inst;
+ config.link_enc_idx = (uint8_t)pipe_ctx->link_res.hpo_dp_link_enc->inst;
+ if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+ config.link_enc_idx = (uint8_t)pipe_ctx->stream->link->hpo_frl_link_enc->inst;
/* dio output index is dpia index for DPIA endpoint & dcio index by default */
if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA)
- config.dio_output_idx = pipe_ctx->stream->link->link_id.enum_id - ENUM_ID_1;
+ config.dio_output_idx = (uint8_t)(pipe_ctx->stream->link->link_id.enum_id - ENUM_ID_1);
else
- config.dio_output_idx = link_enc->transmitter - TRANSMITTER_UNIPHY_A;
+ config.dio_output_idx = (uint8_t)(link_enc->transmitter - TRANSMITTER_UNIPHY_A);
/* phy index */
@@ -577,6 +599,7 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
config.assr_enabled = (panel_mode == DP_PANEL_MODE_EDP) ? 1 : 0;
config.mst_enabled = (pipe_ctx->stream->signal ==
SIGNAL_TYPE_DISPLAY_PORT_MST) ? 1 : 0;
+ config.frl_enabled = dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) ? 1 : 0;
config.dp2_enabled = dp_is_128b_132b_signal(pipe_ctx) ? 1 : 0;
config.usb4_enabled = (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) ?
1 : 0;
@@ -588,6 +611,38 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
cp_psp->funcs.update_stream_config(cp_psp->handle, &config);
}
+void link_wait_for_unlocked(struct dc_link *link)
+{
+ unsigned long long enter_timestamp;
+ unsigned long long finish_timestamp;
+ unsigned long long time_taken_in_ns;
+ bool waited = false;
+
+ DC_LOGGER_INIT(link->ctx->logger);
+
+ enter_timestamp = dm_get_timestamp(link->ctx);
+
+ while (link->is_link_locked) { // busy wait
+ if (!waited) {
+ DC_LOG_DC("%s: 0x%p ...", __func__, link);
+
+ waited = true;
+ }
+
+ udelay(1);
+ }
+
+ if (!waited)
+ return;
+
+ 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_DC("%s: 0x%p took %llu ms.", __func__,
+ link, div_u64(time_taken_in_ns, 1000000));
+}
+
static void set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
{
struct dc *dc = pipe_ctx->stream->ctx->dc;
@@ -595,6 +650,9 @@ static void set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
if (!dc_is_hdmi_signal(pipe_ctx->stream->signal))
return;
+ if (pipe_ctx->stream->timing.flags.DSC)
+ return;
+
dc->hwss.set_avmute(pipe_ctx, enable);
}
@@ -737,6 +795,22 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
/* PPS SDP is set elsewhere because it has to be done after DIG FE is connected to DIG BE */
}
+ else if (dc_is_hdmi_frl_signal(stream->signal)) {
+ uint8_t dsc_packed_pps[128];
+ struct dc_crtc_timing patched_crtc_timing = stream->timing;
+
+ DC_LOG_DSC("Setting stream encoder DSC config for engine %d:", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
+ dsc_optc_config_log(dsc, &dsc_optc_cfg);
+
+ /* if we are borrowing from hblank, h_addressable and pic_width need to be adjusted */
+ if (dc->debug.enable_hblank_borrow) {
+ dsc_cfg.pic_width = stream->timing.h_addressable;
+ }
+
+ dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg, &dsc_packed_pps[0]);
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &patched_crtc_timing, &dsc_packed_pps[0]);
+ }
/* Enable DSC in OPTC */
DC_LOG_DSC("Setting optc DSC config for tg instance %d:", pipe_ctx->stream_res.tg->inst);
@@ -768,6 +842,9 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
pipe_ctx->stream_res.stream_enc, false, NULL, true);
}
}
+ else if (dc_is_hdmi_frl_signal(stream->signal))
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
/* disable DSC block */
for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
@@ -855,6 +932,12 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi
&dsc_packed_pps[0],
immediate_update);
}
+ else if (dc_is_hdmi_frl_signal(stream->signal)) {
+ //TODO: bring HDMI FRL in line with DP
+ DC_LOG_DSC("Setting stream encoder DSC PPS SDP for engine %d\n", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, &dsc_packed_pps[0]);
+ }
} else {
/* disable DSC PPS in stream encoder */
memset(&stream->dsc_packed_pps[0], 0, sizeof(stream->dsc_packed_pps));
@@ -869,6 +952,10 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi
pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet(
pipe_ctx->stream_res.stream_enc, false, NULL, true);
}
+ else if (dc_is_hdmi_frl_signal(stream->signal))
+ //TODO: bring HDMI FRL in line with DP
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
}
return true;
@@ -1411,7 +1498,7 @@ static bool write_128b_132b_sst_payload_allocation_table(
if (allocate) {
avg_time_slots_per_mtp = link_calculate_sst_avg_time_slots_per_mtp(stream, link);
- req_slot_count = dc_fixpt_ceil(avg_time_slots_per_mtp);
+ req_slot_count = (uint8_t)dc_fixpt_ceil(avg_time_slots_per_mtp);
/// Validation should filter out modes that exceed link BW
ASSERT(req_slot_count <= MAX_MTP_SLOT_COUNT);
if (req_slot_count > MAX_MTP_SLOT_COUNT)
@@ -1811,8 +1898,15 @@ static void enable_link_hdmi(struct pipe_ctx *pipe_ctx)
write_scdc_data(
stream->link->ddc,
stream->phy_pix_clk,
- stream->timing.flags.LTE_340MCSC_SCRAMBLE);
+ (stream->timing.flags.LTE_340MCSC_SCRAMBLE != 0));
+
+ if (dc->debug.enable_hdmi_idcc) {
+ union hdmi_idcc_source_id source_id;
+ source_id.raw = 0xff;
+ write_idcc_data(stream->link->ddc, HDMI_IDCC_SCOPE_WRITE,
+ &source_id.raw, 0, 1);
+ }
memset(&stream->link->cur_link_settings, 0,
sizeof(struct dc_link_settings));
@@ -1838,6 +1932,109 @@ static void enable_link_hdmi(struct pipe_ctx *pipe_ctx)
read_scdc_data(link->ddc);
}
+static enum dc_status enable_link_hdmi_frl(struct pipe_ctx *pipe_ctx)
+{
+ enum link_result link_stat = LINK_RESULT_UNKNOWN;
+ enum dc_status status = DC_OK;
+ struct dc_stream_state *stream = pipe_ctx->stream;
+ struct dc *core_dc = pipe_ctx->stream->ctx->dc;
+ enum clock_source_id frl_phy_clock_source_id;
+ bool frl_poll_start = true;
+
+ DC_LOGGER_INIT(stream->ctx->logger);
+
+ if ((!stream->link->link_enc) ||
+ (!stream->link->hpo_frl_link_enc) ||
+ (!core_dc->res_pool->dccg->funcs->enable_hdmicharclk) ||
+ (!(pipe_ctx->stream_res.hpo_frl_stream_enc)))
+ return DC_ERROR_UNEXPECTED;
+
+ /* get link settings for video mode timing */
+ hdmi_frl_decide_link_settings(stream, &stream->link->frl_link_settings, &pipe_ctx->dsc_padding_params);
+
+ switch (stream->link->frl_link_settings.frl_link_rate) {
+ case HDMI_FRL_LINK_RATE_3GBPS:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 166667;
+ break;
+ case HDMI_FRL_LINK_RATE_6GBPS:
+ case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 333333;
+ break;
+ case HDMI_FRL_LINK_RATE_8GBPS:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 444444;
+ break;
+ case HDMI_FRL_LINK_RATE_10GBPS:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 555555;
+ break;
+ case HDMI_FRL_LINK_RATE_12GBPS:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 666667;
+ break;
+ case HDMI_FRL_LINK_RATE_16GBPS:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 888889;
+ break;
+ case HDMI_FRL_LINK_RATE_20GBPS:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 1111111;
+ break;
+ case HDMI_FRL_LINK_RATE_24GBPS:
+ pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 1333333;
+ break;
+ default:
+ break;
+ }
+
+ stream->phy_pix_clk = pipe_ctx->stream_res.pix_clk_params.requested_sym_clk;
+
+ memset(&stream->link->cur_link_settings, 0,
+ sizeof(struct dc_link_settings));
+
+ /* Find proper clock source in HDMI FRL mode for phy used for DCCG */
+ frl_phy_clock_source_id = hdmi_frl_find_matching_phypll(stream->link);
+
+ FRL_INFO("FRL LINK TRAINING: LTS:P Start\n");
+ /* Setup FRL PHY, enable HDMI character clock and HPO link encoder */
+ core_dc->hwss.setup_hdmi_frl_link(stream->link,
+ (pipe_ctx->stream_res.hpo_frl_stream_enc->id - ENGINE_ID_HPO_0),
+ frl_phy_clock_source_id);
+
+ link_stat = hdmi_frl_perform_link_training_with_retries(stream->link);
+
+ if (core_dc->res_pool->dccg->funcs->set_valid_pixel_rate)
+ core_dc->res_pool->dccg->funcs->set_valid_pixel_rate(
+ core_dc->res_pool->dccg,
+ core_dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(core_dc->clk_mgr),
+ pipe_ctx->stream_res.tg->inst,
+ (stream->timing.pix_clk_100hz / 10));
+
+ /* Enable FRL packet transmission */
+ if (link_stat == LINK_RESULT_SUCCESS) {
+ stream->link->hpo_frl_link_enc->funcs->enable_output(
+ stream->link->hpo_frl_link_enc);
+ if (stream->link->frl_flags.apply_vsdb_rcc_wa)
+ stream->link->hpo_frl_link_enc->funcs->apply_vsdb_rcc_wa(stream->link->hpo_frl_link_enc);
+ if (frl_poll_start)
+ hdmi_frl_poll_start(stream->link->ddc);
+
+ FRL_INFO("FRL LINK TRAINING: LTS:P Success\n");
+
+ /* Set HDMISTREAMCLK source to DTBCLK0 and bypass DTO */
+ if (core_dc->res_pool->dccg->funcs->set_hdmistreamclk) {
+ core_dc->res_pool->dccg->funcs->set_hdmistreamclk(
+ core_dc->res_pool->dccg,
+ DTBCLK0,
+ pipe_ctx->stream_res.tg->inst);
+ }
+
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_enable(
+ pipe_ctx->stream_res.hpo_frl_stream_enc,
+ pipe_ctx->stream_res.tg->inst);
+ } else {
+ status = DC_FAIL_HDMI_FRL_LINK_TRAINING;
+ stream->link->frl_link_settings.frl_link_rate = 0;
+ }
+
+ return status;
+}
+
static enum dc_status enable_link_dp(struct dc_state *state,
struct pipe_ctx *pipe_ctx)
{
@@ -2016,9 +2213,7 @@ static enum dc_status enable_link_dp_mst(
return enable_link_dp(state, pipe_ctx);
}
-static enum dc_status enable_link_analog(
- struct dc_state *state,
- struct pipe_ctx *pipe_ctx)
+static enum dc_status enable_link_analog(struct pipe_ctx *pipe_ctx)
{
struct dc_link *link = pipe_ctx->stream->link;
@@ -2078,12 +2273,20 @@ static enum dc_status enable_link(
enable_link_hdmi(pipe_ctx);
status = DC_OK;
break;
+ case SIGNAL_TYPE_HDMI_FRL:
+ if (link->local_sink &&
+ link->local_sink->edid_caps.panel_patch.delay_hdmi_link_training &&
+ stream->timing.pix_clk_100hz == 6627500) {
+ msleep(link->local_sink->edid_caps.panel_patch.delay_hdmi_link_training);
+ }
+ status = enable_link_hdmi_frl(pipe_ctx);
+ break;
case SIGNAL_TYPE_LVDS:
enable_link_lvds(pipe_ctx);
status = DC_OK;
break;
case SIGNAL_TYPE_RGB:
- status = enable_link_analog(state, pipe_ctx);
+ status = enable_link_analog(pipe_ctx);
break;
case SIGNAL_TYPE_VIRTUAL:
status = enable_link_virtual(pipe_ctx);
@@ -2099,10 +2302,12 @@ static enum dc_status enable_link(
return status;
}
-static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, int bw)
+static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, int stream_bw)
{
+ ASSERT(stream->sink);
+
struct dc_link *link = stream->sink->link;
- int req_bw = bw;
+ int req_bw = stream_bw;
DC_LOGGER_INIT(link->ctx->logger);
@@ -2110,44 +2315,45 @@ static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, i
return false;
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
- int sink_index = 0;
- int i = 0;
+ int sink_index = -1;
+ unsigned int i = 0;
for (i = 0; i < link->sink_count; i++) {
if (link->remote_sinks[i] == NULL)
continue;
- if (stream->sink->sink_id != link->remote_sinks[i]->sink_id)
+ if (stream->sink->sink_id != link->remote_sinks[i]->sink_id) {
+ DC_LOG_DEBUG("%s: add remote_sink=%s, request_bw=%d\n", __func__,
+ (const char *)(&link->remote_sinks[i]->edid_caps.display_name[0]),
+ link->dpia_bw_alloc_config.remote_sink_req_bw[i]);
+
req_bw += link->dpia_bw_alloc_config.remote_sink_req_bw[i];
- else
+ } else
sink_index = i;
}
- link->dpia_bw_alloc_config.remote_sink_req_bw[sink_index] = bw;
- }
+ if (sink_index >= 0)
+ link->dpia_bw_alloc_config.remote_sink_req_bw[sink_index] = stream_bw;
+ else
+ DC_LOG_WARNING("%s: stream sink_id=%u not found in remote_sinks[]\n",
+ __func__, stream->sink->sink_id);
- link->dpia_bw_alloc_config.dp_overhead = link_dpia_get_dp_overhead(link);
- req_bw += link->dpia_bw_alloc_config.dp_overhead;
+ if (req_bw) {
+ link->dpia_bw_alloc_config.dp_overhead = link_dpia_get_dp_overhead(link);
+ req_bw += link->dpia_bw_alloc_config.dp_overhead;
+ } else
+ link->dpia_bw_alloc_config.dp_overhead = 0;
+ }
link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, req_bw);
- if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
- int i = 0;
-
- for (i = 0; i < link->sink_count; i++) {
- if (link->remote_sinks[i] == NULL)
- continue;
- DC_LOG_DEBUG("%s, remote_sink=%s, request_bw=%d\n", __func__,
- (const char *)(&link->remote_sinks[i]->edid_caps.display_name[0]),
- link->dpia_bw_alloc_config.remote_sink_req_bw[i]);
- }
- }
-
return true;
}
static bool allocate_usb4_bandwidth(struct dc_stream_state *stream)
{
+ ASSERT(stream->sink);
+
bool ret;
int bw = dc_bandwidth_in_kbps_from_timing(&stream->timing,
@@ -2167,34 +2373,44 @@ static bool deallocate_usb4_bandwidth(struct dc_stream_state *stream)
return ret;
}
-void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
+static struct vpg *get_vpg(struct pipe_ctx *pipe_ctx)
{
- struct dc *dc = pipe_ctx->stream->ctx->dc;
- struct dc_stream_state *stream = pipe_ctx->stream;
- struct dc_link *link = stream->sink->link;
- struct vpg *vpg = pipe_ctx->stream_res.stream_enc->vpg;
- enum dp_panel_mode panel_mode_dp = dp_get_panel_mode(link);
+ if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+ return pipe_ctx->stream_res.hpo_frl_stream_enc->vpg;
+ else if (dp_is_128b_132b_signal(pipe_ctx))
+ return pipe_ctx->stream_res.hpo_dp_stream_enc->vpg;
+ else
+ return pipe_ctx->stream_res.stream_enc->vpg;
+}
+enum dc_status link_set_dpms_off(struct pipe_ctx *pipe_ctx)
+{
DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
+ struct dc_stream_state *stream = pipe_ctx->stream;
+ struct dc *dc = stream->ctx->dc;
+ struct dc_link *link = stream->link;
+ struct vpg *vpg = get_vpg(pipe_ctx);
+ enum dp_panel_mode panel_mode_dp = dp_get_panel_mode(link);
ASSERT(is_master_pipe_for_link(link, pipe_ctx));
- if (dp_is_128b_132b_signal(pipe_ctx))
- vpg = pipe_ctx->stream_res.hpo_dp_stream_enc->vpg;
- if (dc_is_virtual_signal(pipe_ctx->stream->signal))
- return;
+ if (dc_is_virtual_signal(stream->signal))
+ /* No real hardware to program for virtual signals. */
+ return DC_DPMS_SKIPPED_HANDSHAKE;
- if (pipe_ctx->stream->sink) {
- if (pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
- pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_NONE) {
+ if (stream->sink) {
+ if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
+ stream->sink->sink_signal != SIGNAL_TYPE_NONE) {
DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x link=%d sink_count=%d\n", __func__,
- pipe_ctx->stream->sink->edid_caps.display_name,
- pipe_ctx->stream->signal, link->link_index, link->sink_count);
+ stream->sink->edid_caps.display_name,
+ stream->signal, link->link_index, link->sink_count);
}
}
- if (!pipe_ctx->stream->sink->edid_caps.panel_patch.skip_avmute) {
- if (dc_is_hdmi_signal(pipe_ctx->stream->signal))
+ link_wait_for_unlocked(link);
+
+ if (stream->sink && !stream->sink->edid_caps.panel_patch.skip_avmute) {
+ if (dc_is_hdmi_signal(stream->signal))
set_avmute(pipe_ctx, true);
}
@@ -2204,15 +2420,15 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
dc->hwss.blank_stream(pipe_ctx);
if (pipe_ctx->link_config.dp_tunnel_settings.should_use_dp_bw_allocation)
- deallocate_usb4_bandwidth(pipe_ctx->stream);
+ deallocate_usb4_bandwidth(stream);
- if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
+ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
deallocate_mst_payload(pipe_ctx);
- else if (dc_is_dp_sst_signal(pipe_ctx->stream->signal) &&
+ else if (dc_is_dp_sst_signal(stream->signal) &&
dp_is_128b_132b_signal(pipe_ctx))
update_sst_payload(pipe_ctx, false);
- if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) {
+ if (dc_is_hdmi_signal(stream->signal)) {
struct ext_hdmi_settings settings = {0};
enum engine_id eng_id = pipe_ctx->stream_res.stream_enc->id;
@@ -2235,7 +2451,7 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
}
}
- if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT &&
+ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT &&
!dp_is_128b_132b_signal(pipe_ctx)) {
/* In DP1.x SST mode, our encoder will go to TPS1
@@ -2245,17 +2461,19 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
* state machine.
* In DP2 or MST mode, our encoder will stay video active
*/
- disable_link(pipe_ctx->stream->link, &pipe_ctx->link_res, pipe_ctx->stream->signal);
+ disable_link(link, &pipe_ctx->link_res, stream->signal);
dc->hwss.disable_stream(pipe_ctx);
} else {
dc->hwss.disable_stream(pipe_ctx);
- disable_link(pipe_ctx->stream->link, &pipe_ctx->link_res, pipe_ctx->stream->signal);
+ disable_link(link, &pipe_ctx->link_res, stream->signal);
}
edp_set_panel_assr(link, pipe_ctx, &panel_mode_dp, false);
- if (pipe_ctx->stream->timing.flags.DSC) {
- if (dc_is_dp_signal(pipe_ctx->stream->signal))
+ if (stream->timing.flags.DSC) {
+ if (dc_is_dp_signal(stream->signal))
link_set_dsc_enable(pipe_ctx, false);
+ else if (dc_is_hdmi_frl_signal(stream->signal))
+ link_set_dsc_on_stream(pipe_ctx, false);
}
if (dp_is_128b_132b_signal(pipe_ctx)) {
if (pipe_ctx->stream_res.tg->funcs->set_out_mux)
@@ -2268,64 +2486,73 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
/* for psp not exist case */
if (link->connector_signal == SIGNAL_TYPE_EDP && dc->debug.psp_disabled_wa) {
/* reset internal save state to default since eDP is off */
- enum dp_panel_mode panel_mode = dp_get_panel_mode(pipe_ctx->stream->link);
+ enum dp_panel_mode panel_mode = dp_get_panel_mode(link);
/* since current psp not loaded, we need to reset it to default */
link->panel_mode = panel_mode;
}
+
+ return DC_DPMS_SUCCESS;
}
-void link_set_dpms_on(
+static enum dc_status link_set_dpms_on_pre_enable_link(
struct dc_state *state,
- struct pipe_ctx *pipe_ctx)
+ struct pipe_ctx *pipe_ctx
+)
{
- struct dc *dc = pipe_ctx->stream->ctx->dc;
+ // Used conditionally in ifdef'ed diagnostic builds
+ (void) state;
+
+ DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
struct dc_stream_state *stream = pipe_ctx->stream;
- struct dc_link *link = stream->sink->link;
- enum dc_status status;
- struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
+ struct dc *dc = stream->ctx->dc;
+ struct dc_link *link = stream->link;
+
enum otg_out_mux_dest otg_out_dest = OUT_MUX_DIO;
- struct vpg *vpg = pipe_ctx->stream_res.stream_enc->vpg;
+ struct vpg *vpg = get_vpg(pipe_ctx);
const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
bool apply_edp_fast_boot_optimization =
- pipe_ctx->stream->apply_edp_fast_boot_optimization;
-
- DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
+ stream->apply_edp_fast_boot_optimization;
ASSERT(is_master_pipe_for_link(link, pipe_ctx));
- if (dp_is_128b_132b_signal(pipe_ctx))
- vpg = pipe_ctx->stream_res.hpo_dp_stream_enc->vpg;
- if (dc_is_virtual_signal(pipe_ctx->stream->signal))
- return;
+ if (dc_is_virtual_signal(stream->signal))
+ /* No real hardware to program for virtual signals. */
+ return DC_DPMS_SKIPPED_HANDSHAKE;
- if (pipe_ctx->stream->sink) {
- if (pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
- pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_NONE) {
+ if (stream->sink) {
+ if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
+ stream->sink->sink_signal != SIGNAL_TYPE_NONE) {
DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x link=%d sink_count=%d\n", __func__,
- pipe_ctx->stream->sink->edid_caps.display_name,
- pipe_ctx->stream->signal,
+ stream->sink->edid_caps.display_name,
+ stream->signal,
link->link_index,
link->sink_count);
}
}
- if (!dc->config.unify_link_enc_assignment)
- link_enc = link_enc_cfg_get_link_enc(link);
- ASSERT(link_enc);
+ link_wait_for_unlocked(link);
- if (!dc_is_virtual_signal(pipe_ctx->stream->signal)
+ if (!dc_is_virtual_signal(stream->signal)
+ && !dc_is_hdmi_frl_signal(stream->signal)
&& !dp_is_128b_132b_signal(pipe_ctx)) {
+ struct link_encoder *link_enc = get_link_encoder(pipe_ctx);
+
if (link_enc)
link_enc->funcs->setup(
link_enc,
- pipe_ctx->stream->signal);
+ stream->signal);
}
- pipe_ctx->stream->link->link_state_valid = true;
+ link->link_state_valid = true;
+
+ if (dc_is_hdmi_frl_signal(stream->signal))
+ hdmi_frl_decide_link_settings(stream, &link->frl_link_settings, &pipe_ctx->dsc_padding_params);
if (pipe_ctx->stream_res.tg->funcs->set_out_mux) {
if (dp_is_128b_132b_signal(pipe_ctx))
otg_out_dest = OUT_MUX_HPO_DP;
+ else if (dc_is_hdmi_frl_signal(stream->signal))
+ otg_out_dest = OUT_MUX_HPO_FRL;
else
otg_out_dest = OUT_MUX_DIO;
pipe_ctx->stream_res.tg->funcs->set_out_mux(pipe_ctx->stream_res.tg, otg_out_dest);
@@ -2333,7 +2560,7 @@ void link_set_dpms_on(
link_hwss->setup_stream_attribute(pipe_ctx);
- pipe_ctx->stream->apply_edp_fast_boot_optimization = false;
+ stream->apply_edp_fast_boot_optimization = false;
// Enable VPG before building infoframe
if (vpg && vpg->funcs->vpg_poweron)
@@ -2342,29 +2569,31 @@ void link_set_dpms_on(
resource_build_info_frame(pipe_ctx);
dc->hwss.update_info_frame(pipe_ctx);
- if (dc_is_dp_signal(pipe_ctx->stream->signal))
+ if (dc_is_dp_signal(stream->signal))
dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_UPDATE_INFO_FRAME);
/* Do not touch link on seamless boot optimization. */
- if (pipe_ctx->stream->apply_seamless_boot_optimization) {
- pipe_ctx->stream->dpms_off = false;
+ if (stream->apply_seamless_boot_optimization) {
+ stream->dpms_off = false;
/* Still enable stream features & audio on seamless boot for DP external displays */
- if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT) {
+ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT) {
enable_stream_features(pipe_ctx);
dc->hwss.enable_audio_stream(pipe_ctx);
}
update_psp_stream_config(pipe_ctx, false);
- return;
+
+ /* Seamless boot: hardware already enabled by BIOS; skip link training. */
+ return DC_DPMS_SKIPPED_HANDSHAKE;
}
/* eDP lit up by bios already, no need to enable again. */
- if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP &&
+ if (stream->signal == SIGNAL_TYPE_EDP &&
apply_edp_fast_boot_optimization &&
- !pipe_ctx->stream->timing.flags.DSC &&
+ !stream->timing.flags.DSC &&
!pipe_ctx->next_odm_pipe) {
- pipe_ctx->stream->dpms_off = false;
+ stream->dpms_off = false;
update_psp_stream_config(pipe_ctx, false);
if (link->is_dds) {
@@ -2374,11 +2603,16 @@ void link_set_dpms_on(
msleep(post_oui_delay);
}
- return;
+ /* eDP already lit by BIOS; skip standard enable steps. */
+ return DC_DPMS_SKIPPED_HANDSHAKE;
}
- if (pipe_ctx->stream->dpms_off)
- return;
+ if (stream->dpms_off)
+ /*
+ * Stream is configured as DPMS-off; skip link enable.
+ * Hardware will NOT be in a fully enabled state after this early exit.
+ */
+ return DC_DPMS_FAILED_INCOMPLETE;
/* For Dp tunneling link, a pending HPD means that we have a race condition between processing
* current link and processing the pending HPD. If we enable the link now, we may end up with a
@@ -2386,7 +2620,11 @@ void link_set_dpms_on(
*/
if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->is_hpd_pending) {
DC_LOG_DEBUG("%s, Link%d HPD is pending, not enable it.\n", __func__, link->link_index);
- return;
+ /*
+ * Pending HPD on USB4 DPIA link: skip enable to avoid race condition.
+ * Hardware will NOT be in a fully enabled state after this early exit.
+ */
+ return DC_DPMS_FAILED_INCOMPLETE;
}
/* Have to setup DSC before DIG FE and BE are connected (which happens before the
@@ -2395,37 +2633,75 @@ void link_set_dpms_on(
* will be automatically set at a later time when the video is enabled
* (DP_VID_STREAM_EN = 1).
*/
- if (pipe_ctx->stream->timing.flags.DSC) {
- if (dc_is_dp_signal(pipe_ctx->stream->signal) ||
- dc_is_virtual_signal(pipe_ctx->stream->signal))
+ if (stream->timing.flags.DSC) {
+ if (dc_is_dp_signal(stream->signal) ||
+ dc_is_virtual_signal(stream->signal))
link_set_dsc_enable(pipe_ctx, true);
}
if (link->replay_settings.config.replay_supported && !dc_is_embedded_signal(link->connector_signal))
dp_setup_replay(link, stream);
- status = enable_link(state, pipe_ctx);
+ return DC_DPMS_SUCCESS;
+}
+
+static enum dc_status link_set_dpms_on_enable_link(
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx
+)
+{
+ DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
+ struct dc_stream_state *stream = pipe_ctx->stream;
+ struct dc_link *link = stream->link;
+ const enum dc_status status = enable_link(state, pipe_ctx);
+
+ if (status == DC_OK)
+ return DC_DPMS_SUCCESS;
- if (status != DC_OK) {
- DC_LOG_WARNING("enabling link %u failed: %d\n",
- pipe_ctx->stream->link->link_index,
- status);
+ DC_LOG_WARNING("enabling link %u failed: %d\n", link->link_index, status);
- /* Abort stream enable *unless* the failure was due to
- * DP link training - some DP monitors will recover and
- * show the stream anyway. But MST displays can't proceed
- * without link training.
- */
- if (status != DC_FAIL_DP_LINK_TRAINING ||
- pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
- if (false == stream->link->link_status.link_active)
- disable_link(stream->link, &pipe_ctx->link_res,
- pipe_ctx->stream->signal);
- BREAK_TO_DEBUGGER();
- return;
- }
+ /* Abort stream enable *unless* the failure was due to
+ * DP link training - some DP monitors will recover and
+ * show the stream anyway. But MST displays can't proceed
+ * without link training.
+ */
+ switch (status) {
+ case DC_FAIL_DP_LINK_TRAINING:
+ case DC_FAIL_HDMI_FRL_LINK_TRAINING:
+ if (stream->signal != SIGNAL_TYPE_DISPLAY_PORT_MST)
+ return DC_DPMS_SUCCESS;
+ break;
+
+ default:
+ break;
}
+ if (!link->link_status.link_active)
+ disable_link(link, &pipe_ctx->link_res, stream->signal);
+
+ /*
+ * Link enable failed; do NOT set skip_remaining so that post_enable_link
+ * still runs and leaves hardware in a consistent state.
+ */
+ return DC_DPMS_FAILED_HANDSHAKE;
+}
+
+static enum dc_status link_set_dpms_on_post_enable_link(
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx
+)
+{
+ // Used conditionally in ifdef'ed diagnostic builds
+ (void) state;
+
+ struct dc_stream_state *stream = pipe_ctx->stream;
+ struct dc_link *link = stream->link;
+ struct hw_sequencer_funcs *hwss = &stream->ctx->dc->hwss;
+
+ if (stream->timing.flags.DSC && dc_is_hdmi_frl_signal(stream->signal))
+ //TODO: bring HDMI FRL in line with DP
+ link_set_dsc_on_stream(pipe_ctx, true);
+
/* turn off otg test pattern if enable */
if (pipe_ctx->stream_res.tg->funcs->set_test_pattern)
pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg,
@@ -2436,36 +2712,37 @@ void link_set_dpms_on(
* as a workaround for the incorrect value being applied
* from transmitter control.
*/
- if (!(dc_is_virtual_signal(pipe_ctx->stream->signal) ||
+ if (!(dc_is_virtual_signal(stream->signal) ||
+ dc_is_hdmi_frl_signal(stream->signal) ||
dp_is_128b_132b_signal(pipe_ctx))) {
+ struct link_encoder *link_enc = get_link_encoder(pipe_ctx);
- if (link_enc)
- link_enc->funcs->setup(
+ if (link_enc)
+ link_enc->funcs->setup(
link_enc,
- pipe_ctx->stream->signal);
-
- }
+ stream->signal);
+ }
- dc->hwss.enable_stream(pipe_ctx);
+ hwss->enable_stream(pipe_ctx);
/* Set DPS PPS SDP (AKA "info frames") */
- if (pipe_ctx->stream->timing.flags.DSC) {
- if (dc_is_dp_signal(pipe_ctx->stream->signal) ||
- dc_is_virtual_signal(pipe_ctx->stream->signal)) {
+ if (stream->timing.flags.DSC) {
+ if (dc_is_dp_signal(stream->signal) ||
+ dc_is_virtual_signal(stream->signal)) {
dp_set_dsc_on_rx(pipe_ctx, true);
link_set_dsc_pps_packet(pipe_ctx, true, true);
}
}
- if (dc_is_dp_signal(pipe_ctx->stream->signal))
+ if (dc_is_dp_signal(stream->signal))
dp_set_hblank_reduction_on_rx(pipe_ctx);
if (pipe_ctx->link_config.dp_tunnel_settings.should_use_dp_bw_allocation)
- allocate_usb4_bandwidth(pipe_ctx->stream);
+ allocate_usb4_bandwidth(stream);
- if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
+ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
allocate_mst_payload(pipe_ctx);
- else if (dc_is_dp_sst_signal(pipe_ctx->stream->signal) &&
+ else if (dc_is_dp_sst_signal(stream->signal) &&
dp_is_128b_132b_signal(pipe_ctx))
update_sst_payload(pipe_ctx, true);
@@ -2474,23 +2751,64 @@ void link_set_dpms_on(
* training and stream unblank resolves the corruption issue.
* This is workaround.
*/
- if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP &&
+ if (stream->signal == SIGNAL_TYPE_EDP &&
link->is_display_mux_present)
msleep(20);
- dc->hwss.unblank_stream(pipe_ctx,
- &pipe_ctx->stream->link->cur_link_settings);
+ hwss->unblank_stream(pipe_ctx,
+ &link->cur_link_settings);
if (stream->sink_patches.delay_ignore_msa > 0)
msleep(stream->sink_patches.delay_ignore_msa);
- if (dc_is_dp_signal(pipe_ctx->stream->signal))
+ if (dc_is_dp_signal(stream->signal))
enable_stream_features(pipe_ctx);
update_psp_stream_config(pipe_ctx, false);
- dc->hwss.enable_audio_stream(pipe_ctx);
+ hwss->enable_audio_stream(pipe_ctx);
- if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) {
+ if (dc_is_hdmi_signal(stream->signal))
set_avmute(pipe_ctx, false);
+
+ return DC_DPMS_SUCCESS;
+}
+
+enum dc_status link_set_dpms_on(
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx
+)
+{
+ enum dc_status result = DC_DPMS_SUCCESS;
+
+ typedef enum dc_status (*step)(struct dc_state *, struct pipe_ctx *);
+ const step steps[] = {
+ link_set_dpms_on_pre_enable_link,
+ link_set_dpms_on_enable_link,
+ link_set_dpms_on_post_enable_link,
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(steps); i++) {
+ const enum dc_status step_result = steps[i](state, pipe_ctx);
+
+ switch (step_result) {
+ case DC_DPMS_SUCCESS:
+ case DC_DPMS_FAILED_HANDSHAKE:
+ // Enum is ordered from "best" to "worst" results
+ result = max(result, step_result);
+ break;
+
+ case DC_DPMS_SKIPPED_HANDSHAKE:
+ return step_result;
+
+ case DC_DPMS_FAILED_INCOMPLETE:
+ ASSERT(false);
+ return step_result;
+
+ default:
+ ASSERT(false);
+ return step_result;
+ }
}
+
+ return result;
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.h b/drivers/gpu/drm/amd/display/dc/link/link_dpms.h
index bd6fc63064a3..6559bc04d90e 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.h
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.h
@@ -27,10 +27,10 @@
#define __DC_LINK_DPMS_H__
#include "link_service.h"
-void link_set_dpms_on(
+enum dc_status link_set_dpms_on(
struct dc_state *state,
struct pipe_ctx *pipe_ctx);
-void link_set_dpms_off(struct pipe_ctx *pipe_ctx);
+enum dc_status link_set_dpms_off(struct pipe_ctx *pipe_ctx);
void link_resume(struct dc_link *link);
void link_blank_all_dp_displays(struct dc *dc);
void link_blank_all_edp_displays(struct dc *dc);
@@ -50,4 +50,5 @@ struct fixed31_32 link_calculate_sst_avg_time_slots_per_mtp(
void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable);
bool link_set_dsc_enable(struct pipe_ctx *pipe_ctx, bool enable);
bool link_update_dsc_config(struct pipe_ctx *pipe_ctx);
+void link_wait_for_unlocked(struct dc_link *link);
#endif /* __DC_LINK_DPMS_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
index ae4c4ad05baa..7146f8356233 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
@@ -42,6 +42,7 @@
#include "protocols/link_dp_training.h"
#include "protocols/link_edp_panel_control.h"
#include "protocols/link_dp_panel_replay.h"
+#include "protocols/link_hdmi_frl.h"
#include "protocols/link_hpd.h"
#include "gpio_service_interface.h"
#include "atomfirmware.h"
@@ -101,6 +102,8 @@ static void construct_link_service_validation(struct link_service *link_srv)
link_srv->validate_mode_timing = link_validate_mode_timing;
link_srv->dp_link_bandwidth_kbps = dp_link_bandwidth_kbps;
link_srv->validate_dp_tunnel_bandwidth = link_validate_dp_tunnel_bandwidth;
+ link_srv->frl_link_bandwidth_kbps = frl_link_bandwidth_kbps;
+ link_srv->frl_margin_check_uncompressed_video = frl_capacity_computations_uncompressed_video;
link_srv->dp_required_hblank_size_bytes = dp_required_hblank_size_bytes;
}
@@ -121,6 +124,7 @@ static void construct_link_service_dpms(struct link_service *link_srv)
link_srv->set_dsc_on_stream = link_set_dsc_on_stream;
link_srv->set_dsc_enable = link_set_dsc_enable;
link_srv->update_dsc_config = link_update_dsc_config;
+ link_srv->wait_for_unlocked = link_wait_for_unlocked;
}
/* link ddc implements generic display communication protocols such as i2c, aux
@@ -139,6 +143,7 @@ static void construct_link_service_ddc(struct link_service *link_srv)
link_aux_transfer_with_retries_no_mutex;
link_srv->is_in_aux_transaction_mode = link_is_in_aux_transaction_mode;
link_srv->get_aux_defer_delay = link_get_aux_defer_delay;
+ link_srv->get_ddc_aux_inst = link_get_ddc_aux_inst;
}
/* link dp capability implements dp specific link capability retrieval sequence.
@@ -243,6 +248,25 @@ static void construct_link_service_dp_panel_replay(struct link_service *link_srv
link_srv->dp_pr_get_state = dp_pr_get_state;
}
+/* link hdmi frl implements FRL link capability and link training related
+ * functions. FRL link is established by order of retrieve_link, verify_link,
+ * and poll_status. Other helper functions exist to obtain information required
+ * to maintain the correct sequence according to HDMI specification. Each
+ * sequence and state inside link training functions are timing sensitive and order sensitive.
+ * It is mandatory that these functions are debugged with FRL_LTP output message
+ * configurable in DSAT. Any changes in the LT sequence should follow the HDMI
+ * specification as much as possible and tested through HDMI electrical and
+ * link layer compliance.
+ */
+static void construct_link_service_hdmi_frl(struct link_service *link_srv)
+{
+ link_srv->hdmi_frl_poll_status_flag = hdmi_frl_poll_status_flag;
+ link_srv->hdmi_frl_get_verified_link_cap =
+ hdmi_frl_get_verified_link_cap;
+ link_srv->hdmi_frl_set_preferred_link_settings =
+ hdmi_frl_set_preferred_link_settings;
+}
+
/* link dp cts implements dp compliance test automation protocols and manual
* testing interfaces for debugging and certification purpose.
*/
@@ -296,6 +320,7 @@ static void construct_link_service(struct link_service *link_srv)
construct_link_service_dp_irq_handler(link_srv);
construct_link_service_edp_panel_control(link_srv);
construct_link_service_dp_panel_replay(link_srv);
+ construct_link_service_hdmi_frl(link_srv);
construct_link_service_dp_cts(link_srv);
construct_link_service_dp_trace(link_srv);
}
@@ -381,7 +406,7 @@ static bool encoder_is_external_dp(
static void link_destruct(struct dc_link *link)
{
- int i;
+ unsigned int i;
if (link->ddc)
link_destroy_ddc_service(&link->ddc);
@@ -401,6 +426,8 @@ static void link_destruct(struct dc_link *link)
link->link_enc->funcs->destroy(&link->link_enc);
}
+ if (link->hpo_frl_link_enc)
+ link->hpo_frl_link_enc->funcs->destroy(&link->hpo_frl_link_enc);
if (link->local_sink)
dc_sink_release(link->local_sink);
@@ -415,40 +442,43 @@ static enum channel_id get_ddc_line(struct dc_link *link)
channel = CHANNEL_ID_UNKNOWN;
- ddc = get_ddc_pin(link->ddc);
+ if (link->force_to_use_aux) {
+ channel = link->aux_hw_inst + 1;
+ } else {
+ ddc = get_ddc_pin(link->ddc);
- if (ddc) {
- switch (dal_ddc_get_line(ddc)) {
- case GPIO_DDC_LINE_DDC1:
- channel = CHANNEL_ID_DDC1;
- break;
- case GPIO_DDC_LINE_DDC2:
- channel = CHANNEL_ID_DDC2;
- break;
- case GPIO_DDC_LINE_DDC3:
- channel = CHANNEL_ID_DDC3;
- break;
- case GPIO_DDC_LINE_DDC4:
- channel = CHANNEL_ID_DDC4;
- break;
- case GPIO_DDC_LINE_DDC5:
- channel = CHANNEL_ID_DDC5;
- break;
- case GPIO_DDC_LINE_DDC6:
- channel = CHANNEL_ID_DDC6;
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- channel = CHANNEL_ID_DDC_VGA;
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- channel = CHANNEL_ID_I2C_PAD;
- break;
- default:
- BREAK_TO_DEBUGGER();
- break;
+ if (ddc) {
+ switch (dal_ddc_get_line(ddc)) {
+ case GPIO_DDC_LINE_DDC1:
+ channel = CHANNEL_ID_DDC1;
+ break;
+ case GPIO_DDC_LINE_DDC2:
+ channel = CHANNEL_ID_DDC2;
+ break;
+ case GPIO_DDC_LINE_DDC3:
+ channel = CHANNEL_ID_DDC3;
+ break;
+ case GPIO_DDC_LINE_DDC4:
+ channel = CHANNEL_ID_DDC4;
+ break;
+ case GPIO_DDC_LINE_DDC5:
+ channel = CHANNEL_ID_DDC5;
+ break;
+ case GPIO_DDC_LINE_DDC6:
+ channel = CHANNEL_ID_DDC6;
+ break;
+ case GPIO_DDC_LINE_DDC_VGA:
+ channel = CHANNEL_ID_DDC_VGA;
+ break;
+ case GPIO_DDC_LINE_I2C_PAD:
+ channel = CHANNEL_ID_I2C_PAD;
+ break;
+ default:
+ BREAK_TO_DEBUGGER();
+ break;
+ }
}
}
-
return channel;
}
@@ -516,7 +546,7 @@ static bool construct_phy(struct dc_link *link,
sizeof(struct dc_link_settings));
link->link_id =
- bios->funcs->get_connector_id(bios, init_params->connector_index);
+ bios->funcs->get_connector_id(bios, (uint8_t)init_params->connector_index);
link->ep_type = DISPLAY_ENDPOINT_PHY;
@@ -544,8 +574,15 @@ static bool construct_phy(struct dc_link *link,
if (bios->funcs->get_disp_connector_caps_info) {
bios->funcs->get_disp_connector_caps_info(bios, link->link_id, &disp_connect_caps_info);
- link->is_internal_display = disp_connect_caps_info.INTERNAL_DISPLAY;
+ link->is_internal_display = (disp_connect_caps_info.INTERNAL_DISPLAY != 0);
DC_LOG_DC("BIOS object table - is_internal_display: %d", link->is_internal_display);
+ link->no_ddc_pin = disp_connect_caps_info.NO_DDC_PIN != 0;
+ //reworked DP port on N-1 board, for testing on N-1, to be removed when IFWI ready
+ if (link->dc->config.dp_connector_no_native_i2c &&
+ (link->link_id.enum_id == link->dc->config.link_index_with_no_ddc))
+ link->no_ddc_pin = true;
+ link->force_to_use_aux = link->dc->config.dp_connector_no_native_i2c
+ && link->no_ddc_pin;
}
if (link->link_id.type != OBJECT_TYPE_CONNECTOR) {
@@ -568,15 +605,19 @@ static bool construct_phy(struct dc_link *link,
goto ddc_create_fail;
}
- /* Embedded display connectors such as LVDS may not have DDC. */
- if (!link->ddc->ddc_pin &&
- !dc_is_embedded_signal(link->connector_signal)) {
- DC_ERROR("Failed to get I2C info for connector!\n");
- goto ddc_create_fail;
- }
+ if (link->force_to_use_aux) {
+ link->ddc_hw_inst = link->aux_hw_inst;
+ } else {
+ /* Embedded display connectors such as LVDS may not have DDC. */
+ if (!link->ddc->ddc_pin &&
+ !dc_is_embedded_signal(link->connector_signal)) {
+ DC_ERROR("Failed to get I2C info for connector!\n");
+ goto ddc_create_fail;
+ }
- link->ddc_hw_inst =
- dal_ddc_get_line(get_ddc_pin(link->ddc));
+ link->ddc_hw_inst =
+ dal_ddc_get_line(get_ddc_pin(link->ddc));
+ }
enc_init_data.ctx = dc_ctx;
enc_init_data.connector = link->link_id;
@@ -598,13 +639,14 @@ static bool construct_phy(struct dc_link *link,
DC_LOG_DC("BIOS object table - hpd_gpio id: %d", enc_init_data.hpd_gpio->id);
DC_LOG_DC("BIOS object table - hpd_gpio en: %d", enc_init_data.hpd_gpio->en);
- } else {
+ } else if (link->ctx->dce_version > DCN_VERSION_4_01) {
struct graphics_object_hpd_info hpd_info;
if (link->ctx->dc_bios->funcs->get_hpd_info(link->ctx->dc_bios, link->link_id, &hpd_info) == BP_RESULT_OK) {
link->hpd_src = hpd_info.hpd_int_gpio_uid - 1;
link->irq_source_hpd = DC_IRQ_SOURCE_HPD1 + link->hpd_src;
enc_init_data.hpd_source = link->hpd_src;
+ enc_init_data.hpd_active_high = (hpd_info.hpd_active == 0) ? true : false;
DC_LOG_DC("BIOS object table - hpd_int_gpio_uid id: %d", hpd_info.hpd_int_gpio_uid);
} else {
ASSERT(0);
@@ -622,6 +664,7 @@ static bool construct_phy(struct dc_link *link,
DC_LOG_DC("BIOS object table - DP_IS_USB_C: %d", link->link_enc->features.flags.bits.DP_IS_USB_C);
DC_LOG_DC("BIOS object table - IS_DP2_CAPABLE: %d", link->link_enc->features.flags.bits.IS_DP2_CAPABLE);
+ DC_LOG_DC("BIOS object table - IS_HDMI_FRL_CAPABLE: %d", link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE);
switch (link->link_id.id) {
case CONNECTOR_ID_HDMI_TYPE_A:
@@ -825,12 +868,29 @@ static bool construct_phy(struct dc_link *link,
*/
program_hpd_filter(link);
+ /* If the connector is HDMI FRL capable, also create an HPO link encoder */
+ if ((link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE) &&
+ (!link->link_enc->features.flags.bits.DP_IS_USB_C) &&
+ (link->dc->res_pool->funcs->hpo_frl_link_enc_create)) {
+ enum engine_id hpo_eng_id;
+ hpo_eng_id = ENGINE_ID_HPO_0;
+
+ link->hpo_frl_link_enc = link->dc->res_pool->funcs->hpo_frl_link_enc_create(
+ hpo_eng_id,
+ dc_ctx);
+ if (link->hpo_frl_link_enc == NULL) {
+ DC_ERROR("Failed to create HPO link encoder!\n");
+ goto hpo_enc_create_fail;
+ }
+ }
+
link->psr_settings.psr_vtotal_control_support = false;
link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
link->replay_settings.config.replay_version = DC_REPLAY_VERSION_UNSUPPORTED;
DC_LOG_DC("BIOS object table - %s finished successfully.\n", __func__);
return true;
+hpo_enc_create_fail:
device_tag_fail:
link_enc_create_fail:
panel_cntl_create_fail:
@@ -897,7 +957,7 @@ static bool construct_dpia(struct dc_link *link,
}
/* Set dpia port index : 0 to number of dpia ports */
- link->ddc_hw_inst = init_params->connector_index;
+ link->ddc_hw_inst = (uint8_t)init_params->connector_index;
// Assign Dpia preferred eng_id
if (link->dc->res_pool->funcs->get_preferred_eng_id_dpia)
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.c b/drivers/gpu/drm/amd/display/dc/link/link_validation.c
index eb791285ed06..d7bd352880b3 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_validation.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.c
@@ -31,6 +31,7 @@
#include "link_validation.h"
#include "protocols/link_dp_capability.h"
#include "protocols/link_dp_dpia_bw.h"
+#include "protocols/link_hdmi_frl.h"
#include "resource.h"
#define DC_LOGGER_INIT(logger)
@@ -263,6 +264,195 @@ uint32_t dp_link_bandwidth_kbps(
return link_rate_per_lane_kbps * link_settings->lane_count / 10000 * total_data_bw_efficiency_x10000;
}
+uint32_t frl_link_bandwidth_kbps(enum hdmi_frl_link_rate link_rate)
+{
+ switch (link_rate) {
+ case HDMI_FRL_LINK_RATE_3GBPS:
+ return 9000000;
+ case HDMI_FRL_LINK_RATE_6GBPS:
+ return 18000000;
+ case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
+ return 24000000;
+ case HDMI_FRL_LINK_RATE_8GBPS:
+ return 32000000;
+ case HDMI_FRL_LINK_RATE_10GBPS:
+ return 40000000;
+ case HDMI_FRL_LINK_RATE_12GBPS:
+ return 48000000;
+ case HDMI_FRL_LINK_RATE_16GBPS:
+ return 64000000;
+ case HDMI_FRL_LINK_RATE_20GBPS:
+ return 80000000;
+ case HDMI_FRL_LINK_RATE_24GBPS:
+ return 96000000;
+ default:
+ return 0;
+ }
+}
+
+bool frl_capacity_computations_common(struct frl_cap_chk_params_fixed31_32 *params,
+ struct frl_cap_chk_intermediates_fixed31_32 *inter)
+{
+ struct fixed31_32 audio_bw_reserve = dc_fixpt_from_int((params->compressed ? 192000 : 0));
+ struct fixed31_32 pixel_rate_tolerance = dc_fixpt_div_int(dc_fixpt_from_int(5), 1000);
+ struct fixed31_32 max_audio_tol_rate;
+ struct fixed31_32 overhead_m;
+
+ inter->c_frl_sb = 4 * C_FRL_CB + params->lanes;
+ inter->overhead_sb = dc_fixpt_div_int(dc_fixpt_from_int(params->lanes), inter->c_frl_sb);
+ inter->overhead_rs = dc_fixpt_div_int(dc_fixpt_from_int(32), inter->c_frl_sb);
+ inter->overhead_map = dc_fixpt_div_int(dc_fixpt_from_int(25), (inter->c_frl_sb * 10));
+
+ inter->overhead_min = dc_fixpt_add(inter->overhead_sb, inter->overhead_rs);
+ inter->overhead_min = dc_fixpt_add(inter->overhead_min, inter->overhead_map);
+ overhead_m = dc_fixpt_div_int(dc_fixpt_from_int(3), 1000);
+ inter->overhead_max = dc_fixpt_add(inter->overhead_min, overhead_m);
+
+ pixel_rate_tolerance = dc_fixpt_add_int(pixel_rate_tolerance, 1);
+
+ inter->f_pixel_clock_max = dc_fixpt_mul(params->f_pixel_clock_nominal, pixel_rate_tolerance);
+ inter->t_line = dc_fixpt_div(dc_fixpt_from_int(params->h_active + params->h_blank), inter->f_pixel_clock_max);
+ inter->r_bit_min = dc_fixpt_div_int(dc_fixpt_from_int(TOLERANCE_FRL_BIT), 1000000);
+ inter->r_bit_min = dc_fixpt_sub(dc_fixpt_from_int(1), inter->r_bit_min);
+ inter->r_bit_min = dc_fixpt_mul(params->r_bit_nominal, inter->r_bit_min);
+
+ inter->r_frl_char_min = dc_fixpt_div_int(inter->r_bit_min, 18);
+ inter->c_frl_line = dc_fixpt_mul(inter->t_line, inter->r_frl_char_min);
+ inter->c_frl_line = dc_fixpt_mul_int(inter->c_frl_line, params->lanes);
+
+ switch (params->audio_packet_type) {
+ case 0x02:
+ if (params->layout == 0)
+ inter->ap = dc_fixpt_div_int(dc_fixpt_from_int(25), 100);
+ else if (params->layout == 1)
+ inter->ap = dc_fixpt_from_int(1);
+ break;
+ case 0x08:
+ inter->ap = dc_fixpt_div_int(dc_fixpt_from_int(25), 100);
+ break;
+ case 0x09:
+ inter->ap = dc_fixpt_from_int(1);
+ break;
+ case 0x07:
+ case 0x0e:
+ case 0x0f:
+ case 0x0b:
+ case 0x0c:
+ /* Unsupported audio format */
+ return false;
+ default:
+ inter->ap = dc_fixpt_from_int(0);
+ }
+
+ inter->r_ap = dc_fixpt_max(audio_bw_reserve, dc_fixpt_mul(params->f_audio, inter->ap));
+ inter->r_ap = dc_fixpt_add(inter->r_ap, dc_fixpt_from_int(2 * ACR_RATE_MAX));
+ max_audio_tol_rate = dc_fixpt_div_int(dc_fixpt_from_int(TOLERANCE_AUDIO_CLOCK), 1000000);
+ max_audio_tol_rate = dc_fixpt_add(dc_fixpt_from_int(1), max_audio_tol_rate);
+ inter->r_ap = dc_fixpt_mul(inter->r_ap, max_audio_tol_rate);
+
+ inter->avg_audio_packets_line = dc_fixpt_mul(inter->r_ap, inter->t_line);
+ inter->avg_audio_packets_line = dc_fixpt_div_int(inter->avg_audio_packets_line, 1000000);
+ inter->audio_packets_line = dc_fixpt_ceil(inter->avg_audio_packets_line);
+
+ inter->blank_audio_min = 32 + 32 * inter->audio_packets_line;
+
+ params->borrow_params.audio_packets_line = inter->audio_packets_line;
+
+ return true;
+}
+
+bool frl_capacity_computations_uncompressed_video(struct frl_cap_chk_params_fixed31_32 *params,
+ struct frl_cap_chk_intermediates_fixed31_32 *inter)
+{
+ bool res;
+ int k_420;
+ struct fixed31_32 k_cd;
+ struct fixed31_32 c_frl_free;
+ int c_frl_free_int;
+ int c_frl_rc_margin;
+ struct fixed31_32 c_frl_rc_savings;
+ int c_frl_rc_savings_int;
+ int bpp;
+ struct fixed31_32 bytes_line;
+ int tb_active;
+ int tb_blank;
+ struct fixed31_32 f_tb_average;
+ struct fixed31_32 t_active_ref;
+ struct fixed31_32 t_blank_ref;
+ struct fixed31_32 t_active_min;
+ struct fixed31_32 t_blank_min;
+ int c_frl_actual_payload;
+ struct fixed31_32 utilization;
+
+ res = frl_capacity_computations_common(params, inter);
+ if (res != true)
+ return res;
+
+ k_420 = params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_420 ? 2 : 1;
+ if (params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_422)
+ k_cd = dc_fixpt_from_int(1);
+ else
+ k_cd = dc_fixpt_div_int(dc_fixpt_from_int(params->bpc), 8);
+
+ c_frl_free = dc_fixpt_div_int(dc_fixpt_mul_int(k_cd, params->h_blank), k_420);
+ c_frl_free = dc_fixpt_sub_int(c_frl_free, 32 * (1 + inter->audio_packets_line) + 7);
+ c_frl_free = dc_fixpt_max(c_frl_free, dc_fixpt_from_int(0));
+ c_frl_free_int = dc_fixpt_ceil(c_frl_free);
+ c_frl_rc_margin = 4;
+ c_frl_rc_savings = dc_fixpt_mul_int(dc_fixpt_div_int(dc_fixpt_from_int(7), 8), c_frl_free_int);
+ c_frl_rc_savings = dc_fixpt_sub_int(c_frl_rc_savings, c_frl_rc_margin);
+ c_frl_rc_savings_int = dc_fixpt_floor(dc_fixpt_max(c_frl_rc_savings, dc_fixpt_from_int(0)));
+
+ bpp = dc_fixpt_ceil(dc_fixpt_mul_int(dc_fixpt_div_int(k_cd, k_420), 24));
+ bytes_line = dc_fixpt_div_int(dc_fixpt_from_int(bpp * params->h_active), 8);
+ tb_active = dc_fixpt_ceil(dc_fixpt_div_int(bytes_line, 3));
+ tb_blank = dc_fixpt_ceil(dc_fixpt_div_int(dc_fixpt_mul_int(k_cd, params->h_blank), k_420));
+
+ if (!(inter->blank_audio_min <= tb_blank)) {
+ return false;
+ }
+
+ f_tb_average = dc_fixpt_div_int(inter->f_pixel_clock_max, (params->h_active + params->h_blank));
+ f_tb_average = dc_fixpt_mul_int(f_tb_average, (tb_active + tb_blank));
+
+ t_active_ref = dc_fixpt_div(dc_fixpt_from_int(params->h_active), dc_fixpt_from_int(params->h_active + params->h_blank));
+ t_active_ref = dc_fixpt_mul(inter->t_line, t_active_ref);
+
+ t_blank_ref = dc_fixpt_div(dc_fixpt_from_int(params->h_blank), dc_fixpt_from_int(params->h_active + params->h_blank));
+ t_blank_ref = dc_fixpt_mul(inter->t_line, t_blank_ref);
+
+ t_active_min = dc_fixpt_sub(dc_fixpt_from_int(1), inter->overhead_max);
+ t_active_min = dc_fixpt_mul(t_active_min, inter->r_frl_char_min);
+ t_active_min = dc_fixpt_mul_int(t_active_min, params->lanes);
+ t_active_min = dc_fixpt_div_int(t_active_min, 1000);
+ t_blank_min = t_active_min;
+
+ t_active_min = dc_fixpt_div(dc_fixpt_mul_int(dc_fixpt_div(dc_fixpt_from_int(3), dc_fixpt_from_int(2)), tb_active), t_active_min);
+
+ t_blank_min = dc_fixpt_div(dc_fixpt_from_int(tb_blank), t_blank_min);
+
+ if (dc_fixpt_le(t_active_min, t_active_ref) && dc_fixpt_le(t_blank_min, t_blank_ref)) {
+ params->borrow_params.borrow_mode = FRL_BORROW_MODE_NONE;
+ } else if (dc_fixpt_lt(t_active_ref, t_active_min) && dc_fixpt_le(t_blank_min, t_blank_ref)) {
+ params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_BLANK;
+ } else {
+ return false;
+ }
+
+
+ c_frl_actual_payload = dc_fixpt_ceil(dc_fixpt_mul_int(dc_fixpt_div(dc_fixpt_from_int(3), dc_fixpt_from_int(2)), tb_active)) + tb_blank - c_frl_rc_savings_int;
+
+ utilization = dc_fixpt_div(dc_fixpt_from_int(c_frl_actual_payload), inter->c_frl_line);
+ utilization = dc_fixpt_mul_int(utilization, 1000);
+
+ inter->margin = dc_fixpt_sub(dc_fixpt_from_int(1), dc_fixpt_add(utilization, inter->overhead_max));
+
+ if (dc_fixpt_lt(inter->margin, dc_fixpt_from_int(0)) && dc_fixpt_lt(dc_fixpt_from_fraction(1, 100), dc_fixpt_abs(inter->margin)))
+ return false;
+
+ return true;
+}
+
static uint32_t dp_get_timing_bandwidth_kbps(
const struct dc_crtc_timing *timing,
const struct dc_link *link)
@@ -302,9 +492,11 @@ static bool dp_validate_mode_timing(
req_bw = dc_bandwidth_in_kbps_from_timing(timing, dc_link_get_highest_encoding_format(link));
max_bw = dp_link_bandwidth_kbps(link, link_setting);
+ uint32_t max_uncompressed_pixel_rate_100hz =
+ link->dpcd_caps.max_uncompressed_pixel_rate_cap.bits.max_uncompressed_pixel_rate_cap * 10000U;
bool is_max_uncompressed_pixel_rate_exceeded = link->dpcd_caps.max_uncompressed_pixel_rate_cap.bits.valid &&
- timing->pix_clk_100hz > link->dpcd_caps.max_uncompressed_pixel_rate_cap.bits.max_uncompressed_pixel_rate_cap * 10000;
+ timing->pix_clk_100hz > max_uncompressed_pixel_rate_100hz;
if (is_max_uncompressed_pixel_rate_exceeded && !timing->flags.DSC) {
return false;
@@ -329,6 +521,86 @@ static bool dp_validate_mode_timing(
return false;
}
+bool frl_validate_mode_timing(
+ struct dc_link *link,
+ const struct dc_crtc_timing *timing,
+ struct dc_hdmi_frl_link_settings *frl_link_settings)
+{
+ uint32_t req_bw;
+ uint32_t max_bw;
+ enum engine_id hpo_eng_id;
+ unsigned int i;
+ unsigned int hpo_frl_stream_enc_index = 0;
+ bool frl_output_valid = false;
+
+ if (!link)
+ return false;
+ if (!link->local_sink)
+ return false;
+
+ req_bw = dc_bandwidth_in_kbps_from_timing(timing, dc_link_get_highest_encoding_format(link));
+ max_bw = frl_link_bandwidth_kbps(frl_link_settings->frl_link_rate);
+
+ /* Use Engine ID to determine which hpo stream
+ * encoder should be used.
+ */
+ if (link->connector_signal == SIGNAL_TYPE_VIRTUAL)
+ frl_output_valid = true;
+ else {
+ struct audio_check audio_frl_check = {0};
+ struct audio_info audio_info = {0};
+ hpo_eng_id = ENGINE_ID_HPO_0;
+
+ for (i = 0; i < link->dc->res_pool->hpo_frl_stream_enc_count; i++) {
+ if (link->dc->res_pool->hpo_frl_stream_enc[i]->id == hpo_eng_id) {
+ hpo_frl_stream_enc_index = i;
+ break;
+ }
+ }
+ /*add audio check*/
+ for (i = 0; i < (link->local_sink->edid_caps.audio_mode_count); i++) {
+ audio_info.modes[i].channel_count = link->local_sink->edid_caps.audio_modes[i].channel_count;
+ audio_info.modes[i].format_code = link->local_sink->edid_caps.audio_modes[i].format_code;
+ audio_info.modes[i].sample_rates.all = link->local_sink->edid_caps.audio_modes[i].sample_rate;
+ audio_info.modes[i].sample_size = link->local_sink->edid_caps.audio_modes[i].sample_size;
+ }
+ audio_info.mode_count = link->local_sink->edid_caps.audio_mode_count;
+ get_audio_check(&audio_info, &audio_frl_check);
+
+ frl_output_valid =
+ link->dc->res_pool->hpo_frl_stream_enc[hpo_frl_stream_enc_index]->funcs->validate_hdmi_frl_output(
+ link->dc->res_pool->hpo_frl_stream_enc[hpo_frl_stream_enc_index],
+ timing, &audio_frl_check,
+ frl_link_settings,
+ link->local_sink->edid_caps.frl_dsc_max_frl_rate);
+ }
+
+ if (req_bw <= max_bw && frl_output_valid) {
+ /* remember the biggest mode here, during
+ * initial link training (to get
+ * verified_link_cap), LS sends event about
+ * cannot train at reported cap to upper
+ * layer and upper layer will re-enumerate modes.
+ * this is not necessary if the lower
+ * verified_link_cap is enough to drive
+ * all the modes
+ */
+
+ /* TODO: DYNAMIC_VALIDATION needs to be implemented */
+ /* if (flags.DYNAMIC_VALIDATION == 1)
+ * dpsst->max_req_bw_for_verified_linkcap = dal_max(
+ * dpsst->max_req_bw_for_verified_linkcap, req_bw);
+ */
+ return true;
+ } else if (frl_output_valid && timing->dsc_cfg.is_frl) {
+ /* HDMI DSC calculation is validated within frl_output_valid
+ * and req_bw may exceed max_bw
+ */
+ return true;
+ } else
+ return false;
+}
+
enum dc_status link_validate_mode_timing(
const struct dc_stream_state *stream,
struct dc_link *link,
@@ -343,6 +615,15 @@ enum dc_status link_validate_mode_timing(
if (link->remote_sinks[0] && link->remote_sinks[0]->sink_signal == SIGNAL_TYPE_VIRTUAL)
return DC_OK;
+ /* If DSC is supported, but native 422 DSC is not supported,
+ * HDMI 2.1a specification requires that all 422 format be disabled (7.7.1)
+ */
+ if (dc_is_hdmi_signal(stream->signal)) {
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 && link->dc->config.no_native422_support) {
+ return DC_SURFACE_PIXEL_FORMAT_UNSUPPORTED;
+ }
+ }
+
/* Passive Dongle */
if (max_pix_clk != 0 && get_tmds_output_pixel_clock_100hz(timing) > max_pix_clk)
return DC_EXCEED_DONGLE_CAP;
@@ -360,6 +641,25 @@ enum dc_status link_validate_mode_timing(
return DC_NO_DP_LINK_BANDWIDTH;
break;
+ case SIGNAL_TYPE_HDMI_FRL:
+ {
+ uint32_t pxl_clk_mhz;
+ /* Limit pixel clock to DTBCLK Limit (Base Pix > 4 * DTBCLK) */
+ pxl_clk_mhz = (timing->pix_clk_100hz + 10000 - 1) / 10000 ;
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
+ pxl_clk_mhz /= 2;
+ else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422)
+ pxl_clk_mhz = pxl_clk_mhz * 2 / 3;
+ if (pxl_clk_mhz > DTBCLK_LIMIT && link->ctx->dce_version < DCN_VERSION_3_1)
+ return DC_NO_HDMI_FRL_LINK_BANDWIDTH;
+ }
+
+ if (!frl_validate_mode_timing(
+ link,
+ timing,
+ hdmi_frl_get_verified_link_cap(link)))
+ return DC_NO_HDMI_FRL_LINK_BANDWIDTH;
+ break;
default:
break;
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.h b/drivers/gpu/drm/amd/display/dc/link/link_validation.h
index 595774e76453..ccea329c556c 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_validation.h
+++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.h
@@ -26,6 +26,8 @@
#define __LINK_VALIDATION_H__
#include "link_service.h"
+#define TOLERANCE_AUDIO_CLOCK 1000
+
enum dc_status link_validate_mode_timing(
const struct dc_stream_state *stream,
struct dc_link *link,
@@ -33,9 +35,20 @@ enum dc_status link_validate_mode_timing(
enum dc_status link_validate_dp_tunnel_bandwidth(
const struct dc *dc,
const struct dc_state *new_ctx);
+bool frl_validate_mode_timing(
+ struct dc_link *link,
+ const struct dc_crtc_timing *timing,
+ struct dc_hdmi_frl_link_settings *frl_link_settings);
uint32_t dp_link_bandwidth_kbps(
const struct dc_link *link,
const struct dc_link_settings *link_settings);
+uint32_t frl_link_bandwidth_kbps(enum hdmi_frl_link_rate link_rate);
+uint32_t link_timing_bandwidth_kbps(const struct dc_crtc_timing *timing);
+bool frl_capacity_computations_common(struct frl_cap_chk_params_fixed31_32 *params,
+ struct frl_cap_chk_intermediates_fixed31_32 *inter);
+bool frl_capacity_computations_uncompressed_video(
+ struct frl_cap_chk_params_fixed31_32 *params,
+ struct frl_cap_chk_intermediates_fixed31_32 *inter);
uint32_t dp_required_hblank_size_bytes(
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
index a66217e54a09..6123b10977bf 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
@@ -31,6 +31,7 @@
* link training.
*/
#include "link_ddc.h"
+#include "link_hdmi_frl.h"
#include "vector.h"
#include "dce/dce_aux.h"
#include "dal_asic_id.h"
@@ -96,7 +97,7 @@ static void i2c_payloads_add(
for (pos = 0; pos < len; pos += payload_size) {
struct i2c_payload payload = {
.write = write,
- .address = address,
+ .address = (uint8_t)address,
.length = DDC_MIN(payload_size, len - pos),
.data = data + pos };
dal_vector_append(&payloads->payloads, &payload);
@@ -119,25 +120,31 @@ static void ddc_service_construct(
ddc_service->link = init_data->link;
ddc_service->ctx = init_data->ctx;
- if (init_data->is_dpia_link ||
- dcb->funcs->get_i2c_info(dcb, init_data->id, &i2c_info) != BP_RESULT_OK) {
- ddc_service->ddc_pin = NULL;
- } else {
- DC_LOGGER_INIT(ddc_service->ctx->logger);
- DC_LOG_DC("BIOS object table - i2c_line: %d", i2c_info.i2c_line);
- DC_LOG_DC("BIOS object table - i2c_engine_id: %d", i2c_info.i2c_engine_id);
-
- hw_info.ddc_channel = i2c_info.i2c_line;
- if (ddc_service->link != NULL)
- hw_info.hw_supported = i2c_info.i2c_hw_assist;
- else
- hw_info.hw_supported = false;
-
- ddc_service->ddc_pin = dal_gpio_create_ddc(
- gpio_service,
- i2c_info.gpio_info.clk_a_register_index,
- 1 << i2c_info.gpio_info.clk_a_shift,
- &hw_info);
+ if (ddc_service->link && ddc_service->link->force_to_use_aux) {
+ // Obtain aux instance info from i2c_info without GPIO DDC pin info
+ if (dcb->funcs->get_connector_aux_info(dcb, init_data->id, &i2c_info) == BP_RESULT_OK)
+ ddc_service->link->aux_hw_inst = (uint8_t)i2c_info.i2c_line;
+ } else {
+ if (init_data->is_dpia_link ||
+ dcb->funcs->get_i2c_info(dcb, init_data->id, &i2c_info) != BP_RESULT_OK) {
+ ddc_service->ddc_pin = NULL;
+ } else {
+ DC_LOGGER_INIT(ddc_service->ctx->logger);
+ DC_LOG_DC("BIOS object table - i2c_line: %d", i2c_info.i2c_line);
+ DC_LOG_DC("BIOS object table - i2c_engine_id: %d", i2c_info.i2c_engine_id);
+
+ hw_info.ddc_channel = i2c_info.i2c_line;
+ if (ddc_service->link != NULL)
+ hw_info.hw_supported = i2c_info.i2c_hw_assist;
+ else
+ hw_info.hw_supported = false;
+
+ ddc_service->ddc_pin = dal_gpio_create_ddc(
+ gpio_service,
+ i2c_info.gpio_info.clk_a_register_index,
+ 1 << i2c_info.gpio_info.clk_a_shift,
+ &hw_info);
+ }
}
ddc_service->flags.EDID_QUERY_DONE_ONCE = false;
@@ -244,6 +251,21 @@ static uint32_t defer_delay_converter_wa(
#define DP_TRANSLATOR_DELAY 5
+/**
+ * link_get_ddc_aux_inst - Return the AUX/DDC hardware instance for a link.
+ * @link: the link to query
+ *
+ * Return: aux_hw_inst when I2C is forced over AUX, otherwise the DDC pin
+ * channel index.
+ */
+uint8_t link_get_ddc_aux_inst(const struct dc_link *link)
+{
+ if (link->force_to_use_aux)
+ return link->aux_hw_inst;
+ ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
+ return (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
+}
+
uint32_t link_get_aux_defer_delay(struct ddc_service *ddc)
{
uint32_t defer_delay = 0;
@@ -384,8 +406,7 @@ bool link_query_ddc_data(
i2c_payloads_add(
&payloads, address, read_size, read_buf, false);
- command.number_of_payloads =
- i2c_payloads_get_count(&payloads);
+ command.number_of_payloads = (uint8_t)i2c_payloads_get_count(&payloads);
success = dm_helpers_submit_i2c(
ddc->ctx,
@@ -402,12 +423,15 @@ int link_aux_transfer_raw(struct ddc_service *ddc,
struct aux_payload *payload,
enum aux_return_code_type *operation_result)
{
- if (ddc->ctx->dc->debug.enable_dmub_aux_for_legacy_ddc ||
- !ddc->ddc_pin) {
- return dce_aux_transfer_dmub_raw(ddc, payload, operation_result);
- } else {
- return dce_aux_transfer_raw(ddc, payload, operation_result);
+ if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) {
+ /* Check whether aux to be processed via dmub or dcn directly */
+ if (ddc->ctx->dc->debug.enable_dmub_aux_for_legacy_ddc) {
+ return dce_aux_transfer_dmub_raw(ddc, payload, operation_result);
+ } else {
+ return dce_aux_transfer_raw_without_ddc_pin(ddc, payload, operation_result);
+ }
}
+ return dce_aux_transfer_raw(ddc, payload, operation_result);
}
uint32_t link_get_fixed_vs_pe_retimer_write_address(struct dc_link *link)
@@ -524,11 +548,18 @@ bool try_to_configure_aux_timeout(struct ddc_service *ddc,
if (ddc->link->ep_type != DISPLAY_ENDPOINT_PHY)
return true;
- if (ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout) {
- ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout(ddc, timeout);
- result = true;
- }
+ if (ddc->link->force_to_use_aux) {
+ if (ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]->funcs->configure_timeout) {
+ ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]->funcs->configure_timeout(ddc, timeout);
+ result = true;
+ }
+ } else {
+ if (ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout) {
+ ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout(ddc, timeout);
+ result = true;
+ }
+ }
return result;
}
@@ -552,6 +583,8 @@ void write_scdc_data(struct ddc_service *ddc_service,
(ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite ||
!ddc_service->link->local_sink->edid_caps.scdc_present))
return;
+ hdmi_frl_LTS_clear_Link_Setting(ddc_service);
+ hdmi_frl_LTS_clear_Update_flag(ddc_service);
link_query_ddc_data(ddc_service, slave_address, &offset,
sizeof(offset), &sink_version, sizeof(sink_version));
@@ -602,3 +635,90 @@ void read_scdc_data(struct ddc_service *ddc_service)
sizeof(status_data.byte));
}
}
+void write_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope,
+ uint8_t *write_buf, uint8_t offset, uint8_t write_len)
+{
+ uint8_t slave_address = HDMI_IDCC_ADDRESS;
+ uint8_t idcc_header[5] = {0};
+ uint8_t dummy_buf[1] = {0};
+ uint8_t checksum = 0;
+ int i;
+
+ idcc_header[0] = HDMI_IDCC_MARKER0;
+ idcc_header[1] = HDMI_IDCC_MARKER1;
+ idcc_header[2] = (uint8_t)(HDMI_IDCC_MARKER2 | idcc_scope);
+ idcc_header[3] = offset;
+ idcc_header[4] = write_len;
+
+ /* Write the IDCC header */
+ for (i = 0; i < sizeof(idcc_header); ++i) {
+ link_query_ddc_data(ddc_service, slave_address,
+ &idcc_header[i], 1,
+ &dummy_buf[0], 1);
+ checksum += idcc_header[i];
+ }
+
+ /* Write the payload */
+ for (i = 0; i < write_len; ++i) {
+ link_query_ddc_data(ddc_service, slave_address,
+ &write_buf[i], 1,
+ &dummy_buf[0], 1);
+ checksum += write_buf[i];
+ }
+
+ /* Write the checksum */
+ if (write_len > 0) {
+ checksum = 0xff - checksum + 1;
+ link_query_ddc_data(ddc_service, slave_address,
+ &checksum, 1,
+ &dummy_buf[0], 1);
+ }
+}
+
+int read_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope,
+ uint8_t *read_buf, uint8_t offset, uint8_t read_len)
+{
+ uint8_t slave_address = HDMI_IDCC_ADDRESS;
+ uint8_t idcc_header[5] = {0};
+ uint8_t dummy_buf[1] = {0};
+ uint8_t read_buf_local[6] = {0};
+ uint8_t checksum = 0;
+ int i;
+
+ idcc_header[0] = HDMI_IDCC_MARKER0;
+ idcc_header[1] = HDMI_IDCC_MARKER1;
+ idcc_header[2] = (uint8_t)(HDMI_IDCC_MARKER2 | idcc_scope);
+ idcc_header[3] = offset;
+ idcc_header[4] = read_len;
+
+ /* Write the IDCC header */
+ for (i = 0; i < sizeof(idcc_header); ++i) {
+ link_query_ddc_data(ddc_service, slave_address,
+ &idcc_header[i], 1,
+ &dummy_buf[0], 1);
+ checksum += idcc_header[i];
+ }
+
+ /* Read the payload */
+ if (read_len > 0) {
+ dummy_buf[0] = 0x01;
+ if (read_len > 5)
+ read_len = 5;
+ link_query_ddc_data(ddc_service, slave_address,
+ &dummy_buf[0], 1,
+ &read_buf_local[0], read_len + 1);
+
+ memcpy(read_buf, read_buf_local, read_len);
+
+ /* Check checksum */
+ checksum = read_buf_local[read_len];
+ for (i = 0; i < 5; ++i)
+ checksum += idcc_header[i];
+ for (i = 0; i < read_len; ++i)
+ checksum += read_buf_local[i];
+ if (checksum != 0)
+ return -1;
+ }
+
+ return read_len;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h
index d3e6f01a6a90..fdd8a3dce97f 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h
@@ -46,6 +46,8 @@ void set_ddc_transaction_type(
struct ddc_service *ddc,
enum ddc_transaction_type type);
+uint8_t link_get_ddc_aux_inst(const struct dc_link *link);
+
uint32_t link_get_aux_defer_delay(struct ddc_service *ddc);
bool link_is_in_aux_transaction_mode(struct ddc_service *ddc);
@@ -94,6 +96,10 @@ void write_scdc_data(
void read_scdc_data(
struct ddc_service *ddc_service);
+void write_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope,
+ uint8_t *write_buf, uint8_t offset, uint8_t write_len);
+int read_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope,
+ uint8_t *read_buf, uint8_t offset, uint8_t read_len);
void set_dongle_type(struct ddc_service *ddc,
enum display_dongle_type dongle_type);
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
index 782a45caa13d..24e3bbec15ff 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
@@ -181,6 +181,12 @@ uint32_t link_bw_kbps_from_raw_frl_link_rate_data(uint8_t bw)
return 40000000;
case 0b110:
return 48000000;
+ case 0b111:
+ return 64000000;
+ case 0b1000:
+ return 80000000;
+ case 0b1001:
+ return 96000000;
}
return 0;
@@ -750,8 +756,10 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting
if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap))
return false;
- if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN)
- initial_link_setting.link_rate = link->preferred_link_setting.link_rate;
+ if (link->wa_flags.dp_skip_rbr) {
+ initial_link_setting.link_rate = LINK_RATE_HIGH;
+ current_link_setting.link_rate = LINK_RATE_HIGH;
+ }
/* search for the minimum link setting that:
* 1. is supported according to the link training result
@@ -1353,7 +1361,7 @@ bool dp_overwrite_extended_receiver_cap(struct dc_link *link)
union down_stream_port_count down_strm_port_count;
union edp_configuration_cap edp_config_cap;
- int i;
+ unsigned int i;
for (i = 0; i < read_dpcd_retry_cnt; i++) {
status = core_link_read_dpcd(
@@ -1713,7 +1721,7 @@ enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link)
CONN_DATA_DETECT(link, lttpr_dpcd_data, sizeof(lttpr_dpcd_data), "LTTPR Caps: ");
// Identify closest LTTPR to determine if workarounds required for known embedded LTTPR
- closest_lttpr_offset = dp_get_closest_lttpr_offset(lttpr_count);
+ closest_lttpr_offset = dp_get_closest_lttpr_offset((uint8_t)lttpr_count);
core_link_read_dpcd(link, (DP_LTTPR_IEEE_OUI + closest_lttpr_offset),
link->dpcd_caps.lttpr_caps.lttpr_ieee_oui, sizeof(link->dpcd_caps.lttpr_caps.lttpr_ieee_oui));
@@ -1752,7 +1760,7 @@ static bool retrieve_link_cap(struct dc_link *link)
union dp_downstream_port_present ds_port = { 0 };
enum dc_status status = DC_ERROR_UNEXPECTED;
uint32_t read_dpcd_retry_cnt = 20;
- int i;
+ unsigned int i;
struct dp_sink_hw_fw_revision dp_hw_fw_revision;
const uint32_t post_oui_delay = 30; // 30ms
bool is_fec_supported = false;
@@ -2242,10 +2250,14 @@ void detect_edp_sink_caps(struct dc_link *link)
/*
* ALPM is only valid for eDP v1.4 or higher.
*/
- if (link->dpcd_caps.dpcd_rev.raw >= DP_EDP_14)
+ if (link->dpcd_caps.dpcd_rev.raw >= DP_EDP_14) {
core_link_read_dpcd(link, DP_RECEIVER_ALPM_CAP,
&link->dpcd_caps.alpm_caps.raw,
sizeof(link->dpcd_caps.alpm_caps.raw));
+ core_link_read_dpcd(link, DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_CAP,
+ &link->dpcd_caps.psr_info.psr_active_vtotal_control_cap,
+ sizeof(link->dpcd_caps.psr_info.psr_active_vtotal_control_cap));
+ }
/*
* Read REPLAY info
@@ -2570,6 +2582,12 @@ bool dp_is_sink_present(struct dc_link *link)
(connector_id == CONNECTOR_ID_EDP) ||
(connector_id == CONNECTOR_ID_USBC));
+ /* We can't perform the step below for ASICs with no Native
+ * I2C signaling support on DP connectors, so skip it.
+ */
+ if (link->force_to_use_aux)
+ return present;
+
ddc = get_ddc_pin(link->ddc);
if (!ddc) {
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c
index 766b54631c79..da227889e007 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c
@@ -119,7 +119,7 @@ bool dpia_query_hpd_status(struct dc_link *link)
/* prepare QUERY_HPD command */
cmd.query_hpd.header.type = DMUB_CMD__QUERY_HPD_STATE;
cmd.query_hpd.header.payload_bytes = sizeof(cmd.query_hpd.data);
- cmd.query_hpd.data.instance = link->link_id.enum_id - ENUM_ID_1;
+ cmd.query_hpd.data.instance = (uint8_t)(link->link_id.enum_id - ENUM_ID_1);
cmd.query_hpd.data.ch_type = AUX_CHANNEL_DPIA;
/* Query dpia hpd status from dmub */
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
index c958d3f600c8..f43fc4b78a8d 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
@@ -103,6 +103,11 @@ static int get_estimated_bw(struct dc_link *link)
{
uint8_t bw_estimated_bw = 0;
+ if (link->dpia_bw_alloc_config.bw_granularity == 0) {
+ DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__);
+ return 0;
+ }
+
core_link_read_dpcd(
link,
ESTIMATED_BW,
@@ -112,7 +117,7 @@ static int get_estimated_bw(struct dc_link *link)
return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
}
-static int get_non_reduced_max_link_rate(struct dc_link *link)
+static uint8_t get_non_reduced_max_link_rate(struct dc_link *link)
{
uint8_t nrd_max_link_rate = 0;
@@ -125,7 +130,7 @@ static int get_non_reduced_max_link_rate(struct dc_link *link)
return nrd_max_link_rate;
}
-static int get_non_reduced_max_lane_count(struct dc_link *link)
+static uint8_t get_non_reduced_max_lane_count(struct dc_link *link)
{
uint8_t nrd_max_lane_count = 0;
@@ -180,7 +185,7 @@ static void dpia_bw_alloc_unplug(struct dc_link *link)
static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw)
{
- uint8_t request_reg_val;
+ uint32_t request_reg_val;
uint32_t temp, request_bw;
if (link->dpia_bw_alloc_config.bw_granularity == 0) {
@@ -197,7 +202,7 @@ static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw)
request_bw = request_reg_val * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
- if (request_bw > link->dpia_bw_alloc_config.estimated_bw) {
+ if (request_bw > (uint32_t)link->dpia_bw_alloc_config.estimated_bw) {
DC_LOG_ERROR("%s: Link[%d]: Request BW (%d --> %d) > Estimated BW (%d)... Set to Estimated BW!",
__func__, link->link_index,
req_bw, request_bw, link->dpia_bw_alloc_config.estimated_bw);
@@ -212,8 +217,8 @@ static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw)
link->dpia_bw_alloc_config.allocated_bw = request_bw;
DC_LOG_DC("%s: Link[%d]: Request BW: %d", __func__, link->link_index, request_bw);
- core_link_write_dpcd(link, REQUESTED_BW,
- &request_reg_val,
+ uint8_t requested_bw_dpcd = (uint8_t)request_reg_val;
+ core_link_write_dpcd(link, REQUESTED_BW, &requested_bw_dpcd,
sizeof(uint8_t));
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
index 1860d44f63c1..da679fb7d89c 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
@@ -39,7 +39,6 @@
#include "link/link_dpms.h"
#include "dm_helpers.h"
#include "link_dp_dpia_bw.h"
-#include "link_dp_panel_replay.h"
#define DC_LOGGER \
link->ctx->logger
@@ -65,7 +64,7 @@ bool dp_parse_link_loss_status(
/*1. Check that Link Status changed, before re-training.*/
/*parse lane status*/
- for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) {
+ for (lane = 0; lane < (uint32_t)link->cur_link_settings.lane_count; lane++) {
/* check status of lanes 0,1
* changed DpcdAddress_Lane01Status (0x202)
*/
@@ -223,7 +222,7 @@ static void handle_hpd_irq_vesa_replay_sink(struct dc_link *link)
}
}
-static void handle_hpd_irq_replay_sink(struct dc_link *link, bool *need_re_enable)
+static void handle_hpd_irq_replay_sink(struct dc_link *link, bool *need_re_enable, bool *replay_esd_detection_needed)
{
union dpcd_replay_configuration replay_configuration = {0};
union dpcd_replay_configuration replay_sink_status = {0};
@@ -311,6 +310,16 @@ static void handle_hpd_irq_replay_sink(struct dc_link *link, bool *need_re_enabl
*need_re_enable = true;
}
}
+
+ if (link->ctx->dc->debug.enable_replay_esd_recovery) {
+ if (!link->replay_settings.replay_allow_active &&
+ replay_sink_status.bits.SINK_DEVICE_REPLAY_STATUS == 0x7) {
+ /* If sink device replay status is 0x7 and replay is disabled,
+ * it means sink is in a bad state and link retraining is needed to recover
+ */
+ *replay_esd_detection_needed = true;
+ }
+ }
}
void dp_handle_link_loss(struct dc_link *link)
@@ -469,6 +478,7 @@ bool dp_handle_hpd_rx_irq(struct dc_link *link,
enum dc_status result;
bool status = false;
bool replay_re_enable_needed = false;
+ bool replay_esd_detection_needed = false;
if (out_link_loss)
*out_link_loss = false;
@@ -482,6 +492,7 @@ bool dp_handle_hpd_rx_irq(struct dc_link *link,
DC_LOG_HW_HPD_IRQ("%s: Got short pulse HPD on link %d\n",
__func__, link->link_index);
+ handle_hpd_irq_replay_sink(link, &replay_re_enable_needed, &replay_esd_detection_needed);
/* All the "handle_hpd_irq_xxx()" methods
* should be called only after
@@ -528,8 +539,6 @@ bool dp_handle_hpd_rx_irq(struct dc_link *link,
/* PSR-related error was detected and handled */
return true;
- handle_hpd_irq_replay_sink(link, &replay_re_enable_needed);
-
/* If PSR-related error handled, Main link may be off,
* so do not handle as a normal sink status change interrupt.
*/
@@ -552,27 +561,30 @@ bool dp_handle_hpd_rx_irq(struct dc_link *link,
* Downstream port status changed,
* then DM should call DC to do the detection.
* NOTE: Now includes eDP link loss detection and retraining
+ * Link will be retrained if panel is not EDP or
+ * Replay ESD recovery is needed.
*/
-
- if (dp_parse_link_loss_status(
- link,
- &hpd_irq_dpcd_data)) {
- /* Connectivity log: link loss */
- CONN_DATA_LINK_LOSS(link,
- hpd_irq_dpcd_data.raw,
- sizeof(hpd_irq_dpcd_data),
- "Status: ");
-
- if (defer_handling && has_left_work)
- *has_left_work = true;
- else
- dp_handle_link_loss(link);
-
- status = false;
- if (out_link_loss)
- *out_link_loss = true;
-
- dp_trace_link_loss_increment(link);
+ if (link->connector_signal != SIGNAL_TYPE_EDP || replay_esd_detection_needed) {
+ if (dp_parse_link_loss_status(
+ link,
+ &hpd_irq_dpcd_data)) {
+ /* Connectivity log: link loss */
+ CONN_DATA_LINK_LOSS(link,
+ hpd_irq_dpcd_data.raw,
+ sizeof(hpd_irq_dpcd_data),
+ "Status: ");
+
+ if (defer_handling && has_left_work)
+ *has_left_work = true;
+ else
+ dp_handle_link_loss(link);
+
+ status = false;
+ if (out_link_loss)
+ *out_link_loss = true;
+
+ dp_trace_link_loss_increment(link);
+ }
}
if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling) {
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c
index 96afce4ffbfa..0d4f88ff844d 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c
@@ -25,6 +25,7 @@
#include "link_dp_panel_replay.h"
#include "link_edp_panel_control.h"
+#include "link_ddc.h"
#include "link_dpcd.h"
#include "dm_helpers.h"
#include "dc/dc_dmub_srv.h"
@@ -60,11 +61,10 @@ static void dp_pr_set_static_screen_param(struct dc_link *link)
if (dc->current_state->res_ctx.pipe_ctx[i].stream &&
dc->current_state->res_ctx.pipe_ctx[i].stream->link == link) {
struct dc_stream_state *stream = dc->current_state->res_ctx.pipe_ctx[i].stream;
- unsigned int vsync_rate_hz = div64_u64(div64_u64(
- (stream->timing.pix_clk_100hz * (u64)100),
- stream->timing.v_total),
- stream->timing.h_total);
-
+ unsigned int vsync_rate_hz = (unsigned int)div64_u64(div64_u64(
+ (stream->timing.pix_clk_100hz * (u64)100),
+ stream->timing.v_total),
+ stream->timing.h_total);
params.triggers.cursor_update = true;
params.triggers.overlay_update = true;
params.triggers.surface_update = true;
@@ -120,7 +120,7 @@ static bool dp_setup_panel_replay(struct dc_link *link, const struct dc_stream_s
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
- replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel;
+ replay_context.aux_inst = (enum channel_id) link_get_ddc_aux_inst(link);
replay_context.digbe_inst = link->link_enc->transmitter;
replay_context.digfe_inst = link->link_enc->preferred_engine;
@@ -264,7 +264,7 @@ bool dp_pr_enable(struct dc_link *link, bool enable)
cmd.pr_enable.header.type = DMUB_CMD__PR;
cmd.pr_enable.header.sub_type = DMUB_CMD__PR_ENABLE;
cmd.pr_enable.header.payload_bytes = sizeof(struct dmub_cmd_pr_enable_data);
- cmd.pr_enable.data.panel_inst = panel_inst;
+ cmd.pr_enable.data.panel_inst = (uint8_t)panel_inst;
cmd.pr_enable.data.enable = enable ? 1 : 0;
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
@@ -301,17 +301,17 @@ bool dp_pr_copy_settings(struct dc_link *link, struct replay_context *replay_con
cmd.pr_copy_settings.header.type = DMUB_CMD__PR;
cmd.pr_copy_settings.header.sub_type = DMUB_CMD__PR_COPY_SETTINGS;
cmd.pr_copy_settings.header.payload_bytes = sizeof(struct dmub_cmd_pr_copy_settings_data);
- cmd.pr_copy_settings.data.panel_inst = panel_inst;
+ cmd.pr_copy_settings.data.panel_inst = (uint8_t)panel_inst;
// HW inst
cmd.pr_copy_settings.data.aux_inst = replay_context->aux_inst;
cmd.pr_copy_settings.data.digbe_inst = replay_context->digbe_inst;
cmd.pr_copy_settings.data.digfe_inst = replay_context->digfe_inst;
if (pipe_ctx->plane_res.dpp)
- cmd.pr_copy_settings.data.dpp_inst = pipe_ctx->plane_res.dpp->inst;
+ cmd.pr_copy_settings.data.dpp_inst = (uint8_t)pipe_ctx->plane_res.dpp->inst;
else
cmd.pr_copy_settings.data.dpp_inst = 0;
if (pipe_ctx->stream_res.tg)
- cmd.pr_copy_settings.data.otg_inst = pipe_ctx->stream_res.tg->inst;
+ cmd.pr_copy_settings.data.otg_inst = (uint8_t)pipe_ctx->stream_res.tg->inst;
else
cmd.pr_copy_settings.data.otg_inst = 0;
@@ -322,6 +322,19 @@ bool dp_pr_copy_settings(struct dc_link *link, struct replay_context *replay_con
cmd.pr_copy_settings.data.flags.bitfields.dsc_enable_status = (pipe_ctx->stream->timing.flags.DSC == 1);
cmd.pr_copy_settings.data.debug.u32All = link->replay_settings.config.debug_flags;
+ // ALPM settings
+ cmd.pr_copy_settings.data.flags.bitfields.alpm_mode = (enum dmub_alpm_mode)link->replay_settings.config.alpm_mode;
+ if (link->replay_settings.config.alpm_mode == DC_ALPM_AUXLESS) {
+ cmd.pr_copy_settings.data.auxless_alpm_data.lfps_setup_ns = (uint16_t)dc->debug.auxless_alpm_lfps_setup_ns;
+ cmd.pr_copy_settings.data.auxless_alpm_data.lfps_period_ns = (uint16_t)dc->debug.auxless_alpm_lfps_period_ns;
+ cmd.pr_copy_settings.data.auxless_alpm_data.lfps_silence_ns = (uint16_t)dc->debug.auxless_alpm_lfps_silence_ns;
+ cmd.pr_copy_settings.data.auxless_alpm_data.lfps_t1_t2_override_us =
+ (uint16_t)dc->debug.auxless_alpm_lfps_t1t2_us;
+ cmd.pr_copy_settings.data.auxless_alpm_data.lfps_t1_t2_offset_us =
+ (uint16_t)dc->debug.auxless_alpm_lfps_t1t2_offset_us;
+ cmd.pr_copy_settings.data.auxless_alpm_data.lttpr_count = link->dc->link_srv->dp_get_lttpr_count(link);
+ }
+
cmd.pr_copy_settings.data.su_granularity_needed = link->dpcd_caps.vesa_replay_caps.bits.PR_SU_GRANULARITY_NEEDED;
cmd.pr_copy_settings.data.su_x_granularity = link->dpcd_caps.vesa_replay_su_info.pr_su_x_granularity;
cmd.pr_copy_settings.data.su_y_granularity = link->dpcd_caps.vesa_replay_su_info.pr_su_y_granularity;
@@ -329,9 +342,9 @@ bool dp_pr_copy_settings(struct dc_link *link, struct replay_context *replay_con
link->dpcd_caps.vesa_replay_su_info.pr_su_y_granularity_extended_caps;
if (pipe_ctx->stream->timing.dsc_cfg.num_slices_v > 0)
- cmd.pr_copy_settings.data.dsc_slice_height = (pipe_ctx->stream->timing.v_addressable +
+ cmd.pr_copy_settings.data.dsc_slice_height = (uint16_t)((pipe_ctx->stream->timing.v_addressable +
pipe_ctx->stream->timing.v_border_top + pipe_ctx->stream->timing.v_border_bottom) /
- pipe_ctx->stream->timing.dsc_cfg.num_slices_v;
+ pipe_ctx->stream->timing.dsc_cfg.num_slices_v);
if (dc_is_embedded_signal(link->connector_signal))
cmd.pr_copy_settings.data.main_link_activity_option = OPTION_1C;
@@ -358,7 +371,7 @@ bool dp_pr_update_state(struct dc_link *link, struct dmub_cmd_pr_update_state_da
cmd.pr_update_state.header.type = DMUB_CMD__PR;
cmd.pr_update_state.header.sub_type = DMUB_CMD__PR_UPDATE_STATE;
cmd.pr_update_state.header.payload_bytes = sizeof(struct dmub_cmd_pr_update_state_data);
- cmd.pr_update_state.data.panel_inst = panel_inst;
+ cmd.pr_update_state.data.panel_inst = (uint8_t)panel_inst;
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
return true;
@@ -379,7 +392,7 @@ bool dp_pr_set_general_cmd(struct dc_link *link, struct dmub_cmd_pr_general_cmd_
cmd.pr_general_cmd.header.type = DMUB_CMD__PR;
cmd.pr_general_cmd.header.sub_type = DMUB_CMD__PR_GENERAL_CMD;
cmd.pr_general_cmd.header.payload_bytes = sizeof(struct dmub_cmd_pr_general_cmd_data);
- cmd.pr_general_cmd.data.panel_inst = panel_inst;
+ cmd.pr_general_cmd.data.panel_inst = (uint8_t)panel_inst;
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
return true;
@@ -397,7 +410,7 @@ bool dp_pr_get_state(const struct dc_link *link, uint64_t *state)
do {
// Send gpint command and wait for ack
- if (!dc_wake_and_execute_gpint(dc->ctx, DMUB_GPINT__GET_REPLAY_STATE, panel_inst,
+ if (!dc_wake_and_execute_gpint(dc->ctx, DMUB_GPINT__GET_REPLAY_STATE, (uint16_t)panel_inst,
&replay_state, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)) {
// Return invalid state when GPINT times out
replay_state = PR_STATE_INVALID;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c
index ddff0db4ce70..04eedec8a230 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c
@@ -310,7 +310,7 @@ static void maximize_lane_settings(const struct link_training_settings *lt_setti
max_requested.FFE_PRESET = lane_settings[0].FFE_PRESET;
/* Determine what the maximum of the requested settings are*/
- for (lane = 1; lane < lt_settings->link_settings.lane_count; lane++) {
+ for (lane = 1; lane < (uint32_t)lt_settings->link_settings.lane_count; lane++) {
if (lane_settings[lane].VOLTAGE_SWING > max_requested.VOLTAGE_SWING)
max_requested.VOLTAGE_SWING = lane_settings[lane].VOLTAGE_SWING;
@@ -568,7 +568,7 @@ enum link_training_result dp_check_link_loss_status(
sizeof(dpcd_buf));
/*parse lane status*/
- for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) {
+ for (lane = 0; lane < (uint32_t)link->cur_link_settings.lane_count; lane++) {
/*
* check lanes status
*/
@@ -1721,6 +1721,20 @@ bool perform_link_training_with_retries(
}
}
+ if (j == (attempts - 1)) {
+ DC_LOG_WARNING(
+ "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n",
+ __func__, link->link_index, (unsigned int)j + 1, attempts,
+ cur_link_settings.link_rate, cur_link_settings.lane_count,
+ cur_link_settings.link_spread, status);
+ } else {
+ DC_LOG_HW_LINK_TRAINING(
+ "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n",
+ __func__, link->link_index, (unsigned int)j + 1, attempts,
+ cur_link_settings.link_rate, cur_link_settings.lane_count,
+ cur_link_settings.link_spread, status);
+ }
+
fail_count++;
dp_trace_lt_fail_count_update(link, fail_count, false);
if (link->ep_type == DISPLAY_ENDPOINT_PHY) {
@@ -1740,20 +1754,6 @@ bool perform_link_training_with_retries(
do_fallback = false;
}
- if (j == (attempts - 1)) {
- DC_LOG_WARNING(
- "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n",
- __func__, link->link_index, (unsigned int)j + 1, attempts,
- cur_link_settings.link_rate, cur_link_settings.lane_count,
- cur_link_settings.link_spread, status);
- } else {
- DC_LOG_HW_LINK_TRAINING(
- "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n",
- __func__, link->link_index, (unsigned int)j + 1, attempts,
- cur_link_settings.link_rate, cur_link_settings.lane_count,
- cur_link_settings.link_spread, status);
- }
-
dp_disable_link_phy(link, &pipe_ctx->link_res, signal);
/* Abort link training if failure due to sink being unplugged. */
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c
index 66d0fb1b9b9d..4331e032416f 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c
@@ -132,7 +132,7 @@ void decide_8b_10b_training_settings(
*/
lt_settings->link_settings.link_spread = link->dp_ss_off ?
LINK_SPREAD_DISABLED : LINK_SPREAD_05_DOWNSPREAD_30KHZ;
- lt_settings->eq_pattern_time = get_eq_training_aux_rd_interval(link, link_setting);
+ lt_settings->eq_pattern_time = (uint16_t)get_eq_training_aux_rd_interval(link, link_setting);
lt_settings->pattern_for_cr = decide_cr_training_pattern(link_setting);
lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_res, link_setting);
lt_settings->enhanced_framing = 1;
@@ -140,7 +140,7 @@ void decide_8b_10b_training_settings(
lt_settings->disallow_per_lane_settings = true;
lt_settings->always_match_dpcd_with_hw_lane_settings = true;
lt_settings->lttpr_mode = dp_decide_8b_10b_lttpr_mode(link);
- lt_settings->cr_pattern_time = get_cr_training_aux_rd_interval(link, link_setting, lt_settings->lttpr_mode);
+ lt_settings->cr_pattern_time = (uint16_t)get_cr_training_aux_rd_interval(link, link_setting, lt_settings->lttpr_mode);
dp_hw_to_dpcd_lane_settings(lt_settings, lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
/* Some embedded LTTPRs rely on receiving TPS2 before LT to interop reliably with sensitive VGA dongles
@@ -195,7 +195,7 @@ static void set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(str
* 6. Begin link training as usual
* */
- uint32_t closest_lttpr_address_offset = dp_get_closest_lttpr_offset(lttpr_count);
+ uint32_t closest_lttpr_address_offset = dp_get_closest_lttpr_offset((uint8_t)lttpr_count);
union dpcd_training_pattern dpcd_pattern = {0};
@@ -379,7 +379,7 @@ enum link_training_result perform_8b_10b_channel_equalization_sequence(
dpcd_set_lane_settings(link, lt_settings, offset);
/* 3. wait for receiver to lock-on*/
- wait_time_microsec = dp_get_eq_aux_rd_interval(link, lt_settings, offset, retries_ch_eq);
+ wait_time_microsec = dp_get_eq_aux_rd_interval(link, lt_settings, offset, (uint8_t)retries_ch_eq);
dp_wait_for_training_aux_rd_interval(
link,
@@ -408,7 +408,7 @@ enum link_training_result perform_8b_10b_channel_equalization_sequence(
/* 6. check CHEQ done*/
if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
dp_is_symbol_locked(lane_count, dpcd_lane_status) &&
- dp_check_interlane_aligned(dpcd_lane_status_updated, link, retries_ch_eq))
+ dp_check_interlane_aligned(dpcd_lane_status_updated, link, (uint8_t)retries_ch_eq))
return LINK_TRAINING_SUCCESS;
/* 7. update VS/PE/PC2 in lt_settings*/
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
index 4a2699a374b7..16951a9550f2 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
@@ -29,6 +29,7 @@
*/
#include "link_edp_panel_control.h"
+#include "link_ddc.h"
#include "link_dpcd.h"
#include "link_dp_capability.h"
#include "dm_helpers.h"
@@ -47,9 +48,9 @@
#define DP_SINK_PR_ENABLE_AND_CONFIGURATION 0x37B
/* Travis */
-static const uint8_t DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT";
+static const char DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT";
/* Nutmeg */
-static const uint8_t DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA";
+static const char DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA";
void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode)
{
@@ -160,9 +161,7 @@ bool edp_set_backlight_level_nits(struct dc_link *link,
if (link->is_dds && !link->dpcd_caps.panel_luminance_control)
return true;
- // use internal backlight control if dmub capabilities are not present
- if (link->backlight_control_type == BACKLIGHT_CONTROL_VESA_AUX &&
- !link->dc->caps.dmub_caps.aux_backlight_support) {
+ if (link->backlight_control_type == BACKLIGHT_CONTROL_VESA_AUX) {
uint8_t backlight_enable = 0;
struct target_luminance_value *target_luminance = NULL;
@@ -272,10 +271,11 @@ bool edp_backlight_enable_aux(struct dc_link *link, bool enable)
if (link->is_dds)
return true;
- if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_ENABLE,
- &backlight_enable, 1) != DC_OK)
- return false;
-
+ if (!link->dpcd_caps.panel_luminance_control) {
+ if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_ENABLE,
+ &backlight_enable, 1) != DC_OK)
+ return false;
+ }
return true;
}
@@ -462,7 +462,7 @@ bool edp_receiver_ready_T9(struct dc_link *link)
do {
sinkstatus = 1;
result = core_link_read_dpcd(link, DP_SINK_STATUS, &sinkstatus, sizeof(sinkstatus));
- if (sinkstatus == 0)
+ if (!(sinkstatus & DP_RECEIVE_PORT_0_STATUS))
break;
if (result != DC_OK)
break;
@@ -492,7 +492,7 @@ bool edp_receiver_ready_T7(struct dc_link *link)
do {
sinkstatus = 0;
result = core_link_read_dpcd(link, DP_SINK_STATUS, &sinkstatus, sizeof(sinkstatus));
- if (sinkstatus == 1)
+ if (sinkstatus & DP_RECEIVE_PORT_0_STATUS)
break;
if (result != DC_OK)
break;
@@ -601,12 +601,12 @@ bool edp_set_psr_allow_active(struct dc_link *link, const bool *allow_active,
link->psr_settings.psr_power_opt = *power_opts;
if (psr != NULL && link->psr_settings.psr_feature_enabled && psr->funcs->psr_set_power_opt)
- psr->funcs->psr_set_power_opt(psr, link->psr_settings.psr_power_opt, panel_inst);
+ psr->funcs->psr_set_power_opt(psr, link->psr_settings.psr_power_opt, (uint8_t)panel_inst);
}
if (psr != NULL && link->psr_settings.psr_feature_enabled &&
force_static && psr->funcs->psr_force_static)
- psr->funcs->psr_force_static(psr, panel_inst);
+ psr->funcs->psr_force_static(psr, (uint8_t)panel_inst);
/* Enable or Disable PSR */
if (allow_active && link->psr_settings.psr_allow_active != *allow_active) {
@@ -615,9 +615,9 @@ bool edp_set_psr_allow_active(struct dc_link *link, const bool *allow_active,
if (!link->psr_settings.psr_allow_active)
dc_z10_restore(dc);
- if (psr != NULL && link->psr_settings.psr_feature_enabled) {
- psr->funcs->psr_enable(psr, link->psr_settings.psr_allow_active, wait, panel_inst);
- } else if ((dmcu != NULL && dmcu->funcs->is_dmcu_initialized(dmcu)) &&
+ if (psr != NULL && link->psr_settings.psr_feature_enabled)
+ psr->funcs->psr_enable(psr, link->psr_settings.psr_allow_active, wait, (uint8_t)panel_inst);
+ else if ((dmcu != NULL && dmcu->funcs->is_dmcu_initialized(dmcu)) &&
link->psr_settings.psr_feature_enabled)
dmcu->funcs->set_psr_enable(dmcu, link->psr_settings.psr_allow_active, wait);
else
@@ -637,7 +637,7 @@ bool edp_get_psr_state(const struct dc_link *link, enum dc_psr_state *state)
return false;
if (psr != NULL && link->psr_settings.psr_feature_enabled)
- psr->funcs->psr_get_state(psr, state, panel_inst);
+ psr->funcs->psr_get_state(psr, state, (uint8_t)panel_inst);
else if (dmcu != NULL && link->psr_settings.psr_feature_enabled)
dmcu->funcs->get_psr_state(dmcu, state);
@@ -788,7 +788,7 @@ bool edp_setup_psr(struct dc_link *link,
}
}
- psr_context->channel = link->ddc->ddc_pin->hw_info.ddc_channel;
+ psr_context->channel = link_get_ddc_aux_inst(link);
psr_context->transmitterId = link->link_enc->transmitter;
psr_context->engineId = link->link_enc->preferred_engine;
@@ -811,7 +811,7 @@ bool edp_setup_psr(struct dc_link *link,
psr_context->smuPhyId = transmitter_to_phy_id(link);
psr_context->crtcTimingVerticalTotal = stream->timing.v_total;
- psr_context->vsync_rate_hz = div64_u64(div64_u64((stream->
+ psr_context->vsync_rate_hz = (unsigned int)div64_u64(div64_u64((stream->
timing.pix_clk_100hz * (u64)100),
stream->timing.v_total),
stream->timing.h_total);
@@ -885,7 +885,7 @@ bool edp_setup_psr(struct dc_link *link,
if (psr) {
link->psr_settings.psr_feature_enabled = psr->funcs->psr_copy_settings(psr,
- link, psr_context, panel_inst);
+ link, psr_context, (uint8_t)panel_inst);
link->psr_settings.psr_power_opt = 0;
link->psr_settings.psr_allow_active = 0;
} else {
@@ -913,7 +913,7 @@ void edp_get_psr_residency(const struct dc_link *link, uint32_t *residency, enum
// PSR residency measurements only supported on DMCUB
if (psr != NULL && link->psr_settings.psr_feature_enabled)
- psr->funcs->psr_get_residency(psr, residency, panel_inst, mode);
+ psr->funcs->psr_get_residency(psr, residency, (uint8_t)panel_inst, mode);
else
*residency = 0;
}
@@ -947,7 +947,7 @@ bool edp_set_replay_allow_active(struct dc_link *link, const bool *allow_active,
if (power_opts && link->replay_settings.replay_power_opt_active != *power_opts) {
if (replay != NULL && link->replay_settings.replay_feature_enabled &&
replay->funcs->replay_set_power_opt) {
- replay->funcs->replay_set_power_opt(replay, *power_opts, panel_inst);
+ replay->funcs->replay_set_power_opt(replay, *power_opts, (uint8_t)panel_inst);
link->replay_settings.replay_power_opt_active = *power_opts;
}
}
@@ -957,7 +957,7 @@ bool edp_set_replay_allow_active(struct dc_link *link, const bool *allow_active,
// TODO: Handle mux change case if force_static is set
// If force_static is set, just change the replay_allow_active state directly
if (replay != NULL && link->replay_settings.replay_feature_enabled)
- replay->funcs->replay_enable(replay, *allow_active, wait, panel_inst);
+ replay->funcs->replay_enable(replay, *allow_active, wait, (uint8_t)panel_inst);
link->replay_settings.replay_allow_active = *allow_active;
}
@@ -975,7 +975,7 @@ bool edp_get_replay_state(const struct dc_link *link, uint64_t *state)
return false;
if (replay != NULL && link->replay_settings.replay_feature_enabled)
- replay->funcs->replay_get_state(replay, &pr_state, panel_inst);
+ replay->funcs->replay_get_state(replay, &pr_state, (uint8_t)panel_inst);
*state = pr_state;
return true;
@@ -1021,7 +1021,7 @@ bool edp_setup_freesync_replay(struct dc_link *link, const struct dc_stream_stat
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
- replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel;
+ replay_context.aux_inst = link_get_ddc_aux_inst(link);
replay_context.digbe_inst = link->link_enc->transmitter;
replay_context.digfe_inst = link->link_enc->preferred_engine;
@@ -1046,13 +1046,14 @@ bool edp_setup_freesync_replay(struct dc_link *link, const struct dc_stream_stat
replay_context.os_request_force_ffu = link->replay_settings.config.os_request_force_ffu;
link->replay_settings.replay_feature_enabled =
- replay->funcs->replay_copy_settings(replay, link, &replay_context, panel_inst);
+ replay->funcs->replay_copy_settings(replay, link, &replay_context, (uint8_t)panel_inst);
if (link->replay_settings.replay_feature_enabled) {
replay_config.bits.FREESYNC_PANEL_REPLAY_MODE = 1;
- replay_config.bits.TIMING_DESYNC_ERROR_VERIFICATION =
- link->replay_settings.config.replay_timing_sync_supported;
+ replay_config.bits.TIMING_DESYNC_ERROR_VERIFICATION = 0;
replay_config.bits.STATE_TRANSITION_ERROR_DETECTION = 1;
+ replay_config.bits.FRAME_SKIPPING_ERROR_DETECTION = 1;
+ replay_config.bits.FRAME_SKIPPING_ENABLE = 1;
dm_helpers_dp_write_dpcd(link->ctx, link,
DP_SINK_PR_ENABLE_AND_CONFIGURATION,
(uint8_t *)&(replay_config.raw), sizeof(uint8_t));
@@ -1096,7 +1097,7 @@ bool edp_send_replay_cmd(struct dc_link *link,
return false;
if (dp_pr_get_panel_inst(dc, link, &panel_inst))
- cmd_data->panel_inst = panel_inst;
+ cmd_data->panel_inst = (uint8_t)panel_inst;
else {
DC_LOG_DC("%s(): get edp panel inst fail ", __func__);
return false;
@@ -1121,7 +1122,7 @@ bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal, uin
if (coasting_vtotal && (link->replay_settings.coasting_vtotal != coasting_vtotal ||
link->replay_settings.frame_skip_number != frame_skip_number)) {
- replay->funcs->replay_set_coasting_vtotal(replay, coasting_vtotal, panel_inst, frame_skip_number);
+ replay->funcs->replay_set_coasting_vtotal(replay, coasting_vtotal, (uint8_t)panel_inst, frame_skip_number);
link->replay_settings.coasting_vtotal = coasting_vtotal;
link->replay_settings.frame_skip_number = frame_skip_number;
}
@@ -1143,7 +1144,7 @@ bool edp_replay_residency(const struct dc_link *link,
return false;
if (replay != NULL && link->replay_settings.replay_feature_enabled)
- replay->funcs->replay_residency(replay, panel_inst, residency, is_start, mode);
+ replay->funcs->replay_residency(replay, (uint8_t)panel_inst, residency, is_start, mode);
else
*residency = 0;
@@ -1168,7 +1169,7 @@ bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link,
if (link->replay_settings.replay_feature_enabled &&
replay->funcs->replay_set_power_opt_and_coasting_vtotal) {
replay->funcs->replay_set_power_opt_and_coasting_vtotal(replay,
- *power_opts, panel_inst, coasting_vtotal, frame_skip_number);
+ *power_opts, (uint8_t)panel_inst, coasting_vtotal, frame_skip_number);
link->replay_settings.replay_power_opt_active = *power_opts;
link->replay_settings.coasting_vtotal = coasting_vtotal;
link->replay_settings.frame_skip_number = frame_skip_number;
@@ -1211,9 +1212,9 @@ int edp_get_backlight_level(const struct dc_link *link)
fw_set_brightness = dmcu->funcs->is_dmcu_initialized(dmcu);
if (!fw_set_brightness && panel_cntl->funcs->get_current_backlight)
- return panel_cntl->funcs->get_current_backlight(panel_cntl);
+ return (int)panel_cntl->funcs->get_current_backlight(panel_cntl);
else if (abm != NULL && abm->funcs->get_current_backlight != NULL)
- return (int) abm->funcs->get_current_backlight(abm);
+ return (int)abm->funcs->get_current_backlight(abm);
else
return DC_ERROR_UNEXPECTED;
}
@@ -1252,10 +1253,10 @@ static void edp_set_assr_enable(const struct dc *pDC, struct dc_link *link,
memset(&cmd, 0, sizeof(cmd));
- link_enc_index = link->link_enc->transmitter - TRANSMITTER_UNIPHY_A;
+ link_enc_index = (uint8_t)(link->link_enc->transmitter - TRANSMITTER_UNIPHY_A);
if (link_res->hpo_dp_link_enc) {
- link_enc_index = link_res->hpo_dp_link_enc->inst;
+ link_enc_index = (uint8_t)link_res->hpo_dp_link_enc->inst;
use_hpo_dp_link_enc = true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
new file mode 100644
index 000000000000..714168e1ece6
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -0,0 +1,1204 @@
+/*
+ * 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 implements FRL link capability and link training related functions.
+ * FRL link is established by order of retrieve_link, verify_link, and poll_status.
+ * Other helper functions exist to obtain information required to maintain
+ * the correct sequence according to HDMI specification. Each sequence and state
+ * inside link training functions are timing sensitive and order sensitive.
+ * It is mandatory that these functions are debugged with FRL_LTP output message
+ * configurable in DSAT. Any changes in the LT sequence should follow the HDMI
+ * specification as much as possible and tested through HDMI electrical and
+ * link layer compliance.
+ */
+#include "link_hdmi_frl.h"
+#include "link_ddc.h"
+#include "link/link_dpms.h"
+#include "link/link_validation.h"
+#include "resource.h"
+#include "dccg.h"
+
+#include "dml/dml1_frl_cap_chk.h"
+
+#define DC_LOGGER \
+ dc_logger
+#define DC_LOGGER_INIT(logger) \
+ struct dal_logger *dc_logger = logger
+
+#define FRL_INFO(...) \
+ DC_LOG_HDMI_FRL_LTP( \
+ __VA_ARGS__)
+
+static bool hdmi_frl_test_max_rate(struct ddc_service *ddc_service)
+{
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = HDMI_SCDC_SOURCE_TEST_REQ;
+ union hdmi_scdc_source_test_req test_req = {0};
+
+ DC_LOGGER_INIT(ddc_service->link->ctx->logger);
+
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &test_req.byte,
+ sizeof(test_req.byte));
+ if (test_req.fields.FRL_MAX) {
+ FRL_INFO("FRL TEST REQ: FRL_MAX = 1");
+ return true;
+ }
+
+ return false;
+}
+
+static void hdmi_return_preeshoot_and_deemphasis(struct dc_link *link,
+ union hdmi_scdc_source_test_req *test_req, bool *de_emphasis_only,
+ bool *pre_shoot_only, bool *no_ffe)
+{
+ /* check if cable_id is valid */
+ if (link->hdmi_cable_id.raw[0] && link->frl_link_settings.frl_link_rate >=
+ HDMI_FRL_LINK_RATE_16GBPS) {
+ *de_emphasis_only = (test_req->fields.TXFFE_DEEMPHASIS && link->hdmi_cable_id.bits.no_DeEmphasis_n) ||
+ !link->hdmi_cable_id.bits.no_PreShoot_n;
+ *pre_shoot_only = (test_req->fields.TXFFE_PRESHOOT && link->hdmi_cable_id.bits.no_PreShoot_n) ||
+ !link->hdmi_cable_id.bits.no_DeEmphasis_n;
+ *no_ffe = test_req->fields.TXFFE_NOFFE ||
+ (!link->hdmi_cable_id.bits.no_PreShoot_n &&
+ !link->hdmi_cable_id.bits.no_DeEmphasis_n);
+ } else {
+ *de_emphasis_only = test_req->fields.TXFFE_DEEMPHASIS;
+ *pre_shoot_only = test_req->fields.TXFFE_PRESHOOT;
+ *no_ffe = test_req->fields.TXFFE_NOFFE;
+ }
+}
+
+static bool hdmi_frl_test_dsc_max_rate(struct ddc_service *ddc_service)
+{
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = HDMI_SCDC_SOURCE_TEST_REQ;
+ union hdmi_scdc_source_test_req test_req = {0};
+
+ DC_LOGGER_INIT(ddc_service->link->ctx->logger);
+
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &test_req.byte,
+ sizeof(test_req.byte));
+ if (test_req.fields.DSC_FRL_MAX) {
+ FRL_INFO("FRL TEST REQ: DSC_FRL_MAX = 1");
+ return true;
+ }
+
+ return false;
+}
+
+enum clock_source_id hdmi_frl_find_matching_phypll(
+ struct dc_link *link)
+{
+ switch (link->link_enc->transmitter) {
+ case TRANSMITTER_UNIPHY_A:
+ return CLOCK_SOURCE_COMBO_PHY_PLL0;
+ case TRANSMITTER_UNIPHY_B:
+ return CLOCK_SOURCE_COMBO_PHY_PLL1;
+ case TRANSMITTER_UNIPHY_C:
+ return CLOCK_SOURCE_COMBO_PHY_PLL2;
+ case TRANSMITTER_UNIPHY_D:
+ return CLOCK_SOURCE_COMBO_PHY_PLL3;
+ case TRANSMITTER_UNIPHY_E:
+ return CLOCK_SOURCE_COMBO_PHY_PLL4;
+ case TRANSMITTER_UNIPHY_F:
+ return CLOCK_SOURCE_COMBO_PHY_PLL5;
+ default:
+ return CLOCK_SOURCE_ID_UNDEFINED;
+ };
+}
+
+void hdmi_frl_retrieve_link_cap(struct dc_link *link, struct dc_sink *sink)
+{
+ enum hdmi_frl_link_rate encoder_link_rate = HDMI_FRL_LINK_RATE_6GBPS_4LANE;
+
+ if (link->link_enc->features.flags.bits.IS_FRL_8G_CAPABLE)
+ encoder_link_rate = HDMI_FRL_LINK_RATE_8GBPS;
+
+ if (link->link_enc->features.flags.bits.IS_FRL_10G_CAPABLE)
+ encoder_link_rate = HDMI_FRL_LINK_RATE_10GBPS;
+
+ if (link->link_enc->features.flags.bits.IS_FRL_12G_CAPABLE)
+ encoder_link_rate = HDMI_FRL_LINK_RATE_12GBPS;
+ if (link->link_enc->features.flags.bits.IS_FRL_16G_CAPABLE)
+ encoder_link_rate = HDMI_FRL_LINK_RATE_16GBPS;
+
+ if (link->link_enc->features.flags.bits.IS_FRL_20G_CAPABLE)
+ encoder_link_rate = HDMI_FRL_LINK_RATE_20GBPS;
+
+ if (link->dc->debug.max_frl_rate != 0 && encoder_link_rate > link->dc->debug.max_frl_rate)
+ encoder_link_rate = link->dc->debug.max_frl_rate;
+
+ if (link->frl_flags.force_frl_rate != 0 &&
+ link->frl_flags.force_frl_rate != 0xF)
+ encoder_link_rate = link->frl_flags.force_frl_rate;
+
+ link->frl_reported_link_cap.frl_link_rate =
+ (encoder_link_rate < sink->edid_caps.max_frl_rate) ?
+ encoder_link_rate : sink->edid_caps.max_frl_rate;
+
+ if (sink->edid_caps.max_frl_rate < HDMI_FRL_LINK_RATE_6GBPS_4LANE)
+ link->frl_reported_link_cap.frl_num_lanes = 3;
+ else
+ link->frl_reported_link_cap.frl_num_lanes = 4;
+}
+
+struct dc_hdmi_frl_link_settings *hdmi_frl_get_verified_link_cap(
+ struct dc_link *link)
+{
+ // TODO: rework hdmi_frl_get_verified_link_cap to be a const interface
+ return &link->frl_verified_link_cap;
+}
+
+
+void hdmi_frl_LTS_clear_Update_flag(struct ddc_service *ddc_service)
+{
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = HDMI_SCDC_UPDATE_0;
+ uint8_t write_buffer[2] = { 0 };
+ union hdmi_scdc_update_read_data scdc_update = {0};
+ DC_LOGGER_INIT(ddc_service->link->ctx->logger);
+
+ /*Check FLT_update flag*/
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &scdc_update.byte[0],
+ sizeof(scdc_update.byte[0]));
+
+ if (scdc_update.fields.FLT_UPDATE != 0) {
+ FRL_INFO("FRL LINK TRAINING: LTS:L Clear FLT_UPDATE.\n");
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*FLT_update - bit 5*/
+ write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5);
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ }
+}
+
+
+bool hdmi_frl_poll_status_flag(struct dc_link *link)
+{
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = 0;
+ uint32_t ln0_pattern = 0;
+ uint32_t ln1_pattern = 0;
+ uint32_t ln2_pattern = 0;
+ uint32_t ln3_pattern = 0;
+ uint8_t write_buffer[2] = {0};
+ union hdmi_scdc_update_read_data scdc_update = {0};
+ union hdmi_scdc_source_test_req test_req = {0};
+ union hdmi_scdc_LTP_req_data ltp_req = {0};
+ struct hpo_frl_link_encoder *hpo_frl_link_enc = link->hpo_frl_link_enc;
+ struct link_encoder *dio_link_enc = link->link_enc;
+ bool flt_no_timeout = false;
+ bool link_update = false;
+
+ /*Test Condition - FLT_no_timeout avoid link training*/
+ offset = HDMI_SCDC_SOURCE_TEST_REQ;
+ link_query_ddc_data(link->ddc, slave_address, &offset,
+ sizeof(offset), &test_req.byte, sizeof(test_req.byte));
+ if (test_req.fields.FLT_NO_TIMEOUT)
+ flt_no_timeout = true;
+
+ offset = HDMI_SCDC_UPDATE_0;
+
+ /*Check FLT_update flag*/
+ link_query_ddc_data(link->ddc, slave_address,
+ &offset, sizeof(offset), &scdc_update.byte[0],
+ sizeof(scdc_update.byte[0]));
+
+ if (scdc_update.fields.FRL_START == 1) {
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*FLT_START - bit 4*/
+ write_buffer[1] = (scdc_update.fields.FRL_START << 4);
+ link_query_ddc_data(link->ddc, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ }
+ if (scdc_update.fields.FLT_UPDATE) {
+ offset = HDMI_SCDC_LTP_REQ;
+
+ link_query_ddc_data(link->ddc, slave_address,
+ &offset, sizeof(offset), ltp_req.byte,
+ sizeof(ltp_req.byte));
+
+ ln0_pattern = ltp_req.fields.LN0_LTP_REQ;
+ ln1_pattern = ltp_req.fields.LN1_LTP_REQ;
+ ln2_pattern = ltp_req.fields.LN2_LTP_REQ;
+ ln3_pattern = ltp_req.fields.LN3_LTP_REQ;
+
+ if (ln0_pattern == 0x03 || ln1_pattern == 0x03 ||
+ ln2_pattern == 0x03 || ln3_pattern == 0x03) {
+ if (flt_no_timeout) {
+ hpo_frl_link_enc->funcs->set_hdmi_training_pattern(
+ hpo_frl_link_enc,
+ ln0_pattern - 1,
+ ln1_pattern - 1,
+ ln2_pattern - 1,
+ ln3_pattern - 1);
+ } else
+ hpo_frl_link_enc->funcs->set_hdmi_training_pattern(
+ hpo_frl_link_enc,
+ (ln0_pattern == 0x03) ? 0xF : ln0_pattern - 1,
+ (ln1_pattern == 0x03) ? 0xF : ln1_pattern - 1,
+ (ln2_pattern == 0x03) ? 0xF : ln2_pattern - 1,
+ (ln3_pattern == 0x03) ? 0xF : ln3_pattern - 1);
+ } else
+ hpo_frl_link_enc->funcs->set_hdmi_training_pattern(
+ hpo_frl_link_enc,
+ ln0_pattern - 1,
+ ln1_pattern - 1,
+ ln2_pattern - 1,
+ ln3_pattern - 1);
+ if (ln0_pattern == 0x0E || ln1_pattern == 0x0E ||
+ ln2_pattern == 0x0E || ln3_pattern == 0x0E) {
+ if (scdc_update.fields.FLT_UPDATE) {
+ if (link->dc->debug.limit_ffe == 0)
+ return false;
+
+ bool de_emphasis_only = false;
+ bool pre_shoot_only = false;
+ bool no_ffe = false;
+
+ hdmi_return_preeshoot_and_deemphasis(link, &test_req,
+ &de_emphasis_only, &pre_shoot_only, &no_ffe);
+
+ dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0xEE,
+ de_emphasis_only,
+ pre_shoot_only,
+ no_ffe,
+ &link->frl_link_settings);
+ }
+ } else
+ if (!flt_no_timeout) {
+ hdmi_frl_LTS_clear_Link_Setting(link->ddc);
+ hdmi_frl_LTS_clear_Update_flag(link->ddc);
+ hpo_frl_link_enc->funcs->set_hdmi_training_pattern(hpo_frl_link_enc, 0, 0, 0, 0);
+ hdmi_frl_perform_link_training_with_retries(link);
+ link_update = true;
+ }
+
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*Clear SCDC Update Flags*/
+ write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5);
+ link_query_ddc_data(link->ddc, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ }
+ if (scdc_update.fields.SOURCE_TEST_UPDATE) {
+ offset = HDMI_SCDC_SOURCE_TEST_REQ;
+ bool de_emphasis_only = false;
+ bool pre_shoot_only = false;
+ bool no_ffe = false;
+
+ link_query_ddc_data(link->ddc, slave_address,
+ &offset, sizeof(offset), &test_req.byte,
+ sizeof(test_req.byte));
+
+ hdmi_return_preeshoot_and_deemphasis(link, &test_req,
+ &de_emphasis_only, &pre_shoot_only, &no_ffe);
+
+ dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0xFF,
+ de_emphasis_only,
+ pre_shoot_only,
+ no_ffe,
+ &link->frl_link_settings);
+
+
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*Clear SCDC Update Flags*/
+ write_buffer[1] = (scdc_update.fields.SOURCE_TEST_UPDATE << 3);
+ link_query_ddc_data(link->ddc, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+
+ }
+
+ return link_update;
+}
+
+void hdmi_frl_poll_start(struct ddc_service *ddc_service)
+{
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = HDMI_SCDC_UPDATE_0;
+ uint8_t write_buffer[2] = {0};
+ union hdmi_scdc_source_test_req test_req = {0};
+ union hdmi_scdc_update_read_data scdc_update = {0};
+ uint16_t num_polls = 100;
+ uint16_t wait_time = 2000;
+ bool flt_no_timeout = false;
+
+ DC_LOGGER_INIT(ddc_service->link->ctx->logger);
+
+ /*Test Condition - FLT_no_timeout avoid link training*/
+ offset = HDMI_SCDC_SOURCE_TEST_REQ;
+ link_query_ddc_data(ddc_service, slave_address, &offset,
+ sizeof(offset), &test_req.byte, sizeof(test_req.byte));
+ if (test_req.fields.FLT_NO_TIMEOUT)
+ flt_no_timeout = true;
+
+ /*Test Condition - No need to poll FRL_START if FLT_no_timeout*/
+ if (flt_no_timeout)
+ return;
+
+ offset = HDMI_SCDC_UPDATE_0;
+ /*LTS:P: Check FRL_START, poll for 200ms */
+ for (; num_polls; num_polls--) {
+ udelay(wait_time);
+ /*Check FLT_update flag*/
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &scdc_update.byte[0],
+ sizeof(scdc_update.byte[0]));
+ FRL_INFO("FRL LINK TRAINING: Read FRL_START = %d, FLT_UPDATE = %d. num_polls = %d\n",
+ scdc_update.fields.FRL_START, scdc_update.fields.FLT_UPDATE, num_polls);
+ if (scdc_update.fields.FRL_START == 1) {
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*FLT_update - bit 5*/
+ write_buffer[1] = (scdc_update.fields.FRL_START << 4);
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ break;
+ }
+ if (scdc_update.fields.FLT_UPDATE == 1) {
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*FLT_update - bit 5*/
+ write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5);
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ break;
+ }
+ }
+ return;
+}
+
+void hdmi_frl_LTS_clear_Link_Setting(struct ddc_service *ddc_service)
+{
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = HDMI_SCDC_CONFIG_1;
+ uint8_t write_buffer[2] = { 0 };
+ union hdmi_scdc_configuration scdc_config = {0};
+ DC_LOGGER_INIT(ddc_service->link->ctx->logger);
+
+ /*Check FRL_Rate for fallback*/
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &scdc_config.byte[1],
+ sizeof(scdc_config.byte[1]));
+ if (scdc_config.fields.FRL_RATE != 0) {
+ /*LTS:L FRL_Rate = 0*/
+ write_buffer[0] = HDMI_SCDC_CONFIG_1;
+ /*FRL_RATE*/
+ write_buffer[1] = 0;
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ FRL_INFO("FRL LINK TRAINING: LTS:L - Clear FRL_Rate.\n");
+ }
+
+}
+
+static uint8_t hdmi_frl_get_max_ffe_level(struct dc_link *link,
+ struct dc_hdmi_frl_link_settings *link_settings)
+{
+ uint8_t max_ffe_level =
+ (link_settings->frl_link_rate > HDMI_FRL_LINK_RATE_12GBPS) ? 7 : 3;
+
+ if ((uint8_t)link->dc->debug.limit_ffe < max_ffe_level)
+ max_ffe_level = (uint8_t)link->dc->debug.limit_ffe;
+
+ return max_ffe_level;
+}
+
+static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_service,
+ struct dc_hdmi_frl_link_settings *link_settings)
+{
+ enum link_result result = LINK_RESULT_UNKNOWN;
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = HDMI_SCDC_STATUS_FLAGS;
+ uint32_t ln0_pattern = 0, pre_ln0_pattern = 0;
+ uint32_t ln1_pattern = 0, pre_ln1_pattern = 0;
+ uint32_t ln2_pattern = 0, pre_ln2_pattern = 0;
+ uint32_t ln3_pattern = 0, pre_ln3_pattern = 0;
+ uint8_t write_buffer[2] = {0};
+ union hdmi_scdc_update_read_data scdc_update = {0};
+ union hdmi_scdc_status_flags_data status_data = {0};
+ union hdmi_scdc_source_test_req test_req = {0};
+ union hdmi_scdc_LTP_req_data ltp_req = {0};
+ uint16_t num_polls = 0;
+ uint16_t max_polls = 105;
+ unsigned long long wait_time_ns = 2000000;
+ struct hpo_frl_link_encoder *hpo_frl_link_enc = ddc_service->link->hpo_frl_link_enc;
+ struct link_encoder *dio_link_enc = ddc_service->link->link_enc;
+ uint8_t sink_version = 0;
+ uint8_t FFE_Levels = hdmi_frl_get_max_ffe_level(ddc_service->link, link_settings);
+ uint8_t current_FFE = 0;
+ bool override_FFE = false;
+ bool flt_no_timeout = false;
+ unsigned long long flt_poll_cur_time = 0, flt_poll_last_time = 0, flt_poll_elapsed_time_ns = 0;
+
+ DC_LOGGER_INIT(ddc_service->link->ctx->logger);
+ FRL_INFO("FRL LINK TRAINING: Starting FRL Link Training.\n");
+
+ hdmi_frl_LTS_clear_Link_Setting(ddc_service);
+
+ offset = HDMI_SCDC_SINK_VERSION;
+ link_query_ddc_data(ddc_service, slave_address, &offset,
+ sizeof(offset), &sink_version, sizeof(sink_version));
+
+ FRL_INFO("FRL LINK TRAINING: Read Sink Version = %d.\n", sink_version);
+
+ if (sink_version == 0) {
+ FRL_INFO("FRL LINK TRAINING: SKIP - FRL not supported by sink.\n");
+ return LINK_RESULT_FALLBACK;
+ }
+
+ if (sink_version == 1) {
+ /*Source Version = 1*/
+ write_buffer[0] = HDMI_SCDC_SOURCE_VERSION;
+ write_buffer[1] = 1;
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ FRL_INFO("FRL LINK TRAINING: Set Source Version = 1.\n");
+ }
+
+ FRL_INFO("FRL LINK TRAINING: Poll for FLT_READY.\n");
+ offset = HDMI_SCDC_STATUS_FLAGS;
+
+ /*LTS:2: Check FLT Ready, poll for 200ms */
+ while (num_polls < max_polls) {
+ flt_poll_cur_time = dm_get_timestamp(ddc_service->ctx);
+ flt_poll_elapsed_time_ns = dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_cur_time, flt_poll_last_time);
+ if (flt_poll_elapsed_time_ns < wait_time_ns)
+ continue;
+ flt_poll_last_time = dm_get_timestamp(ddc_service->ctx);
+ num_polls++;
+
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &status_data.byte,
+ sizeof(status_data.byte));
+ FRL_INFO("FRL LINK TRAINING: Read FLT_READY = %d. num_polls = %d\n",
+ status_data.fields.FLT_READY, num_polls);
+ if (status_data.fields.FLT_READY) {
+ /* Spec recommends to clear update flag, but QD980 has problem */
+ hdmi_frl_LTS_clear_Update_flag(ddc_service);
+ dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0,
+ test_req.fields.TXFFE_DEEMPHASIS,
+ test_req.fields.TXFFE_PRESHOOT,
+ test_req.fields.TXFFE_NOFFE,
+ link_settings);
+ break;
+ }
+ }
+
+ /*Test Condition - FLT_no_timeout avoid link training*/
+ offset = HDMI_SCDC_SOURCE_TEST_REQ;
+ link_query_ddc_data(ddc_service, slave_address, &offset,
+ sizeof(offset), &test_req.byte, sizeof(test_req.byte));
+ FRL_INFO("FRL TEST REQ: FLT_no_timeout = %d \n", test_req.fields.FLT_NO_TIMEOUT);
+ if (test_req.fields.FLT_NO_TIMEOUT)
+ flt_no_timeout = true;
+
+ if (status_data.fields.FLT_READY) {
+ /*Specify FRL rate*/
+ write_buffer[0] = HDMI_SCDC_CONFIG_1;
+ /*FRL_RATE*/
+ write_buffer[1] = (uint8_t)(link_settings->frl_link_rate | (FFE_Levels << 4));
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ FRL_INFO("FRL LINK TRAINING: Write link rate = %d. Max FFE_Levels = %d\n",
+ link_settings->frl_link_rate, FFE_Levels);
+
+ FRL_INFO("FRL LINK TRAINING: Poll for FLT_UPDATE.\n");
+ /*LTS:3: Start Link Training*/
+ /*Start FLT Timer = 200 ms*/
+ num_polls = 0;
+ if (flt_no_timeout)
+ max_polls = 500;
+
+ while (num_polls < max_polls) {
+ flt_poll_cur_time = dm_get_timestamp(ddc_service->ctx);
+ flt_poll_elapsed_time_ns = dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_cur_time, flt_poll_last_time);
+ if (flt_poll_elapsed_time_ns < wait_time_ns)
+ continue;
+ flt_poll_last_time = flt_poll_cur_time;
+
+ num_polls++;
+
+ offset = HDMI_SCDC_UPDATE_0;
+ /*Check FLT_update flag*/
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &scdc_update.byte[0],
+ sizeof(scdc_update.byte[0]));
+
+ FRL_INFO("FRL LINK TRAINING: Read FLT_UPDATE = %d. num_polls = %d\n",
+ scdc_update.fields.FLT_UPDATE, num_polls);
+ /*Set TxFFE = TxFFE0*/
+ /*Program FFE_Levels - scdc_config has this field at 0 */
+ if (override_FFE) {
+ if (flt_no_timeout)
+ current_FFE = 0;
+ if (current_FFE == 0)
+ dio_link_enc->funcs->prog_eq_setting(dio_link_enc, current_FFE,
+ test_req.fields.TXFFE_DEEMPHASIS,
+ test_req.fields.TXFFE_PRESHOOT,
+ test_req.fields.TXFFE_NOFFE,
+ link_settings);
+ else {
+ if (scdc_update.fields.FLT_UPDATE)
+ dio_link_enc->funcs->prog_eq_setting(dio_link_enc, current_FFE,
+ test_req.fields.TXFFE_DEEMPHASIS,
+ test_req.fields.TXFFE_PRESHOOT,
+ test_req.fields.TXFFE_NOFFE,
+ link_settings);
+ }
+ FRL_INFO("FRL LINK TRAINING: TxFFE = %d.\n", current_FFE);
+ override_FFE = false;
+ }
+ if (scdc_update.fields.FLT_UPDATE) {
+ offset = HDMI_SCDC_LTP_REQ;
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), ltp_req.byte,
+ sizeof(ltp_req.byte));
+
+ pre_ln0_pattern = ln0_pattern;
+ pre_ln1_pattern = ln1_pattern;
+ pre_ln2_pattern = ln2_pattern;
+ pre_ln3_pattern = ln3_pattern;
+
+ ln0_pattern = ltp_req.fields.LN0_LTP_REQ;
+ ln1_pattern = ltp_req.fields.LN1_LTP_REQ;
+ ln2_pattern = ltp_req.fields.LN2_LTP_REQ;
+ ln3_pattern = ltp_req.fields.LN3_LTP_REQ;
+
+ FRL_INFO("FRL LINK TRAINING: Read LN0_LTP_REQ = %d. LN1_LTP_REQ = %d\n",
+ ltp_req.fields.LN0_LTP_REQ,
+ ltp_req.fields.LN1_LTP_REQ);
+ FRL_INFO("FRL LINK TRAINING: Read LN2_LTP_REQ = %d. LN3_LTP_REQ = %d\n",
+ ltp_req.fields.LN2_LTP_REQ,
+ ltp_req.fields.LN3_LTP_REQ);
+
+ /*Clear FLT_update flag*/
+ FRL_INFO("FRL LINK TRAINING: Clear FLT_UPDATE flag.\n");
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*FLT_update - bit 5*/
+ write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5);
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+
+ if (ln0_pattern == 0x03 || ln1_pattern == 0x03 ||
+ ln2_pattern == 0x03 || ln3_pattern == 0x03)
+ if (!flt_no_timeout)
+ continue;
+
+ if (link_settings->frl_num_lanes == 3) {
+ ln3_pattern = 1;
+ if (!ln0_pattern && !ln1_pattern && !ln2_pattern) {
+ /*Link Training is done*/
+ FRL_INFO("FRL LINK TRAINING: PASSED\n");
+ return LINK_RESULT_SUCCESS;
+ }
+ if (ln0_pattern == 0x0F || ln1_pattern == 0x0F || ln2_pattern == 0x0F) {
+ /*sink requesting to lower link rate*/
+ FRL_INFO("FRL LINK TRAINING: Sink requesting lower link rate.\n");
+ return LINK_RESULT_LOWER_LINKRATE;
+ }
+ if (ln0_pattern == 0x0E || ln1_pattern == 0x0E || ln2_pattern == 0x0E) {
+ /*sink requesting to next FFE*/
+ FRL_INFO("FRL LINK TRAINING: Sink requesting next FFE.\n");
+ if (ddc_service->link->dc->debug.limit_ffe == 0) {
+ return LINK_RESULT_LOWER_LINKRATE;
+ }
+ current_FFE++;
+ override_FFE = true;
+ if (current_FFE > FFE_Levels)
+ current_FFE = 0;
+ if (flt_no_timeout)
+ current_FFE = 0;
+ }
+ } else {
+ if (!ln0_pattern && !ln1_pattern && !ln2_pattern && !ln3_pattern) {
+ /*Link Training is done*/
+ FRL_INFO("FRL LINK TRAINING: PASSED\n");
+ return LINK_RESULT_SUCCESS;
+ }
+ if (ln0_pattern == 0x0F || ln1_pattern == 0x0F ||
+ ln2_pattern == 0x0F || ln3_pattern == 0x0F) {
+ /*sink requesting to lower link rate*/
+ FRL_INFO("FRL LINK TRAINING: Sink requesting lower link rate.\n");
+ return LINK_RESULT_LOWER_LINKRATE;
+ }
+ if (ln0_pattern == 0x0E || ln1_pattern == 0x0E ||
+ ln2_pattern == 0x0E || ln3_pattern == 0x0E) {
+ /*sink requesting to next FFE*/
+ FRL_INFO("FRL LINK TRAINING: Sink requesting next FFE.\n");
+ if (ddc_service->link->dc->debug.limit_ffe == 0) {
+ return LINK_RESULT_LOWER_LINKRATE;
+ }
+ current_FFE++;
+ override_FFE = true;
+ if (current_FFE > FFE_Levels)
+ current_FFE = 0;
+ if (flt_no_timeout)
+ current_FFE = 0;
+ }
+ }
+
+ if (override_FFE) {
+ ln0_pattern = pre_ln0_pattern;
+ ln1_pattern = pre_ln1_pattern;
+ ln2_pattern = pre_ln2_pattern;
+ ln3_pattern = pre_ln3_pattern;
+ }
+
+ FRL_INFO("FRL LINK TRAINING: Setting Training Pattern [ln0,ln1,ln2,ln3] = [%d,%d,%d,%d].\n",
+ ln0_pattern, ln1_pattern, ln2_pattern, ln3_pattern);
+
+ hpo_frl_link_enc->funcs->set_hdmi_training_pattern(
+ hpo_frl_link_enc,
+ ln0_pattern - 1,
+ ln1_pattern - 1,
+ ln2_pattern - 1,
+ ln3_pattern - 1);
+ /* Workaround for DEDCN3AG-111
+ * HDMI-FRL Incorrect Serialization Order for LTP4
+ */
+ }
+
+ }
+ if (flt_no_timeout) {
+ return LINK_RESULT_SUCCESS;
+ } else {
+ FRL_INFO("FRL LINK TRAINING: FAILED - Timeout waiting for FLT_UPDATE to be set by sink.\n");
+ write_buffer[0] = HDMI_SCDC_CONFIG_1;
+ /*FRL_RATE*/
+ write_buffer[1] = HDMI_FRL_LINK_RATE_DISABLE | (0 << 4);
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ hdmi_frl_LTS_clear_Link_Setting(ddc_service);
+ result = LINK_RESULT_TIMEOUT;
+ }
+ } else {
+ FRL_INFO("FRL LINK TRAINING: FAILED - FLT_READY not set by sink.\n");
+ result = LINK_RESULT_TIMEOUT;
+ }
+
+ return result;
+}
+
+enum link_result hdmi_frl_perform_link_training_with_retries(
+ struct dc_link *link)
+{
+ enum link_result status = LINK_RESULT_UNKNOWN;
+ unsigned int retry_count = 0;
+ unsigned int max_retries = 3;
+
+ DC_LOGGER_INIT(link->ctx->logger);
+
+ if (link->preferred_hdmi_frl_settings.valid)
+ max_retries = link->preferred_hdmi_frl_settings.max_retries;
+
+ /* FRL Link Training */
+ while (status != LINK_RESULT_FALLBACK) {
+ if (status == LINK_RESULT_SUCCESS) {
+ break;
+ } else if (status == LINK_RESULT_LOWER_LINKRATE) {
+ FRL_INFO("FRL LINK TRAINING: Sink requested lower link rate during link enable. \n");
+ break;
+ } else {
+ if (retry_count > 0)
+ msleep(200);
+ status = hdmi_frl_perform_link_training(link->ddc,
+ &link->frl_link_settings);
+ };
+ retry_count++;
+ FRL_INFO("FRL LINK TRAINING: Retry count = %u out of %u\n", retry_count, max_retries);
+ if (retry_count > max_retries) {
+ status = LINK_RESULT_FALLBACK;
+ break;
+ }
+ }
+
+ return status;
+}
+
+enum link_result hdmi_frl_perform_link_training_with_fallback(
+ struct dc_link *link, struct link_resource *link_res,
+ enum clock_source_id frl_phy_clock_source_id)
+{
+ enum link_result status = LINK_RESULT_UNKNOWN;
+
+ DC_LOGGER_INIT(link->ctx->logger);
+
+ /* FRL Link Training */
+ while (status != LINK_RESULT_FALLBACK) {
+ if (link->frl_link_settings.frl_link_rate ==
+ HDMI_FRL_LINK_RATE_DISABLE) {
+ FRL_INFO("FRL LINK TRAINING: Cannot Link Train. Fall back to TMDS \n");
+ status = LINK_RESULT_FALLBACK;
+ break;
+ }
+
+ msleep(200);
+
+ link->ctx->dc->hwss.setup_hdmi_frl_link(link, 0,
+ frl_phy_clock_source_id);
+
+ status = hdmi_frl_perform_link_training(link->ddc,
+ &link->frl_link_settings);
+
+ link->dc->hwss.disable_link_output(link, link_res, SIGNAL_TYPE_HDMI_FRL);
+
+ if (status == LINK_RESULT_SUCCESS)
+ break;
+
+ link->frl_link_settings.frl_link_rate--;
+ if (link->frl_link_settings.frl_link_rate <
+ HDMI_FRL_LINK_RATE_6GBPS_4LANE)
+ link->frl_link_settings.frl_num_lanes = 3;
+ }
+
+ return status;
+}
+
+void hdmi_frl_verify_link_cap(struct dc_link *link,
+ struct dc_hdmi_frl_link_settings *known_limit_link_setting)
+{
+ struct dc_hdmi_frl_link_settings cur_link_setting = {0};
+ struct dc_hdmi_frl_link_settings *cur = &cur_link_setting;
+ bool success = false;
+ enum link_result status = LINK_RESULT_UNKNOWN;
+ enum clock_source_id frl_phy_clock_source_id;
+ unsigned int t_id = link->link_enc->transmitter;
+ struct link_resource link_res = {.hpo_frl_link_enc = link->hpo_frl_link_enc};
+ struct dc_stream_state *link_stream = NULL;
+ struct dc_stream_state *stream = NULL;
+ int i;
+
+ DC_LOGGER_INIT(link->ctx->logger);
+
+ link->frl_flags.force_frl_rate =
+ link->ctx->dc->debug.force_frl_rate;
+ link->frl_flags.force_frl_always =
+ link->preferred_hdmi_frl_settings.force_frl_always ||
+ link->ctx->dc->debug.force_frl_always;
+ link->frl_flags.force_frl_max =
+ link->preferred_hdmi_frl_settings.force_frl_max ||
+ link->ctx->dc->debug.force_frl_max ? true :
+ hdmi_frl_test_max_rate(link->ddc);
+ link->frl_flags.force_frl_dsc =
+ link->ctx->dc->debug.force_frl_dsc ? true :
+ hdmi_frl_test_dsc_max_rate(link->ddc);
+ link->frl_flags.apply_vsdb_rcc_wa =
+ link->ctx->dc->debug.apply_vsdb_rcc_wa;
+
+ if (link->frl_flags.force_frl_rate == 0xF)
+ return;
+
+ if (link->local_sink &&
+ link->local_sink->edid_caps.panel_patch.force_frl)
+ link->frl_flags.force_frl_always = true;
+
+ if (!link->frl_flags.force_frl_max &&
+ !link->frl_flags.force_frl_dsc &&
+ link->local_sink->edid_caps.panel_patch.hdmi_comp_auto) {
+ link->frl_flags.force_frl_max = true;
+ link->frl_flags.force_frl_dsc = true;
+ }
+
+ if (link->frl_flags.force_frl_max &&
+ !link->frl_flags.force_frl_dsc &&
+ link->local_sink->edid_caps.panel_patch.hdmi_comp_auto) {
+ link->frl_flags.force_frl_dsc = true;
+ }
+
+ if (link->local_sink &&
+ link->local_sink->edid_caps.panel_patch.vsdb_rcc_wa)
+ link->frl_flags.apply_vsdb_rcc_wa = true;
+
+ frl_phy_clock_source_id = hdmi_frl_find_matching_phypll(link);
+
+ cur_link_setting = *known_limit_link_setting;
+
+ if (link->frl_flags.force_frl_rate != 0) {
+ cur->frl_link_rate = (cur_link_setting.frl_link_rate <
+ link->frl_flags.force_frl_rate) ?
+ cur_link_setting.frl_link_rate :
+ link->frl_flags.force_frl_rate;
+ link->frl_verified_link_cap = *cur;
+ return;
+ }
+
+ if (link->local_sink) {
+ if (link->local_sink->edid_caps.panel_patch.hdmi_spe_handling) {
+ link->dc->hwss.disable_link_output(link, &link_res, link->connector_signal);
+ link->dc->res_pool->clock_sources[t_id]->funcs->cs_power_down(
+ link->dc->res_pool->clock_sources[t_id]);
+ link->frl_verified_link_cap = *cur;
+ return;
+ }
+ /* Monitor patch do decrease 10G to 8G*/
+ if (link->local_sink->edid_caps.panel_patch.block_10g) {
+ if (cur->frl_link_rate == HDMI_FRL_LINK_RATE_10GBPS)
+ cur->frl_link_rate--;
+ }
+ }
+
+ link->frl_link_settings = cur_link_setting;
+ /* disable PHY first for PNP */
+ if (link->dc->ctx->dce_version <= DCN_VERSION_3_0)
+ link->dc->hwss.disable_link_output(link, &link_res, SIGNAL_TYPE_HDMI_FRL);
+ else
+ link->dc->hwss.disable_link_output(link, &link_res, link->connector_signal);
+
+ link->dc->res_pool->clock_sources[t_id]->funcs->cs_power_down(
+ link->dc->res_pool->clock_sources[t_id]);
+ /*Either enable PHY ourselves or use VBIOS*/
+
+ FRL_INFO("FRL LINK TRAINING: Validation\n");
+
+ status = hdmi_frl_perform_link_training_with_fallback(link, &link_res, frl_phy_clock_source_id);
+
+ if (status == LINK_RESULT_SUCCESS) {
+ cur->frl_link_rate = link->frl_link_settings.frl_link_rate;
+ cur->frl_num_lanes = link->frl_link_settings.frl_num_lanes;
+ success = true;
+ link->frl_verified_link_cap = *cur;
+ }
+ if (!success) {
+ link->frl_verified_link_cap.frl_link_rate = HDMI_FRL_LINK_RATE_DISABLE;
+ link->frl_verified_link_cap.frl_num_lanes = 3;
+ }
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ stream = link->dc->current_state->streams[i];
+ if (stream && stream->link == link) {
+ link_stream = stream;
+ break;
+ }
+ }
+
+ if (link_stream) {
+ link->dc->hwss.disable_link_output(link, &link_res, link_stream->signal);
+ }
+}
+
+void hdmi_frl_set_preferred_link_settings(struct dc *dc,
+ struct dc_hdmi_frl_link_settings *link_setting,
+ struct dc_hdmi_frl_link_training_overrides *lt_overrides,
+ struct dc_link *link)
+{
+ int i;
+ struct pipe_ctx *pipe;
+ struct dc_stream_state *link_stream = 0;
+ struct pipe_ctx *link_pipe = 0;
+ struct pipe_ctx *odm_pipe;
+ int opp_cnt = 1;
+ enum link_result link_stat = LINK_RESULT_UNKNOWN;
+ enum clock_source_id frl_phy_clock_source_id;
+ struct dc_stream_state *temp_stream = &dc->scratch.temp_stream;
+
+ DC_LOGGER_INIT(link->ctx->logger);
+
+ for (i = 0; i < MAX_PIPES; i++) {
+ pipe = &dc->current_state->res_ctx.pipe_ctx[i];
+ if (pipe->stream && pipe->stream->link) {
+ if (pipe->stream->link == link) {
+ link_stream = pipe->stream;
+ link_pipe = pipe;
+ break;
+ }
+ }
+ }
+
+ /* Stream not found */
+ if (i == MAX_PIPES)
+ return;
+
+ FRL_INFO("FRL LINK TRAINING: Preferred link Update = %d.\n", link_setting->frl_link_rate);
+
+ frl_validate_mode_timing(link, &link_stream->timing, link_setting);
+
+ if (lt_overrides)
+ link->preferred_hdmi_frl_settings = *lt_overrides;
+ else
+ memset(&link->preferred_hdmi_frl_settings, 0, sizeof(link->preferred_hdmi_frl_settings));
+
+ link_stream->link->frl_link_settings = *link_setting;
+ link_stream->link->frl_verified_link_cap = *link_setting;
+
+ while (link_stat != LINK_RESULT_SUCCESS) {
+ link_set_dpms_off(pipe);
+ /* For DCN3.0, can also have 4:1 combine mode.
+ * TODO: Add function get_odm_combine_mode that has different
+ * implementation for DCN2/DCN3AG and DCN3.0
+ */
+ for (odm_pipe = pipe->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
+ opp_cnt++;
+
+ memcpy(temp_stream, link_stream, sizeof(struct dc_stream_state));
+ /* Modify patched_crtc_timing as required for padding */
+ if (link_pipe->dsc_padding_params.dsc_hactive_padding) {
+ temp_stream->timing.h_addressable = link_stream->timing.h_addressable + link_pipe->dsc_padding_params.dsc_hactive_padding;
+ temp_stream->timing.h_total = link_stream->timing.h_total + link_pipe->dsc_padding_params.dsc_htotal_padding;
+ }
+
+ pipe->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_stream_attribute(
+ pipe->stream_res.hpo_frl_stream_enc,
+ &temp_stream->timing,
+ &link_stream->link->frl_link_settings.borrow_params,
+ opp_cnt);
+
+ if (pipe->stream_res.tg->funcs->set_out_mux)
+ pipe->stream_res.tg->funcs->set_out_mux(pipe->stream_res.tg, OUT_MUX_HPO_FRL);
+
+ if ((!link_stream->link->link_enc) ||
+ (!link_stream->link->hpo_frl_link_enc) ||
+ (!link_stream->ctx->dc->res_pool->dccg->funcs->enable_hdmicharclk) ||
+ (!(pipe->stream_res.hpo_frl_stream_enc)))
+ return;
+
+ switch (link_stream->link->frl_link_settings.frl_link_rate) {
+ case HDMI_FRL_LINK_RATE_3GBPS:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 166667;
+ break;
+ case HDMI_FRL_LINK_RATE_6GBPS:
+ case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 333333;
+ break;
+ case HDMI_FRL_LINK_RATE_8GBPS:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 444444;
+ break;
+ case HDMI_FRL_LINK_RATE_10GBPS:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 555555;
+ break;
+ case HDMI_FRL_LINK_RATE_12GBPS:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 666667;
+ break;
+ case HDMI_FRL_LINK_RATE_16GBPS:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 888889;
+ break;
+ case HDMI_FRL_LINK_RATE_20GBPS:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 1111111;
+ break;
+ case HDMI_FRL_LINK_RATE_24GBPS:
+ pipe->stream_res.pix_clk_params.requested_sym_clk = 1333333;
+ break;
+ default:
+ break;
+ }
+
+ link_stream->phy_pix_clk = pipe->stream_res.pix_clk_params.requested_sym_clk;
+
+ memset(&link_stream->link->cur_link_settings, 0,
+ sizeof(struct dc_link_settings));
+
+ /* Find proper clock source in HDMI FRL mode for phy used for DCCG */
+ frl_phy_clock_source_id = hdmi_frl_find_matching_phypll(link);
+
+ dc->hwss.setup_hdmi_frl_link(link,
+ (pipe->stream_res.hpo_frl_stream_enc->id - ENGINE_ID_HPO_0),
+ frl_phy_clock_source_id);
+
+ FRL_INFO("FRL LINK TRAINING: Start forced link training at %d. \n",
+ link_stream->link->frl_link_settings.frl_link_rate);
+ link_stat = hdmi_frl_perform_link_training_with_retries(link_stream->link);
+
+ /* Enable FRL packet transmission */
+ if (link_stat == LINK_RESULT_SUCCESS) {
+ link_stream->link->hpo_frl_link_enc->funcs->enable_output(
+ link_stream->link->hpo_frl_link_enc);
+ if (link_stream->link->frl_flags.apply_vsdb_rcc_wa)
+ link_stream->link->hpo_frl_link_enc->funcs->apply_vsdb_rcc_wa(link_stream->link->hpo_frl_link_enc);
+ hdmi_frl_poll_start(link_stream->link->ddc);
+
+ /* Set HDMISTREAMCLK source to DTBCLK0 and bypass DTO */
+ if (dc->res_pool->dccg->funcs->set_hdmistreamclk) {
+ dc->res_pool->dccg->funcs->set_hdmistreamclk(
+ dc->res_pool->dccg,
+ DTBCLK0,
+ pipe->stream_res.tg->inst);
+ }
+
+ pipe->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_enable(
+ pipe->stream_res.hpo_frl_stream_enc,
+ pipe->stream_res.tg->inst);
+ resource_build_info_frame(pipe);
+ link_stream->ctx->dc->hwss.update_info_frame(pipe);
+
+ if (link_stream->timing.flags.DSC)
+ link_set_dsc_on_stream(pipe, true);
+
+ link_stream->ctx->dc->hwss.enable_audio_stream(pipe);
+ link_stream->ctx->dc->hwss.enable_stream(pipe);
+ link_stream->ctx->dc->hwss.unblank_stream(pipe,
+ &pipe->stream->link->cur_link_settings);
+ FRL_INFO("FRL LINK TRAINING: Forced link training successful. \n");
+ }
+ if (link_stat == LINK_RESULT_LOWER_LINKRATE) {
+ link_stream->link->frl_link_settings.frl_link_rate--;
+ if (link_stream->link->frl_link_settings.frl_link_rate >
+ HDMI_FRL_LINK_RATE_6GBPS)
+ link_stream->link->frl_link_settings.frl_num_lanes = 4;
+ else
+ link_stream->link->frl_link_settings.frl_num_lanes = 3;
+ FRL_INFO("FRL LINK TRAINING: Lower link rate = %d.\n",
+ link_stream->link->frl_link_settings.frl_link_rate);
+ }
+ if (link_stat == LINK_RESULT_FALLBACK) {
+ FRL_INFO("FRL LINK TRAINING: Forced Link Training failed. Fallback to TMDS. \n");
+ break;
+ }
+ }
+}
+
+static void update_borrow_mode_from_dsc_padding(struct dsc_padding_params *dsc_padding_params,
+ struct dc_crtc_timing *timing,
+ struct dc_hdmi_frl_link_settings *frl_link_settings)
+{
+#ifdef CONFIG_DRM_AMD_DC_FP
+ uint32_t h_active = timing->h_addressable + timing->h_border_left + timing->h_border_right;
+ uint32_t h_blank = timing->h_total - h_active;
+ struct frl_borrow_params *borrow_params = &frl_link_settings->borrow_params;
+
+ borrow_params->borrow_mode = frl_modify_borrow_mode_for_dsc_padding(timing->pix_clk_100hz,
+ h_active,
+ h_active + dsc_padding_params->dsc_hactive_padding,
+ h_blank,
+ h_blank + dsc_padding_params->dsc_htotal_padding,
+ borrow_params->hc_active_target,
+ borrow_params->hc_blank_target,
+ frl_link_settings->frl_num_lanes,
+ frl_link_settings->frl_link_rate);
+#endif
+}
+
+void hdmi_frl_decide_link_settings(struct dc_stream_state *stream,
+ struct dc_hdmi_frl_link_settings *frl_link_settings,
+ struct dsc_padding_params *dsc_padding_params)
+{
+ bool success = false;
+ struct dc_hdmi_frl_link_settings temp_settings = {0};
+
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS;
+ temp_settings.frl_num_lanes = 3;
+
+ /* Verify FRL and fill in borrow_params to verified_link_cap*/
+ frl_validate_mode_timing(
+ stream->link,
+ &stream->timing,
+ &stream->link->frl_verified_link_cap);
+
+ if (stream->link->frl_flags.force_frl_rate != 0 &&
+ stream->link->frl_flags.force_frl_rate <= stream->link->frl_verified_link_cap.frl_link_rate) {
+ switch (stream->link->frl_flags.force_frl_rate) {
+ case HDMI_FRL_LINK_RATE_3GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS;
+ temp_settings.frl_num_lanes = 3;
+ break;
+ case HDMI_FRL_LINK_RATE_6GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_6GBPS;
+ temp_settings.frl_num_lanes = 3;
+ break;
+ case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_6GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_8GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_8GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_10GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_10GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_12GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_12GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_16GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_16GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_20GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_20GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_24GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_24GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ default:
+ break;
+ }
+ *frl_link_settings = temp_settings;
+ return;
+ }
+ /*test equipment requires max rate, identified at FRL_Max = 1*/
+ if (stream->link->frl_flags.force_frl_max) {
+ *frl_link_settings = stream->link->frl_verified_link_cap;
+ return;
+ }
+ if (stream->link->frl_flags.force_frl_dsc) {
+ *frl_link_settings = stream->link->frl_verified_link_cap;
+ return;
+ }
+
+ if (stream->link->local_sink)
+ if (stream->link->local_sink->edid_caps.panel_patch.hdmi_spe_handling) {
+ *frl_link_settings = stream->link->frl_verified_link_cap;
+ return;
+ }
+
+ do {
+ success = frl_validate_mode_timing(
+ stream->link,
+ &stream->timing,
+ &temp_settings);
+ if (temp_settings.frl_link_rate ==
+ stream->link->frl_verified_link_cap.frl_link_rate)
+ break;
+ if (!success)
+ temp_settings.frl_link_rate++;
+ if (temp_settings.frl_link_rate > HDMI_FRL_LINK_RATE_6GBPS)
+ temp_settings.frl_num_lanes = 4;
+ } while (!success);
+
+ *frl_link_settings = temp_settings;
+ update_borrow_mode_from_dsc_padding(dsc_padding_params, &stream->timing, frl_link_settings);
+}
+
+void hdmi_frl_write_read_request_enable(struct ddc_service *ddc_service)
+{
+ uint8_t slave_address = HDMI_SCDC_ADDRESS;
+ uint8_t offset = HDMI_SCDC_CONFIG_0;
+ uint8_t scdc_config = 0;
+ uint8_t write_buffer[2] = {0};
+
+ link_query_ddc_data(ddc_service, slave_address, &offset,
+ sizeof(offset), &scdc_config, sizeof(scdc_config));
+
+ write_buffer[0] = HDMI_SCDC_CONFIG_0;
+ write_buffer[1] = scdc_config;
+ write_buffer[1] |= 0x1;
+
+ link_query_ddc_data(ddc_service, slave_address, write_buffer,
+ sizeof(write_buffer), NULL, 0);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h
new file mode 100644
index 000000000000..abeec56c6528
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 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
+ *
+ */
+#ifndef __LINK_HDMI_FRL_H__
+#define __LINK_HDMI_FRL_H__
+#include "link_service.h"
+enum clock_source_id hdmi_frl_find_matching_phypll(
+ struct dc_link *link);
+void hdmi_frl_LTS_clear_Update_flag(struct ddc_service *ddc_service);
+void hdmi_frl_poll_start(struct ddc_service *ddc_service);
+void hdmi_frl_LTS_clear_Link_Setting(struct ddc_service *ddc_service);
+void hdmi_frl_retrieve_link_cap(struct dc_link *link, struct dc_sink *sink);
+enum link_result hdmi_frl_perform_link_training_with_retries(
+ struct dc_link *link);
+enum link_result hdmi_frl_perform_link_training_with_fallback(
+ struct dc_link *link, struct link_resource *link_res,
+ enum clock_source_id frl_phy_clock_source_id);
+void hdmi_frl_verify_link_cap(struct dc_link *link,
+ struct dc_hdmi_frl_link_settings *known_limit_link_setting);
+void hdmi_frl_decide_link_settings(struct dc_stream_state *stream,
+ struct dc_hdmi_frl_link_settings *frl_link_settings,
+ struct dsc_padding_params *dsc_paddding_params);
+bool hdmi_frl_poll_status_flag(struct dc_link *link);
+struct dc_hdmi_frl_link_settings *hdmi_frl_get_verified_link_cap(
+ struct dc_link *link);
+void hdmi_frl_set_preferred_link_settings(struct dc *dc,
+ struct dc_hdmi_frl_link_settings *link_setting,
+ struct dc_hdmi_frl_link_training_overrides *lt_overrides,
+ struct dc_link *link);
+void hdmi_frl_write_read_request_enable(
+ struct ddc_service *ddc_service);
+#endif /* __LINK_HDMI_FRL_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c
index b157d05b67ad..cfb5ef0523a7 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c
@@ -80,6 +80,7 @@ bool program_hpd_filter(const struct dc_link *link)
case SIGNAL_TYPE_DVI_SINGLE_LINK:
case SIGNAL_TYPE_DVI_DUAL_LINK:
case SIGNAL_TYPE_HDMI_TYPE_A:
+ case SIGNAL_TYPE_HDMI_FRL:
/* Program hpd filter */
delay_on_connect_in_ms = 500;
delay_on_disconnect_in_ms = 100;