Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,67 @@
#
# 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.
#
# Makefile for the link sub-component of DAL.
# It abstracts the control and status of back end pipe such as DIO, HPO, DPIA,
# PHY, HPD, DDC and etc).
LINK = link_detection.o link_dpms.o link_factory.o link_resource.o \
link_validation.o
AMD_DAL_LINK = $(addprefix $(AMDDALPATH)/dc/link/, \
$(LINK))
AMD_DISPLAY_FILES += $(AMD_DAL_LINK)
###############################################################################
# accessories
###############################################################################
LINK_ACCESSORIES = link_dp_trace.o link_dp_cts.o
AMD_DAL_LINK_ACCESSORIES = $(addprefix $(AMDDALPATH)/dc/link/accessories/, \
$(LINK_ACCESSORIES))
AMD_DISPLAY_FILES += $(AMD_DAL_LINK_ACCESSORIES)
###############################################################################
# hwss
###############################################################################
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))
AMD_DISPLAY_FILES += $(AMD_DAL_LINK_HWSS)
###############################################################################
# protocols
###############################################################################
LINK_PROTOCOLS = link_hpd.o link_ddc.o link_dpcd.o link_dp_dpia.o \
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))
AMD_DISPLAY_FILES += $(AMD_DAL_LINK_PROTOCOLS)
@@ -0,0 +1,995 @@
/*
* 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_dp_cts.h"
#include "link/link_resource.h"
#include "link/protocols/link_dpcd.h"
#include "link/protocols/link_dp_training.h"
#include "link/protocols/link_dp_phy.h"
#include "link/protocols/link_dp_training_fixed_vs_pe_retimer.h"
#include "link/protocols/link_dp_capability.h"
#include "link/link_dpms.h"
#include "resource.h"
#include "dm_helpers.h"
#include "dc_dmub_srv.h"
#include "dce/dmub_hw_lock_mgr.h"
#include "clk_mgr.h"
#define DC_LOGGER \
link->ctx->logger
static enum dc_link_rate get_link_rate_from_test_link_rate(uint8_t test_rate)
{
switch (test_rate) {
case DP_TEST_LINK_RATE_RBR:
return LINK_RATE_LOW;
case DP_TEST_LINK_RATE_HBR:
return LINK_RATE_HIGH;
case DP_TEST_LINK_RATE_HBR2:
return LINK_RATE_HIGH2;
case DP_TEST_LINK_RATE_HBR3:
return LINK_RATE_HIGH3;
case DP_TEST_LINK_RATE_UHBR10:
return LINK_RATE_UHBR10;
case DP_TEST_LINK_RATE_UHBR20:
return LINK_RATE_UHBR20;
case DP_TEST_LINK_RATE_UHBR13_5_LEGACY:
case DP_TEST_LINK_RATE_UHBR13_5:
return LINK_RATE_UHBR13_5;
default:
return LINK_RATE_UNKNOWN;
}
}
static void dp_retrain_link_dp_test(struct dc_link *link,
struct dc_link_settings *link_setting,
bool skip_video_pattern)
{
(void)skip_video_pattern;
struct pipe_ctx *pipes[MAX_PIPES];
struct dc_state *state = link->dc->current_state;
struct dc_stream_update stream_update = { 0 };
bool dpms_off = false;
bool needs_divider_update = false;
bool was_hpo_acquired = resource_is_hpo_acquired(link->dc->current_state);
bool is_hpo_acquired;
uint8_t count;
int i;
struct audio_output audio_output[MAX_PIPES];
struct dc_stream_state *streams_on_link[MAX_PIPES];
int num_streams_on_link = 0;
struct dc *dc = (struct dc *)link->dc;
needs_divider_update = (link->dc->link_srv->dp_get_encoding_format(link_setting) !=
link->dc->link_srv->dp_get_encoding_format((const struct dc_link_settings *) &link->cur_link_settings))
|| link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA;
udelay(100);
link_get_master_pipes_with_dpms_on(link, state, &count, pipes);
for (i = 0; i < count; i++) {
link_set_dpms_off(pipes[i]);
pipes[i]->link_config.dp_link_settings = *link_setting;
update_dp_encoder_resources_for_test_harness(
link->dc,
state,
pipes[i]);
// Disable OTG and re-enable after updating clocks
pipes[i]->stream_res.tg->funcs->disable_crtc(pipes[i]->stream_res.tg);
}
if (needs_divider_update && link->dc->res_pool->funcs->update_dc_state_for_encoder_switch) {
link->dc->res_pool->funcs->update_dc_state_for_encoder_switch(link,
link_setting, count,
*pipes, &audio_output[0]);
for (i = 0; i < count; i++) {
pipes[i]->clock_source->funcs->program_pix_clk(
pipes[i]->clock_source,
&pipes[i]->stream_res.pix_clk_params,
link->dc->link_srv->dp_get_encoding_format(&pipes[i]->link_config.dp_link_settings),
&pipes[i]->pll_settings);
if (pipes[i]->stream_res.audio != NULL) {
const struct link_hwss *link_hwss = get_link_hwss(
link, &pipes[i]->link_res);
link_hwss->setup_audio_output(pipes[i], &audio_output[i],
pipes[i]->stream_res.audio->inst);
pipes[i]->stream_res.audio->funcs->az_configure(
pipes[i]->stream_res.audio,
pipes[i]->stream->signal,
&audio_output[i].crtc_info,
&pipes[i]->stream->audio_info,
&audio_output[i].dp_link_info);
if (link->dc->config.disable_hbr_audio_dp2 &&
pipes[i]->stream_res.audio->funcs->az_disable_hbr_audio &&
link->dc->link_srv->dp_is_128b_132b_signal(pipes[i]))
pipes[i]->stream_res.audio->funcs->az_disable_hbr_audio(pipes[i]->stream_res.audio);
}
}
}
// Toggle on HPO I/O if necessary
is_hpo_acquired = resource_is_hpo_acquired(state);
if (was_hpo_acquired != is_hpo_acquired && link->dc->hwss.setup_hpo_hw_control)
link->dc->hwss.setup_hpo_hw_control(link->dc->hwseq, is_hpo_acquired);
for (i = 0; i < count; i++)
pipes[i]->stream_res.tg->funcs->enable_crtc(pipes[i]->stream_res.tg);
// 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];
}
for (i = 0; i < num_streams_on_link; i++) {
if (streams_on_link[i] && streams_on_link[i]->link && streams_on_link[i]->link == link) {
stream_update.stream = streams_on_link[i];
stream_update.dpms_off = &dpms_off;
dc_update_planes_and_stream(dc, NULL, 0, streams_on_link[i], &stream_update);
}
}
}
static void dp_test_send_link_training(struct dc_link *link)
{
struct dc_link_settings link_settings = {0};
uint8_t test_rate = 0;
core_link_read_dpcd(
link,
DP_TEST_LANE_COUNT,
(unsigned char *)(&link_settings.lane_count),
1);
core_link_read_dpcd(
link,
DP_TEST_LINK_RATE,
&test_rate,
1);
link_settings.link_rate = get_link_rate_from_test_link_rate(test_rate);
if (link_settings.link_rate == LINK_RATE_UNKNOWN) {
DC_LOG_ERROR("%s: Invalid test link rate.", __func__);
ASSERT(0);
}
/* Set preferred link settings */
link->verified_link_cap.lane_count = link_settings.lane_count;
link->verified_link_cap.link_rate = link_settings.link_rate;
dp_retrain_link_dp_test(link, &link_settings, false);
}
static void dp_test_get_audio_test_data(struct dc_link *link, bool disable_video)
{
union audio_test_mode dpcd_test_mode = {0};
struct audio_test_pattern_type dpcd_pattern_type = {0};
union audio_test_pattern_period dpcd_pattern_period[AUDIO_CHANNELS_COUNT] = {0};
enum dp_test_pattern test_pattern = DP_TEST_PATTERN_AUDIO_OPERATOR_DEFINED;
struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx;
struct pipe_ctx *pipe_ctx = &pipes[0];
unsigned int channel_count;
unsigned int channel = 0;
unsigned int modes = 0;
// get audio test mode and test pattern parameters
core_link_read_dpcd(
link,
DP_TEST_AUDIO_MODE,
&dpcd_test_mode.raw,
sizeof(dpcd_test_mode));
core_link_read_dpcd(
link,
DP_TEST_AUDIO_PATTERN_TYPE,
&dpcd_pattern_type.value,
sizeof(dpcd_pattern_type));
channel_count = min(dpcd_test_mode.bits.channel_count + 1, AUDIO_CHANNELS_COUNT);
// read pattern periods for requested channels when sawTooth pattern is requested
if (dpcd_pattern_type.value == AUDIO_TEST_PATTERN_SAWTOOTH ||
dpcd_pattern_type.value == AUDIO_TEST_PATTERN_OPERATOR_DEFINED) {
test_pattern = (dpcd_pattern_type.value == AUDIO_TEST_PATTERN_SAWTOOTH) ?
DP_TEST_PATTERN_AUDIO_SAWTOOTH : DP_TEST_PATTERN_AUDIO_OPERATOR_DEFINED;
// read period for each channel
for (channel = 0; channel < channel_count; channel++) {
core_link_read_dpcd(
link,
DP_TEST_AUDIO_PERIOD_CH1 + channel,
&dpcd_pattern_period[channel].raw,
sizeof(dpcd_pattern_period[channel]));
}
}
link->audio_test_data.flags.test_requested = 1;
link->audio_test_data.flags.disable_video = disable_video;
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) {
for (modes = 0; modes < pipe_ctx->stream->audio_info.mode_count; modes++) {
link->audio_test_data.pattern_period[modes] = dpcd_pattern_period[modes].bits.pattern_period;
}
}
}
/* TODO Raven hbr2 compliance eye output is unstable
* (toggling on and off) with debugger break
* This caueses intermittent PHY automation failure
* Need to look into the root cause */
static void dp_test_send_phy_test_pattern(struct dc_link *link)
{
union phy_test_pattern dpcd_test_pattern;
union lane_adjust dpcd_lane_adjustment[2];
unsigned char dpcd_post_cursor_2_adjustment = 0;
unsigned char test_pattern_buffer[
(DP_TEST_264BIT_CUSTOM_PATTERN_263_256 -
DP_TEST_264BIT_CUSTOM_PATTERN_7_0)+1] = {0};
unsigned int test_pattern_size = 0;
enum dp_test_pattern test_pattern;
union lane_adjust dpcd_lane_adjust;
unsigned int lane;
struct link_training_settings link_training_settings;
unsigned char no_preshoot = 0;
unsigned char no_deemphasis = 0;
dpcd_test_pattern.raw = 0;
memset(dpcd_lane_adjustment, 0, sizeof(dpcd_lane_adjustment));
memset(&link_training_settings, 0, sizeof(link_training_settings));
/* get phy test pattern and pattern parameters from DP receiver */
core_link_read_dpcd(
link,
DP_PHY_TEST_PATTERN,
&dpcd_test_pattern.raw,
sizeof(dpcd_test_pattern));
core_link_read_dpcd(
link,
DP_ADJUST_REQUEST_LANE0_1,
&dpcd_lane_adjustment[0].raw,
sizeof(dpcd_lane_adjustment));
/* prepare link training settings */
link_training_settings.link_settings = link->cur_link_settings;
link_training_settings.lttpr_mode = dp_decide_lttpr_mode(link, &link->cur_link_settings);
if (((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) &&
link_training_settings.lttpr_mode == LTTPR_MODE_TRANSPARENT)
dp_fixed_vs_pe_read_lane_adjust(
link,
link_training_settings.dpcd_lane_settings);
/*get post cursor 2 parameters
* For DP 1.1a or eariler, this DPCD register's value is 0
* For DP 1.2 or later:
* Bits 1:0 = POST_CURSOR2_LANE0; Bits 3:2 = POST_CURSOR2_LANE1
* Bits 5:4 = POST_CURSOR2_LANE2; Bits 7:6 = POST_CURSOR2_LANE3
*/
core_link_read_dpcd(
link,
DP_ADJUST_REQUEST_POST_CURSOR2,
&dpcd_post_cursor_2_adjustment,
sizeof(dpcd_post_cursor_2_adjustment));
/* translate request */
switch (dpcd_test_pattern.bits.PATTERN) {
case PHY_TEST_PATTERN_D10_2:
test_pattern = DP_TEST_PATTERN_D102;
break;
case PHY_TEST_PATTERN_SYMBOL_ERROR:
test_pattern = DP_TEST_PATTERN_SYMBOL_ERROR;
break;
case PHY_TEST_PATTERN_PRBS7:
test_pattern = DP_TEST_PATTERN_PRBS7;
break;
case PHY_TEST_PATTERN_80BIT_CUSTOM:
test_pattern = DP_TEST_PATTERN_80BIT_CUSTOM;
break;
case PHY_TEST_PATTERN_CP2520_1:
/* CP2520 pattern is unstable, temporarily use TPS4 instead */
test_pattern = (link->dc->caps.force_dp_tps4_for_cp2520 == 1) ?
DP_TEST_PATTERN_TRAINING_PATTERN4 :
DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE;
break;
case PHY_TEST_PATTERN_CP2520_2:
/* CP2520 pattern is unstable, temporarily use TPS4 instead */
test_pattern = (link->dc->caps.force_dp_tps4_for_cp2520 == 1) ?
DP_TEST_PATTERN_TRAINING_PATTERN4 :
DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE;
break;
case PHY_TEST_PATTERN_CP2520_3:
test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN4;
break;
case PHY_TEST_PATTERN_128b_132b_TPS1:
test_pattern = DP_TEST_PATTERN_128b_132b_TPS1;
break;
case PHY_TEST_PATTERN_128b_132b_TPS2:
test_pattern = DP_TEST_PATTERN_128b_132b_TPS2;
break;
case PHY_TEST_PATTERN_PRBS9:
test_pattern = DP_TEST_PATTERN_PRBS9;
break;
case PHY_TEST_PATTERN_PRBS11:
test_pattern = DP_TEST_PATTERN_PRBS11;
break;
case PHY_TEST_PATTERN_PRBS15:
test_pattern = DP_TEST_PATTERN_PRBS15;
break;
case PHY_TEST_PATTERN_PRBS23:
test_pattern = DP_TEST_PATTERN_PRBS23;
break;
case PHY_TEST_PATTERN_PRBS31:
test_pattern = DP_TEST_PATTERN_PRBS31;
break;
case PHY_TEST_PATTERN_264BIT_CUSTOM:
test_pattern = DP_TEST_PATTERN_264BIT_CUSTOM;
break;
case PHY_TEST_PATTERN_SQUARE:
test_pattern = DP_TEST_PATTERN_SQUARE;
break;
case PHY_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED:
test_pattern = DP_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED;
no_preshoot = 1;
break;
case PHY_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED:
test_pattern = DP_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED;
no_deemphasis = 1;
break;
case PHY_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED:
test_pattern = DP_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED;
no_preshoot = 1;
no_deemphasis = 1;
break;
default:
test_pattern = DP_TEST_PATTERN_VIDEO_MODE;
break;
}
if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM) {
test_pattern_size = (DP_TEST_80BIT_CUSTOM_PATTERN_79_72 -
DP_TEST_80BIT_CUSTOM_PATTERN_7_0) + 1;
core_link_read_dpcd(
link,
DP_TEST_80BIT_CUSTOM_PATTERN_7_0,
test_pattern_buffer,
test_pattern_size);
}
if (IS_DP_PHY_SQUARE_PATTERN(test_pattern)) {
test_pattern_size = 1; // Square pattern data is 1 byte (DP spec)
core_link_read_dpcd(
link,
DP_PHY_SQUARE_PATTERN,
test_pattern_buffer,
test_pattern_size);
}
if (test_pattern == DP_TEST_PATTERN_264BIT_CUSTOM) {
test_pattern_size = (DP_TEST_264BIT_CUSTOM_PATTERN_263_256-
DP_TEST_264BIT_CUSTOM_PATTERN_7_0) + 1;
core_link_read_dpcd(
link,
DP_TEST_264BIT_CUSTOM_PATTERN_7_0,
test_pattern_buffer,
test_pattern_size);
}
for (lane = 0; lane <
(unsigned int)(link->cur_link_settings.lane_count);
lane++) {
dpcd_lane_adjust.raw =
dp_get_nibble_at_index(&dpcd_lane_adjustment[0].raw, lane);
if (link_dp_get_encoding_format(&link->cur_link_settings) ==
DP_8b_10b_ENCODING) {
link_training_settings.hw_lane_settings[lane].VOLTAGE_SWING =
(enum dc_voltage_swing)
(dpcd_lane_adjust.bits.VOLTAGE_SWING_LANE);
link_training_settings.hw_lane_settings[lane].PRE_EMPHASIS =
(enum dc_pre_emphasis)
(dpcd_lane_adjust.bits.PRE_EMPHASIS_LANE);
link_training_settings.hw_lane_settings[lane].POST_CURSOR2 =
(enum dc_post_cursor2)
((dpcd_post_cursor_2_adjustment >> (lane * 2)) & 0x03);
} else if (link_dp_get_encoding_format(&link->cur_link_settings) ==
DP_128b_132b_ENCODING) {
link_training_settings.hw_lane_settings[lane].FFE_PRESET.settings.level =
dpcd_lane_adjust.tx_ffe.PRESET_VALUE;
link_training_settings.hw_lane_settings[lane].FFE_PRESET.settings.no_preshoot = no_preshoot;
link_training_settings.hw_lane_settings[lane].FFE_PRESET.settings.no_deemphasis = no_deemphasis;
}
}
dp_hw_to_dpcd_lane_settings(&link_training_settings,
link_training_settings.hw_lane_settings,
link_training_settings.dpcd_lane_settings);
/*Usage: Measure DP physical lane signal
* by DP SI test equipment automatically.
* PHY test pattern request is generated by equipment via HPD interrupt.
* HPD needs to be active all the time. HPD should be active
* all the time. Do not touch it.
* forward request to DS
*/
dp_set_test_pattern(
link,
test_pattern,
DP_TEST_PATTERN_COLOR_SPACE_UNDEFINED,
&link_training_settings,
test_pattern_buffer,
test_pattern_size);
}
static void set_crtc_test_pattern(struct dc_link *link,
struct pipe_ctx *pipe_ctx,
enum dp_test_pattern test_pattern,
enum dp_test_pattern_color_space test_pattern_color_space)
{
(void)test_pattern_color_space;
enum controller_dp_test_pattern controller_test_pattern;
enum dc_color_depth color_depth = pipe_ctx->
stream->timing.display_color_depth;
struct bit_depth_reduction_params params;
struct output_pixel_processor *opp = pipe_ctx->stream_res.opp;
struct pipe_ctx *odm_pipe;
struct test_pattern_params *tp_params;
memset(&params, 0, sizeof(params));
resource_build_test_pattern_params(&link->dc->current_state->res_ctx,
pipe_ctx);
controller_test_pattern = pipe_ctx->stream_res.test_pattern_params.test_pattern;
switch (test_pattern) {
case DP_TEST_PATTERN_COLOR_SQUARES:
case DP_TEST_PATTERN_COLOR_SQUARES_CEA:
case DP_TEST_PATTERN_VERTICAL_BARS:
case DP_TEST_PATTERN_HORIZONTAL_BARS:
case DP_TEST_PATTERN_COLOR_RAMP:
{
/* disable bit depth reduction */
pipe_ctx->stream->bit_depth_params = params;
if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) {
opp->funcs->opp_program_bit_depth_reduction(opp, &params);
pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg,
controller_test_pattern, color_depth);
} else if (link->dc->hwss.set_disp_pattern_generator) {
enum controller_dp_color_space controller_color_space;
struct output_pixel_processor *odm_opp;
controller_color_space = pipe_ctx->stream_res.test_pattern_params.color_space;
if (controller_color_space == CONTROLLER_DP_COLOR_SPACE_UDEFINED) {
DC_LOG_ERROR("%s: Color space must be defined for test pattern", __func__);
ASSERT(0);
}
odm_pipe = pipe_ctx;
while (odm_pipe) {
tp_params = &odm_pipe->stream_res.test_pattern_params;
odm_opp = odm_pipe->stream_res.opp;
odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, &params);
link->dc->hwss.set_disp_pattern_generator(link->dc,
odm_pipe,
tp_params->test_pattern,
tp_params->color_space,
tp_params->color_depth,
NULL,
tp_params->width,
tp_params->height,
tp_params->offset);
odm_pipe = odm_pipe->next_odm_pipe;
}
}
}
break;
case DP_TEST_PATTERN_VIDEO_MODE:
{
/* restore bitdepth reduction */
resource_build_bit_depth_reduction_params(pipe_ctx->stream, &params);
pipe_ctx->stream->bit_depth_params = params;
if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) {
opp->funcs->opp_program_bit_depth_reduction(opp, &params);
pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg,
CONTROLLER_DP_TEST_PATTERN_VIDEOMODE,
color_depth);
} else if (link->dc->hwss.set_disp_pattern_generator) {
struct output_pixel_processor *odm_opp;
odm_pipe = pipe_ctx;
while (odm_pipe) {
tp_params = &odm_pipe->stream_res.test_pattern_params;
odm_opp = odm_pipe->stream_res.opp;
odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, &params);
link->dc->hwss.set_disp_pattern_generator(link->dc,
odm_pipe,
tp_params->test_pattern,
tp_params->color_space,
tp_params->color_depth,
NULL,
tp_params->width,
tp_params->height,
tp_params->offset);
odm_pipe = odm_pipe->next_odm_pipe;
}
}
}
break;
default:
break;
}
}
void dp_handle_automated_test(struct dc_link *link)
{
union test_request test_request;
union test_response test_response;
memset(&test_request, 0, sizeof(test_request));
memset(&test_response, 0, sizeof(test_response));
core_link_read_dpcd(
link,
DP_TEST_REQUEST,
&test_request.raw,
sizeof(union test_request));
if (test_request.bits.LINK_TRAINING) {
/* ACK first to let DP RX test box monitor LT sequence */
test_response.bits.ACK = 1;
core_link_write_dpcd(
link,
DP_TEST_RESPONSE,
&test_response.raw,
sizeof(test_response));
dp_test_send_link_training(link);
/* no acknowledge request is needed again */
test_response.bits.ACK = 0;
}
if (test_request.bits.LINK_TEST_PATTRN) {
union test_misc dpcd_test_params;
union link_test_pattern dpcd_test_pattern;
memset(&dpcd_test_pattern, 0, sizeof(dpcd_test_pattern));
memset(&dpcd_test_params, 0, sizeof(dpcd_test_params));
/* get link test pattern and pattern parameters */
core_link_read_dpcd(
link,
DP_TEST_PATTERN,
&dpcd_test_pattern.raw,
sizeof(dpcd_test_pattern));
core_link_read_dpcd(
link,
DP_TEST_MISC0,
&dpcd_test_params.raw,
sizeof(dpcd_test_params));
test_response.bits.ACK = dm_helpers_dp_handle_test_pattern_request(link->ctx, link,
dpcd_test_pattern, dpcd_test_params) ? 1 : 0;
}
if (test_request.bits.AUDIO_TEST_PATTERN) {
dp_test_get_audio_test_data(link, test_request.bits.TEST_AUDIO_DISABLED_VIDEO);
test_response.bits.ACK = 1;
}
if (test_request.bits.PHY_TEST_PATTERN) {
dp_test_send_phy_test_pattern(link);
test_response.bits.ACK = 1;
}
/* send request acknowledgment */
if (test_response.bits.ACK)
core_link_write_dpcd(
link,
DP_TEST_RESPONSE,
&test_response.raw,
sizeof(test_response));
}
bool dp_set_test_pattern(
struct dc_link *link,
enum dp_test_pattern test_pattern,
enum dp_test_pattern_color_space test_pattern_color_space,
const struct link_training_settings *p_link_settings,
const unsigned char *p_custom_pattern,
unsigned int cust_pattern_size)
{
const struct link_hwss *link_hwss;
struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx;
struct pipe_ctx *pipe_ctx = NULL;
unsigned int lane;
unsigned int i;
unsigned char link_qual_pattern[LANE_COUNT_DP_MAX] = {0};
union dpcd_training_pattern training_pattern;
enum dpcd_phy_test_patterns pattern;
memset(&training_pattern, 0, sizeof(training_pattern));
for (i = 0; i < MAX_PIPES; i++) {
if (pipes[i].stream == NULL)
continue;
if (resource_is_pipe_type(&pipes[i], OTG_MASTER) &&
pipes[i].stream->link == link) {
pipe_ctx = &pipes[i];
break;
}
}
if (pipe_ctx == NULL)
return false;
link->pending_test_pattern = test_pattern;
/* Reset CRTC Test Pattern if it is currently running and request is VideoMode */
if (link->test_pattern_enabled && test_pattern ==
DP_TEST_PATTERN_VIDEO_MODE) {
/* Set CRTC Test Pattern */
set_crtc_test_pattern(link, pipe_ctx, test_pattern, test_pattern_color_space);
dp_set_hw_test_pattern(link, &pipe_ctx->link_res, test_pattern,
(uint8_t *)p_custom_pattern,
(uint32_t)cust_pattern_size);
/* Unblank Stream */
link->dc->hwss.unblank_stream(
pipe_ctx,
&link->verified_link_cap);
/* TODO:m_pHwss->MuteAudioEndpoint
* (pPathMode->pDisplayPath, false);
*/
/* Reset Test Pattern state */
link->test_pattern_enabled = false;
link->current_test_pattern = test_pattern;
link->pending_test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
return true;
}
/* Check for PHY Test Patterns */
if (IS_DP_PHY_PATTERN(test_pattern)) {
/* Set DPCD Lane Settings before running test pattern */
if (p_link_settings != NULL) {
if (((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) &&
p_link_settings->lttpr_mode == LTTPR_MODE_TRANSPARENT) {
dp_fixed_vs_pe_set_retimer_lane_settings(
link,
p_link_settings->dpcd_lane_settings,
p_link_settings->link_settings.lane_count);
} else {
dp_set_hw_lane_settings(link, &pipe_ctx->link_res, p_link_settings, DPRX);
}
dpcd_set_lane_settings(link, p_link_settings, DPRX);
}
/* Blank stream if running test pattern */
if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) {
/*TODO:
* m_pHwss->
* MuteAudioEndpoint(pPathMode->pDisplayPath, true);
*/
/* Blank stream */
link->dc->hwss.blank_stream(pipe_ctx);
}
dp_set_hw_test_pattern(link, &pipe_ctx->link_res, test_pattern,
(uint8_t *)p_custom_pattern,
(uint32_t)cust_pattern_size);
if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) {
/* Set Test Pattern state */
link->test_pattern_enabled = true;
link->current_test_pattern = test_pattern;
link->pending_test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
if (p_link_settings != NULL)
dpcd_set_link_settings(link,
p_link_settings);
}
switch (test_pattern) {
case DP_TEST_PATTERN_VIDEO_MODE:
pattern = PHY_TEST_PATTERN_NONE;
break;
case DP_TEST_PATTERN_D102:
pattern = PHY_TEST_PATTERN_D10_2;
break;
case DP_TEST_PATTERN_SYMBOL_ERROR:
pattern = PHY_TEST_PATTERN_SYMBOL_ERROR;
break;
case DP_TEST_PATTERN_PRBS7:
pattern = PHY_TEST_PATTERN_PRBS7;
break;
case DP_TEST_PATTERN_80BIT_CUSTOM:
pattern = PHY_TEST_PATTERN_80BIT_CUSTOM;
break;
case DP_TEST_PATTERN_CP2520_1:
pattern = PHY_TEST_PATTERN_CP2520_1;
break;
case DP_TEST_PATTERN_CP2520_2:
pattern = PHY_TEST_PATTERN_CP2520_2;
break;
case DP_TEST_PATTERN_CP2520_3:
pattern = PHY_TEST_PATTERN_CP2520_3;
break;
case DP_TEST_PATTERN_128b_132b_TPS1:
pattern = PHY_TEST_PATTERN_128b_132b_TPS1;
break;
case DP_TEST_PATTERN_128b_132b_TPS2:
pattern = PHY_TEST_PATTERN_128b_132b_TPS2;
break;
case DP_TEST_PATTERN_PRBS9:
pattern = PHY_TEST_PATTERN_PRBS9;
break;
case DP_TEST_PATTERN_PRBS11:
pattern = PHY_TEST_PATTERN_PRBS11;
break;
case DP_TEST_PATTERN_PRBS15:
pattern = PHY_TEST_PATTERN_PRBS15;
break;
case DP_TEST_PATTERN_PRBS23:
pattern = PHY_TEST_PATTERN_PRBS23;
break;
case DP_TEST_PATTERN_PRBS31:
pattern = PHY_TEST_PATTERN_PRBS31;
break;
case DP_TEST_PATTERN_264BIT_CUSTOM:
pattern = PHY_TEST_PATTERN_264BIT_CUSTOM;
break;
case DP_TEST_PATTERN_SQUARE:
pattern = PHY_TEST_PATTERN_SQUARE;
break;
case DP_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED:
pattern = PHY_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED;
break;
case DP_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED:
pattern = PHY_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED;
break;
case DP_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED:
pattern = PHY_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED;
break;
default:
return false;
}
if (test_pattern == DP_TEST_PATTERN_VIDEO_MODE
/*TODO:&& !pPathMode->pDisplayPath->IsTargetPoweredOn()*/)
return false;
if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) {
if (IS_DP_PHY_SQUARE_PATTERN(test_pattern))
core_link_write_dpcd(link,
DP_LINK_SQUARE_PATTERN,
p_custom_pattern,
1);
/* tell receiver that we are sending qualification
* pattern DP 1.2 or later - DP receiver's link quality
* pattern is set using DPCD LINK_QUAL_LANEx_SET
* register (0x10B~0x10E)\
*/
for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++)
link_qual_pattern[lane] =
(unsigned char)(pattern);
core_link_write_dpcd(link,
DP_LINK_QUAL_LANE0_SET,
link_qual_pattern,
sizeof(link_qual_pattern));
} else if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_10 ||
link->dpcd_caps.dpcd_rev.raw == 0) {
/* tell receiver that we are sending qualification
* pattern DP 1.1a or earlier - DP receiver's link
* quality pattern is set using
* DPCD TRAINING_PATTERN_SET -> LINK_QUAL_PATTERN_SET
* register (0x102). We will use v_1.3 when we are
* setting test pattern for DP 1.1.
*/
core_link_read_dpcd(link, DP_TRAINING_PATTERN_SET,
&training_pattern.raw,
sizeof(training_pattern));
training_pattern.v1_3.LINK_QUAL_PATTERN_SET = pattern;
core_link_write_dpcd(link, DP_TRAINING_PATTERN_SET,
&training_pattern.raw,
sizeof(training_pattern));
}
} else {
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
switch (test_pattern_color_space) {
case DP_TEST_PATTERN_COLOR_SPACE_RGB:
color_space = COLOR_SPACE_SRGB;
if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA)
color_space = COLOR_SPACE_SRGB_LIMITED;
break;
case DP_TEST_PATTERN_COLOR_SPACE_YCBCR601:
color_space = COLOR_SPACE_YCBCR601;
if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA)
color_space = COLOR_SPACE_YCBCR601_LIMITED;
break;
case DP_TEST_PATTERN_COLOR_SPACE_YCBCR709:
color_space = COLOR_SPACE_YCBCR709;
if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA)
color_space = COLOR_SPACE_YCBCR709_LIMITED;
break;
default:
break;
}
if (!pipe_ctx->stream)
return false;
if (pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_enable) {
if (should_use_dmub_inbox1_lock(pipe_ctx->stream->link->dc, pipe_ctx->stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
hw_locks.bits.lock_dig = 1;
inst_flags.dig_inst = (uint8_t)pipe_ctx->stream_res.tg->inst;
dmub_hw_lock_mgr_cmd(link->ctx->dmub_srv,
true,
&hw_locks,
&inst_flags);
} else
pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_enable(
pipe_ctx->stream_res.tg);
}
pipe_ctx->stream_res.tg->funcs->lock(pipe_ctx->stream_res.tg);
/* update MSA to requested color space */
link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
pipe_ctx->stream->output_color_space = color_space;
link_hwss->setup_stream_attribute(pipe_ctx);
if (pipe_ctx->stream->use_vsc_sdp_for_colorimetry) {
if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA)
pipe_ctx->stream->vsc_infopacket.sb[17] |= (1 << 7); // sb17 bit 7 Dynamic Range: 0 = VESA range, 1 = CTA range
else
pipe_ctx->stream->vsc_infopacket.sb[17] &= ~(1 << 7);
if (color_space == COLOR_SPACE_YCBCR601_LIMITED)
pipe_ctx->stream->vsc_infopacket.sb[16] &= 0xf0;
else if (color_space == COLOR_SPACE_YCBCR709_LIMITED)
pipe_ctx->stream->vsc_infopacket.sb[16] |= 1;
resource_build_info_frame(pipe_ctx);
link->dc->hwss.update_info_frame(pipe_ctx);
}
/* CRTC Patterns */
set_crtc_test_pattern(link, pipe_ctx, test_pattern, test_pattern_color_space);
pipe_ctx->stream_res.tg->funcs->unlock(pipe_ctx->stream_res.tg);
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg,
CRTC_STATE_VACTIVE);
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg,
CRTC_STATE_VBLANK);
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg,
CRTC_STATE_VACTIVE);
if (pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_disable) {
if (should_use_dmub_inbox1_lock(pipe_ctx->stream->link->dc, pipe_ctx->stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
hw_locks.bits.lock_dig = 1;
inst_flags.dig_inst = (uint8_t)pipe_ctx->stream_res.tg->inst;
dmub_hw_lock_mgr_cmd(link->ctx->dmub_srv,
false,
&hw_locks,
&inst_flags);
} else
pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_disable(
pipe_ctx->stream_res.tg);
}
/* Set Test Pattern state */
link->test_pattern_enabled = true;
link->current_test_pattern = test_pattern;
link->pending_test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
}
return true;
}
void dp_set_preferred_link_settings(struct dc *dc,
struct dc_link_settings *link_setting,
struct dc_link *link)
{
int i;
struct pipe_ctx *pipe;
struct dc_stream_state *link_stream = 0;
struct dc_link_settings store_settings = *link_setting;
link->preferred_link_setting = store_settings;
/* Retrain with preferred link settings only relevant for
* DP signal type
* Check for non-DP signal or if passive dongle present
*/
if (!dc_is_dp_signal(link->connector_signal) ||
link->dongle_max_pix_clk > 0)
return;
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;
break;
}
}
}
/* Stream not found */
if (i == MAX_PIPES)
return;
/* Cannot retrain link if backend is off */
if (link_stream->dpms_off)
return;
if (link_decide_link_settings(link_stream, &store_settings))
dp_retrain_link_dp_test(link, &store_settings, false);
}
void dp_set_preferred_training_settings(struct dc *dc,
struct dc_link_settings *link_setting,
struct dc_link_training_overrides *lt_overrides,
struct dc_link *link,
bool skip_immediate_retrain)
{
if (lt_overrides != NULL)
link->preferred_training_settings = *lt_overrides;
else
memset(&link->preferred_training_settings, 0, sizeof(link->preferred_training_settings));
if (link_setting != NULL) {
link->preferred_link_setting = *link_setting;
} else {
link->preferred_link_setting.lane_count = LANE_COUNT_UNKNOWN;
link->preferred_link_setting.link_rate = LINK_RATE_UNKNOWN;
}
if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT &&
link->type == dc_connection_mst_branch)
dm_helpers_dp_mst_update_branch_bandwidth(dc->ctx, link);
/* Retrain now, or wait until next stream update to apply */
if (skip_immediate_retrain == false)
dp_set_preferred_link_settings(dc, &link->preferred_link_setting, link);
}
@@ -0,0 +1,44 @@
/*
* 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_DP_CTS_H__
#define __LINK_DP_CTS_H__
#include "link_service.h"
void dp_handle_automated_test(struct dc_link *link);
bool dp_set_test_pattern(
struct dc_link *link,
enum dp_test_pattern test_pattern,
enum dp_test_pattern_color_space test_pattern_color_space,
const struct link_training_settings *p_link_settings,
const unsigned char *p_custom_pattern,
unsigned int cust_pattern_size);
void dp_set_preferred_link_settings(struct dc *dc,
struct dc_link_settings *link_setting,
struct dc_link *link);
void dp_set_preferred_training_settings(struct dc *dc,
struct dc_link_settings *link_setting,
struct dc_link_training_overrides *lt_overrides,
struct dc_link *link,
bool skip_immediate_retrain);
#endif /* __LINK_DP_CTS_H__ */
@@ -0,0 +1,172 @@
/*
* 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_dp_trace.h"
#include "link/protocols/link_dpcd.h"
void dp_trace_init(struct dc_link *link)
{
memset(&link->dp_trace, 0, sizeof(link->dp_trace));
link->dp_trace.is_initialized = true;
}
void dp_trace_reset(struct dc_link *link)
{
memset(&link->dp_trace, 0, sizeof(link->dp_trace));
}
bool dp_trace_is_initialized(struct dc_link *link)
{
return link->dp_trace.is_initialized;
}
void dp_trace_detect_lt_init(struct dc_link *link)
{
memset(&link->dp_trace.detect_lt_trace, 0, sizeof(link->dp_trace.detect_lt_trace));
}
void dp_trace_commit_lt_init(struct dc_link *link)
{
memset(&link->dp_trace.commit_lt_trace, 0, sizeof(link->dp_trace.commit_lt_trace));
}
void dp_trace_link_loss_increment(struct dc_link *link)
{
link->dp_trace.link_loss_count++;
}
void dp_trace_lt_fail_count_update(struct dc_link *link,
unsigned int fail_count,
bool in_detection)
{
if (in_detection)
link->dp_trace.detect_lt_trace.counts.fail = fail_count;
else
link->dp_trace.commit_lt_trace.counts.fail = fail_count;
}
void dp_trace_lt_total_count_increment(struct dc_link *link,
bool in_detection)
{
if (in_detection)
link->dp_trace.detect_lt_trace.counts.total++;
else
link->dp_trace.commit_lt_trace.counts.total++;
}
void dp_trace_set_is_logged_flag(struct dc_link *link,
bool in_detection,
bool is_logged)
{
if (in_detection)
link->dp_trace.detect_lt_trace.is_logged = is_logged;
else
link->dp_trace.commit_lt_trace.is_logged = is_logged;
}
bool dp_trace_is_logged(struct dc_link *link, bool in_detection)
{
if (in_detection)
return link->dp_trace.detect_lt_trace.is_logged;
else
return link->dp_trace.commit_lt_trace.is_logged;
}
void dp_trace_lt_result_update(struct dc_link *link,
enum link_training_result result,
bool in_detection)
{
if (in_detection)
link->dp_trace.detect_lt_trace.result = result;
else
link->dp_trace.commit_lt_trace.result = result;
}
void dp_trace_set_lt_start_timestamp(struct dc_link *link,
bool in_detection)
{
if (in_detection)
link->dp_trace.detect_lt_trace.timestamps.start = dm_get_timestamp(link->dc->ctx);
else
link->dp_trace.commit_lt_trace.timestamps.start = dm_get_timestamp(link->dc->ctx);
}
void dp_trace_set_lt_end_timestamp(struct dc_link *link,
bool in_detection)
{
if (in_detection)
link->dp_trace.detect_lt_trace.timestamps.end = dm_get_timestamp(link->dc->ctx);
else
link->dp_trace.commit_lt_trace.timestamps.end = dm_get_timestamp(link->dc->ctx);
}
unsigned long long dp_trace_get_lt_end_timestamp(struct dc_link *link,
bool in_detection)
{
if (in_detection)
return link->dp_trace.detect_lt_trace.timestamps.end;
else
return link->dp_trace.commit_lt_trace.timestamps.end;
}
const struct dp_trace_lt_counts *dp_trace_get_lt_counts(struct dc_link *link,
bool in_detection)
{
if (in_detection)
return &link->dp_trace.detect_lt_trace.counts;
else
return &link->dp_trace.commit_lt_trace.counts;
}
unsigned int dp_trace_get_link_loss_count(struct dc_link *link)
{
return link->dp_trace.link_loss_count;
}
void dp_trace_set_edp_power_timestamp(struct dc_link *link,
bool power_up)
{
if (!power_up)
/*save driver power off time stamp*/
link->dp_trace.edp_trace_power_timestamps.poweroff = dm_get_timestamp(link->dc->ctx);
else
link->dp_trace.edp_trace_power_timestamps.poweron = dm_get_timestamp(link->dc->ctx);
}
uint64_t dp_trace_get_edp_poweron_timestamp(struct dc_link *link)
{
return link->dp_trace.edp_trace_power_timestamps.poweron;
}
uint64_t dp_trace_get_edp_poweroff_timestamp(struct dc_link *link)
{
return link->dp_trace.edp_trace_power_timestamps.poweroff;
}
void dp_trace_source_sequence(struct dc_link *link, uint8_t dp_test_mode)
{
if (link != NULL && link->dc->debug.enable_driver_sequence_debug)
core_link_write_dpcd(link, DP_SOURCE_SEQUENCE,
&dp_test_mode, sizeof(dp_test_mode));
}
@@ -0,0 +1,63 @@
/*
* 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_DP_TRACE_H__
#define __LINK_DP_TRACE_H__
#include "link_service.h"
void dp_trace_init(struct dc_link *link);
void dp_trace_reset(struct dc_link *link);
bool dp_trace_is_initialized(struct dc_link *link);
void dp_trace_detect_lt_init(struct dc_link *link);
void dp_trace_commit_lt_init(struct dc_link *link);
void dp_trace_link_loss_increment(struct dc_link *link);
void dp_trace_lt_fail_count_update(struct dc_link *link,
unsigned int fail_count,
bool in_detection);
void dp_trace_lt_total_count_increment(struct dc_link *link,
bool in_detection);
void dp_trace_set_is_logged_flag(struct dc_link *link,
bool in_detection,
bool is_logged);
bool dp_trace_is_logged(struct dc_link *link,
bool in_detection);
void dp_trace_lt_result_update(struct dc_link *link,
enum link_training_result result,
bool in_detection);
void dp_trace_set_lt_start_timestamp(struct dc_link *link,
bool in_detection);
void dp_trace_set_lt_end_timestamp(struct dc_link *link,
bool in_detection);
unsigned long long dp_trace_get_lt_end_timestamp(struct dc_link *link,
bool in_detection);
const struct dp_trace_lt_counts *dp_trace_get_lt_counts(struct dc_link *link,
bool in_detection);
unsigned int dp_trace_get_link_loss_count(struct dc_link *link);
void dp_trace_set_edp_power_timestamp(struct dc_link *link,
bool power_up);
uint64_t dp_trace_get_edp_poweron_timestamp(struct dc_link *link);
uint64_t dp_trace_get_edp_poweroff_timestamp(struct dc_link *link);
void dp_trace_source_sequence(struct dc_link *link, uint8_t dp_test_mode);
#endif /* __LINK_DP_TRACE_H__ */
@@ -0,0 +1,353 @@
/*
* 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_dio.h"
#include "core_types.h"
#include "link_enc_cfg.h"
/**
* DOC: overview
*
* Display Input Output (DIO), is the display input and output unit in DCN. It
* includes output encoders to support different display output, like
* DisplayPort, HDMI, DVI interface, and others. It also includes the control
* and status channels for these interfaces.
*/
void set_dio_throttled_vcp_size(struct pipe_ctx *pipe_ctx,
struct fixed31_32 throttled_vcp_size)
{
struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc;
stream_encoder->funcs->set_throttled_vcp_size(
stream_encoder,
throttled_vcp_size);
}
void setup_dio_stream_encoder(struct pipe_ctx *pipe_ctx)
{
struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;
if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link);
if (!link_enc) {
ASSERT(link_enc);
return;
}
if (!dc_is_rgb_signal(pipe_ctx->stream->signal))
link_enc->funcs->connect_dig_be_to_fe(link_enc,
pipe_ctx->stream_res.stream_enc->id, true);
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(pipe_ctx->stream->link,
DPCD_SOURCE_SEQ_AFTER_CONNECT_DIG_FE_BE);
if (stream_enc->funcs->enable_stream)
stream_enc->funcs->enable_stream(stream_enc,
pipe_ctx->stream->signal, true);
if (stream_enc->funcs->map_stream_to_link)
stream_enc->funcs->map_stream_to_link(stream_enc,
stream_enc->stream_enc_inst, link_enc->transmitter - TRANSMITTER_UNIPHY_A);
if (stream_enc->funcs->set_input_mode)
stream_enc->funcs->set_input_mode(stream_enc,
pipe_ctx->stream_res.pix_clk_params.dio_se_pix_per_cycle);
if (stream_enc->funcs->enable_fifo)
stream_enc->funcs->enable_fifo(stream_enc);
}
void reset_dio_stream_encoder(struct pipe_ctx *pipe_ctx)
{
struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;
if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link);
if (!link_enc) {
ASSERT(link_enc);
return;
}
if (!stream_enc)
return;
if (stream_enc->funcs->disable_fifo)
stream_enc->funcs->disable_fifo(stream_enc);
if (stream_enc->funcs->set_input_mode)
stream_enc->funcs->set_input_mode(stream_enc, 0);
if (stream_enc->funcs->enable_stream)
stream_enc->funcs->enable_stream(stream_enc,
pipe_ctx->stream->signal, false);
if (!dc_is_rgb_signal(pipe_ctx->stream->signal))
link_enc->funcs->connect_dig_be_to_fe(
link_enc,
pipe_ctx->stream_res.stream_enc->id,
false);
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(
pipe_ctx->stream->link,
DPCD_SOURCE_SEQ_AFTER_DISCONNECT_DIG_FE_BE);
}
void setup_dio_stream_attribute(struct pipe_ctx *pipe_ctx)
{
struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc;
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
if (!dc_is_virtual_signal(stream->signal) &&
!dc_is_rgb_signal(stream->signal))
stream_encoder->funcs->setup_stereo_sync(
stream_encoder,
pipe_ctx->stream_res.tg->inst,
stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE);
if (dc_is_dp_signal(stream->signal))
stream_encoder->funcs->dp_set_stream_attribute(
stream_encoder,
&stream->timing,
stream->output_color_space,
stream->use_vsc_sdp_for_colorimetry,
link->dpcd_caps.dprx_feature.bits.SST_SPLIT_SDP_CAP);
else if (dc_is_hdmi_tmds_signal(stream->signal))
stream_encoder->funcs->hdmi_set_stream_attribute(
stream_encoder,
&stream->timing,
stream->phy_pix_clk,
pipe_ctx->stream_res.audio != NULL);
else if (dc_is_dvi_signal(stream->signal))
stream_encoder->funcs->dvi_set_stream_attribute(
stream_encoder,
&stream->timing,
stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK);
else if (dc_is_lvds_signal(stream->signal))
stream_encoder->funcs->lvds_set_stream_attribute(
stream_encoder,
&stream->timing);
if (dc_is_dp_signal(stream->signal))
link->dc->link_srv->dp_trace_source_sequence(link,
DPCD_SOURCE_SEQ_AFTER_DP_STREAM_ATTR);
}
void enable_dio_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (!link_enc) {
ASSERT(link_enc);
return;
}
if (dc_is_dp_sst_signal(signal))
link_enc->funcs->enable_dp_output(
link_enc,
link_settings,
clock_source);
else
link_enc->funcs->enable_dp_mst_output(
link_enc,
link_settings,
clock_source);
link->dc->link_srv->dp_trace_source_sequence(link,
DPCD_SOURCE_SEQ_AFTER_ENABLE_LINK_PHY);
}
void disable_dio_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (!link_enc) {
ASSERT(link_enc);
return;
}
link_enc->funcs->disable_output(link_enc, signal);
link->dc->link_srv->dp_trace_source_sequence(link,
DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY);
}
void set_dio_dp_link_test_pattern(struct dc_link *link,
const struct link_resource *link_res,
struct encoder_set_dp_phy_pattern_param *tp_params)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (!link_enc) {
ASSERT(link_enc);
return;
}
link_enc->funcs->dp_set_phy_pattern(link_enc, tp_params);
link->dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_SET_SOURCE_PATTERN);
}
void set_dio_dp_lane_settings(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX])
{
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (!link_enc) {
ASSERT(link_enc);
return;
}
link_enc->funcs->dp_set_lane_settings(link_enc, link_settings, lane_settings);
}
void update_dio_stream_allocation_table(struct dc_link *link,
const struct link_resource *link_res,
const struct link_mst_stream_allocation_table *table)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (!link_enc) {
ASSERT(link_enc);
return;
}
link_enc->funcs->update_mst_stream_allocation_table(link_enc, table);
}
void setup_dio_audio_output(struct pipe_ctx *pipe_ctx,
struct audio_output *audio_output, uint32_t audio_inst)
{
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream_res.stream_enc->funcs->dp_audio_setup(
pipe_ctx->stream_res.stream_enc,
audio_inst,
&pipe_ctx->stream->audio_info);
else
pipe_ctx->stream_res.stream_enc->funcs->hdmi_audio_setup(
pipe_ctx->stream_res.stream_enc,
audio_inst,
&pipe_ctx->stream->audio_info,
&audio_output->crtc_info);
}
void enable_dio_audio_packet(struct pipe_ctx *pipe_ctx)
{
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream_res.stream_enc->funcs->dp_audio_enable(
pipe_ctx->stream_res.stream_enc);
pipe_ctx->stream_res.stream_enc->funcs->audio_mute_control(
pipe_ctx->stream_res.stream_enc, false);
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(
pipe_ctx->stream->link,
DPCD_SOURCE_SEQ_AFTER_ENABLE_AUDIO_STREAM);
}
void disable_dio_audio_packet(struct pipe_ctx *pipe_ctx)
{
pipe_ctx->stream_res.stream_enc->funcs->audio_mute_control(
pipe_ctx->stream_res.stream_enc, true);
if (pipe_ctx->stream_res.audio) {
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream_res.stream_enc->funcs->dp_audio_disable(
pipe_ctx->stream_res.stream_enc);
else
pipe_ctx->stream_res.stream_enc->funcs->hdmi_audio_disable(
pipe_ctx->stream_res.stream_enc);
}
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(
pipe_ctx->stream->link,
DPCD_SOURCE_SEQ_AFTER_DISABLE_AUDIO_STREAM);
}
static const struct link_hwss dio_link_hwss = {
.setup_stream_encoder = setup_dio_stream_encoder,
.reset_stream_encoder = reset_dio_stream_encoder,
.setup_stream_attribute = setup_dio_stream_attribute,
.disable_link_output = disable_dio_link_output,
.setup_audio_output = setup_dio_audio_output,
.enable_audio_packet = enable_dio_audio_packet,
.disable_audio_packet = disable_dio_audio_packet,
.ext = {
.set_throttled_vcp_size = set_dio_throttled_vcp_size,
.enable_dp_link_output = enable_dio_dp_link_output,
.set_dp_link_test_pattern = set_dio_dp_link_test_pattern,
.set_dp_lane_settings = set_dio_dp_lane_settings,
.update_stream_allocation_table = update_dio_stream_allocation_table,
},
};
/**
* can_use_dio_link_hwss - Check if the link_hwss is accessible
*
* @link: Reference a link struct containing one or more sinks and the
* connective status.
* @link_res: Mappable hardware resource used to enable a link.
*
* Returns:
* Return true if the link encoder is accessible from link.
*/
bool can_use_dio_link_hwss(const struct dc_link *link,
const struct link_resource *link_res)
{
if (!link->dc->config.unify_link_enc_assignment)
return link->link_enc != NULL;
else
return link_res->dio_link_enc != NULL;
}
/**
* get_dio_link_hwss - Return link_hwss reference
*
* This function behaves like a get function to return the link_hwss populated
* in the link_hwss_dio.c file.
*
* Returns:
* Return the reference to the filled struct of link_hwss.
*/
const struct link_hwss *get_dio_link_hwss(void)
{
return &dio_link_hwss;
}
@@ -0,0 +1,63 @@
/*
* 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_DIO_H__
#define __LINK_HWSS_DIO_H__
#include "link_hwss.h"
#include "link_service.h"
const struct link_hwss *get_dio_link_hwss(void);
bool can_use_dio_link_hwss(const struct dc_link *link,
const struct link_resource *link_res);
void set_dio_throttled_vcp_size(struct pipe_ctx *pipe_ctx,
struct fixed31_32 throttled_vcp_size);
void setup_dio_stream_encoder(struct pipe_ctx *pipe_ctx);
void reset_dio_stream_encoder(struct pipe_ctx *pipe_ctx);
void setup_dio_stream_attribute(struct pipe_ctx *pipe_ctx);
void enable_dio_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings);
void disable_dio_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal);
void set_dio_dp_link_test_pattern(struct dc_link *link,
const struct link_resource *link_res,
struct encoder_set_dp_phy_pattern_param *tp_params);
void set_dio_dp_lane_settings(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]);
void setup_dio_audio_output(struct pipe_ctx *pipe_ctx,
struct audio_output *audio_output, uint32_t audio_inst);
void enable_dio_audio_packet(struct pipe_ctx *pipe_ctx);
void disable_dio_audio_packet(struct pipe_ctx *pipe_ctx);
void update_dio_stream_allocation_table(struct dc_link *link,
const struct link_resource *link_res,
const struct link_mst_stream_allocation_table *table);
#endif /* __LINK_HWSS_DIO_H__ */
@@ -0,0 +1,199 @@
/*
* 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
*
*/
#include "link_hwss_dio.h"
#include "link_hwss_dio_fixed_vs_pe_retimer.h"
#include "link_enc_cfg.h"
uint8_t dp_dio_fixed_vs_pe_retimer_lane_cfg_to_hw_cfg(struct dc_link *link)
{
// TODO: Get USB-C cable orientation
if (link->cur_link_settings.lane_count == LANE_COUNT_FOUR)
return 0xF2;
else
return 0x12;
}
void dp_dio_fixed_vs_pe_retimer_exit_manual_automation(struct dc_link *link)
{
const uint8_t dp_type = dp_dio_fixed_vs_pe_retimer_lane_cfg_to_hw_cfg(link);
const uint8_t vendor_lttpr_exit_manual_automation_0[4] = {0x1, 0x11, 0x0, 0x06};
const uint8_t vendor_lttpr_exit_manual_automation_1[4] = {0x1, 0x50, dp_type, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_2[4] = {0x1, 0x50, 0x50, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_3[4] = {0x1, 0x51, 0x50, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_4[4] = {0x1, 0x10, 0x58, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_5[4] = {0x1, 0x10, 0x59, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_6[4] = {0x1, 0x30, 0x51, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_7[4] = {0x1, 0x30, 0x52, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_8[4] = {0x1, 0x30, 0x54, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_9[4] = {0x1, 0x30, 0x55, 0x0};
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_0[0], sizeof(vendor_lttpr_exit_manual_automation_0));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_1[0], sizeof(vendor_lttpr_exit_manual_automation_1));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_2[0], sizeof(vendor_lttpr_exit_manual_automation_2));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_3[0], sizeof(vendor_lttpr_exit_manual_automation_3));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_4[0], sizeof(vendor_lttpr_exit_manual_automation_4));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_5[0], sizeof(vendor_lttpr_exit_manual_automation_5));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_6[0], sizeof(vendor_lttpr_exit_manual_automation_6));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_7[0], sizeof(vendor_lttpr_exit_manual_automation_7));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_8[0], sizeof(vendor_lttpr_exit_manual_automation_8));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_9[0], sizeof(vendor_lttpr_exit_manual_automation_9));
}
static bool set_dio_fixed_vs_pe_retimer_dp_link_test_pattern_override(struct dc_link *link,
const struct link_resource *link_res, struct encoder_set_dp_phy_pattern_param *tp_params,
const struct link_hwss *link_hwss)
{
struct encoder_set_dp_phy_pattern_param hw_tp_params = { 0 };
const uint8_t pltpat_custom[10] = {0x1F, 0x7C, 0xF0, 0xC1, 0x07, 0x1F, 0x7C, 0xF0, 0xC1, 0x07};
const uint8_t vendor_lttpr_write_data_pg0[4] = {0x1, 0x11, 0x0, 0x0};
const uint8_t vendor_lttpr_exit_manual_automation_0[4] = {0x1, 0x11, 0x0, 0x06};
if (!link->dpcd_caps.lttpr_caps.main_link_channel_coding.bits.DP_128b_132b_SUPPORTED)
return false;
if (tp_params == NULL)
return false;
if (IS_DP_PHY_SQUARE_PATTERN(link->current_test_pattern))
// Deprogram overrides from previous test pattern
dp_dio_fixed_vs_pe_retimer_exit_manual_automation(link);
switch (tp_params->dp_phy_pattern) {
case DP_TEST_PATTERN_80BIT_CUSTOM:
if (tp_params->custom_pattern_size == 0 || memcmp(tp_params->custom_pattern,
pltpat_custom, tp_params->custom_pattern_size) != 0)
return false;
hw_tp_params.custom_pattern = tp_params->custom_pattern;
hw_tp_params.custom_pattern_size = tp_params->custom_pattern_size;
break;
case DP_TEST_PATTERN_D102:
break;
default:
if (link->current_test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM ||
link->current_test_pattern == DP_TEST_PATTERN_D102)
// Deprogram overrides from previous test pattern
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_0[0],
sizeof(vendor_lttpr_exit_manual_automation_0));
return false;
}
hw_tp_params.dp_phy_pattern = tp_params->dp_phy_pattern;
hw_tp_params.dp_panel_mode = tp_params->dp_panel_mode;
if (link_hwss->ext.set_dp_link_test_pattern)
link_hwss->ext.set_dp_link_test_pattern(link, link_res, &hw_tp_params);
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg0[0], sizeof(vendor_lttpr_write_data_pg0));
return true;
}
static void set_dio_fixed_vs_pe_retimer_dp_link_test_pattern(struct dc_link *link,
const struct link_resource *link_res,
struct encoder_set_dp_phy_pattern_param *tp_params)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (!set_dio_fixed_vs_pe_retimer_dp_link_test_pattern_override(
link, link_res, tp_params, get_dio_link_hwss())) {
link_enc->funcs->dp_set_phy_pattern(link_enc, tp_params);
}
link->dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_SET_SOURCE_PATTERN);
}
void enable_dio_fixed_vs_pe_retimer_program_4lane_output(struct dc_link *link)
{
const uint8_t vendor_lttpr_write_data_4lane_1[4] = {0x1, 0x6E, 0xF2, 0x19};
const uint8_t vendor_lttpr_write_data_4lane_2[4] = {0x1, 0x6B, 0xF2, 0x01};
const uint8_t vendor_lttpr_write_data_4lane_3[4] = {0x1, 0x6D, 0xF2, 0x18};
const uint8_t vendor_lttpr_write_data_4lane_4[4] = {0x1, 0x6C, 0xF2, 0x03};
const uint8_t vendor_lttpr_write_data_4lane_5[4] = {0x1, 0x03, 0xF3, 0x06};
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_1[0], sizeof(vendor_lttpr_write_data_4lane_1));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_2[0], sizeof(vendor_lttpr_write_data_4lane_2));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_3[0], sizeof(vendor_lttpr_write_data_4lane_3));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_4[0], sizeof(vendor_lttpr_write_data_4lane_4));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_5[0], sizeof(vendor_lttpr_write_data_4lane_5));
}
static void enable_dio_fixed_vs_pe_retimer_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings)
{
if (link_settings->lane_count == LANE_COUNT_FOUR)
enable_dio_fixed_vs_pe_retimer_program_4lane_output(link);
enable_dio_dp_link_output(link, link_res, signal, clock_source, link_settings);
}
static const struct link_hwss dio_fixed_vs_pe_retimer_link_hwss = {
.setup_stream_encoder = setup_dio_stream_encoder,
.reset_stream_encoder = reset_dio_stream_encoder,
.setup_stream_attribute = setup_dio_stream_attribute,
.disable_link_output = disable_dio_link_output,
.setup_audio_output = setup_dio_audio_output,
.enable_audio_packet = enable_dio_audio_packet,
.disable_audio_packet = disable_dio_audio_packet,
.ext = {
.set_throttled_vcp_size = set_dio_throttled_vcp_size,
.enable_dp_link_output = enable_dio_fixed_vs_pe_retimer_dp_link_output,
.set_dp_link_test_pattern = set_dio_fixed_vs_pe_retimer_dp_link_test_pattern,
.set_dp_lane_settings = set_dio_dp_lane_settings,
.update_stream_allocation_table = update_dio_stream_allocation_table,
},
};
bool requires_fixed_vs_pe_retimer_dio_link_hwss(const struct dc_link *link)
{
return ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN);
}
const struct link_hwss *get_dio_fixed_vs_pe_retimer_link_hwss(void)
{
return &dio_fixed_vs_pe_retimer_link_hwss;
}
@@ -0,0 +1,37 @@
/*
* 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_HWSS_DIO_FIXED_VS_PE_RETIMER_H__
#define __LINK_HWSS_DIO_FIXED_VS_PE_RETIMER_H__
#include "link_service.h"
uint32_t dp_dio_fixed_vs_pe_retimer_get_lttpr_write_address(struct dc_link *link);
uint8_t dp_dio_fixed_vs_pe_retimer_lane_cfg_to_hw_cfg(struct dc_link *link);
void dp_dio_fixed_vs_pe_retimer_exit_manual_automation(struct dc_link *link);
void enable_dio_fixed_vs_pe_retimer_program_4lane_output(struct dc_link *link);
bool requires_fixed_vs_pe_retimer_dio_link_hwss(const struct dc_link *link);
const struct link_hwss *get_dio_fixed_vs_pe_retimer_link_hwss(void);
#endif /* __LINK_HWSS_DIO_FIXED_VS_PE_RETIMER_H__ */
@@ -0,0 +1,184 @@
/*
* 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_dpia.h"
#include "core_types.h"
#include "link_hwss_dio.h"
#include "link_enc_cfg.h"
#define DC_LOGGER \
link->ctx->logger
#define DC_LOGGER_INIT(logger)
static void update_dpia_stream_allocation_table(struct dc_link *link,
const struct link_resource *link_res,
const struct link_mst_stream_allocation_table *table)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
static enum dc_status status;
uint8_t mst_alloc_slots = 0, prev_mst_slots_in_use = 0xFF;
int i;
DC_LOGGER_INIT(link->ctx->logger);
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
for (i = 0; i < table->stream_count; i++)
mst_alloc_slots += table->stream_allocations[i].slot_count;
status = dc_process_dmub_set_mst_slots(link->dc, link->link_index,
mst_alloc_slots, &prev_mst_slots_in_use);
ASSERT(status == DC_OK);
DC_LOG_MST("dpia : status[%d]: alloc_slots[%d]: used_slots[%d]\n",
status, mst_alloc_slots, prev_mst_slots_in_use);
if (link_enc)
link_enc->funcs->update_mst_stream_allocation_table(link_enc, table);
}
static void set_dio_dpia_link_test_pattern(struct dc_link *link,
const struct link_resource *link_res,
struct encoder_set_dp_phy_pattern_param *tp_params)
{
if (tp_params->dp_phy_pattern != DP_TEST_PATTERN_VIDEO_MODE)
return;
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (!link_enc)
return;
link_enc->funcs->dp_set_phy_pattern(link_enc, tp_params);
link->dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_SET_SOURCE_PATTERN);
}
static void set_dio_dpia_lane_settings(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX])
{
(void)link;
(void)link_res;
(void)link_settings;
(void)lane_settings;
}
static void enable_dpia_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
DC_LOGGER_INIT(link->ctx->logger);
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (link_enc != NULL) {
if (link->dc->config.enable_dpia_pre_training || link->dc->config.unify_link_enc_assignment) {
uint8_t fec_rdy = link->dc->link_srv->dp_should_enable_fec(link);
uint8_t digmode = dc_is_dp_sst_signal(signal) ? DIG_SST_MODE : DIG_MST_MODE;
if (link_enc->funcs->enable_dpia_output)
link_enc->funcs->enable_dpia_output(
link_enc,
link_settings,
link->ddc_hw_inst,
digmode,
fec_rdy);
else
DC_LOG_ERROR("%s: link encoder does not support enable_dpia_output\n", __func__);
} else
enable_dio_dp_link_output(link, link_res, signal, clock_source, link_settings);
}
link->dc->link_srv->dp_trace_source_sequence(link,
DPCD_SOURCE_SEQ_AFTER_ENABLE_LINK_PHY);
}
static void disable_dpia_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
DC_LOGGER_INIT(link->ctx->logger);
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (link_enc != NULL) {
if (link->dc->config.enable_dpia_pre_training || link->dc->config.unify_link_enc_assignment) {
uint8_t digmode = dc_is_dp_sst_signal(signal) ? DIG_SST_MODE : DIG_MST_MODE;
if (link_enc->funcs->disable_dpia_output)
link_enc->funcs->disable_dpia_output(link_enc, link->ddc_hw_inst, digmode);
else
DC_LOG_ERROR("%s: link encoder does not support disable_dpia_output\n", __func__);
} else
link_enc->funcs->disable_output(link_enc, signal);
}
link->dc->link_srv->dp_trace_source_sequence(link,
DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY);
}
static const struct link_hwss dpia_link_hwss = {
/* Ensure initialization order matches the declaration in link_hwss.h
* for strict compiler compliance and consistency across HWSS implementations
*/
.ext = {
.set_throttled_vcp_size = set_dio_throttled_vcp_size,
.enable_dp_link_output = enable_dpia_link_output,
.set_dp_link_test_pattern = set_dio_dpia_link_test_pattern,
.set_dp_lane_settings = set_dio_dpia_lane_settings,
.update_stream_allocation_table = update_dpia_stream_allocation_table,
},
.setup_stream_encoder = setup_dio_stream_encoder,
.reset_stream_encoder = reset_dio_stream_encoder,
.setup_stream_attribute = setup_dio_stream_attribute,
.disable_link_output = disable_dpia_link_output,
.setup_audio_output = setup_dio_audio_output,
.enable_audio_packet = enable_dio_audio_packet,
.disable_audio_packet = disable_dio_audio_packet,
};
bool can_use_dpia_link_hwss(const struct dc_link *link,
const struct link_resource *link_res)
{
if (!link->dc->config.unify_link_enc_assignment)
return link->is_dig_mapping_flexible && link->dc->res_pool->funcs->link_encs_assign;
else
return link->is_dig_mapping_flexible && link_res->dio_link_enc != NULL;
}
const struct link_hwss *get_dpia_link_hwss(void)
{
return &dpia_link_hwss;
}
@@ -0,0 +1,37 @@
/*
* 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_DPIA_H__
#define __LINK_HWSS_DPIA_H__
#include "link_hwss.h"
#define DIG_SST_MODE 0
#define DIG_MST_MODE 5
const struct link_hwss *get_dpia_link_hwss(void);
bool can_use_dpia_link_hwss(const struct dc_link *link,
const struct link_resource *link_res);
#endif /* __LINK_HWSS_DPIA_H__ */
@@ -0,0 +1,234 @@
/*
* 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_dp.h"
#include "dm_helpers.h"
#include "core_types.h"
#include "dccg.h"
#include "clk_mgr.h"
#define DC_LOGGER link->ctx->logger
void set_hpo_dp_throttled_vcp_size(struct pipe_ctx *pipe_ctx,
struct fixed31_32 throttled_vcp_size)
{
struct hpo_dp_stream_encoder *hpo_dp_stream_encoder =
pipe_ctx->stream_res.hpo_dp_stream_enc;
struct hpo_dp_link_encoder *hpo_dp_link_encoder =
pipe_ctx->link_res.hpo_dp_link_enc;
hpo_dp_link_encoder->funcs->set_throttled_vcp_size(hpo_dp_link_encoder,
hpo_dp_stream_encoder->inst,
throttled_vcp_size);
}
void set_hpo_dp_hblank_min_symbol_width(struct pipe_ctx *pipe_ctx,
const struct dc_link_settings *link_settings,
struct fixed31_32 throttled_vcp_size)
{
struct hpo_dp_stream_encoder *hpo_dp_stream_encoder =
pipe_ctx->stream_res.hpo_dp_stream_enc;
struct dc_crtc_timing *timing = &pipe_ctx->stream->timing;
struct fixed31_32 h_blank_in_ms, time_slot_in_ms, mtp_cnt_per_h_blank;
uint32_t link_bw_in_kbps =
hpo_dp_stream_encoder->ctx->dc->link_srv->dp_link_bandwidth_kbps(
pipe_ctx->stream->link, link_settings);
uint16_t hblank_min_symbol_width = 0;
if (link_bw_in_kbps > 0) {
h_blank_in_ms = dc_fixpt_div(dc_fixpt_from_int(
timing->h_total - timing->h_addressable),
dc_fixpt_from_fraction(timing->pix_clk_100hz, 10));
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 = (uint16_t)dc_fixpt_floor(
dc_fixpt_mul(mtp_cnt_per_h_blank, throttled_vcp_size));
}
hpo_dp_stream_encoder->funcs->set_hblank_min_symbol_width(hpo_dp_stream_encoder,
hblank_min_symbol_width);
}
void setup_hpo_dp_stream_encoder(struct pipe_ctx *pipe_ctx)
{
struct hpo_dp_stream_encoder *stream_enc = pipe_ctx->stream_res.hpo_dp_stream_enc;
struct hpo_dp_link_encoder *link_enc = pipe_ctx->link_res.hpo_dp_link_enc;
stream_enc->funcs->enable_stream(stream_enc);
stream_enc->funcs->map_stream_to_link(stream_enc, stream_enc->inst, link_enc->inst);
}
void reset_hpo_dp_stream_encoder(struct pipe_ctx *pipe_ctx)
{
struct hpo_dp_stream_encoder *stream_enc = pipe_ctx->stream_res.hpo_dp_stream_enc;
stream_enc->funcs->disable(stream_enc);
}
void setup_hpo_dp_stream_attribute(struct pipe_ctx *pipe_ctx)
{
struct hpo_dp_stream_encoder *stream_enc = pipe_ctx->stream_res.hpo_dp_stream_enc;
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
stream_enc->funcs->set_stream_attribute(
stream_enc,
&stream->timing,
stream->output_color_space,
stream->use_vsc_sdp_for_colorimetry,
(stream->timing.flags.DSC != 0),
false);
link->dc->link_srv->dp_trace_source_sequence(link,
DPCD_SOURCE_SEQ_AFTER_DP_STREAM_ATTR);
}
void enable_hpo_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings)
{
(void)signal;
(void)clock_source;
if (!link_res->hpo_dp_link_enc) {
DC_LOG_ERROR("%s: invalid hpo_dp_link_enc\n", __func__);
return;
}
if (link->dc->res_pool->dccg->funcs->set_symclk32_le_root_clock_gating)
link->dc->res_pool->dccg->funcs->set_symclk32_le_root_clock_gating(
link->dc->res_pool->dccg,
link_res->hpo_dp_link_enc->inst,
true);
link_res->hpo_dp_link_enc->funcs->enable_link_phy(
link_res->hpo_dp_link_enc,
link_settings,
link->link_enc->transmitter,
link->link_enc->hpd_source);
}
void disable_hpo_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal)
{
if (!link_res->hpo_dp_link_enc) {
DC_LOG_ERROR("%s: invalid hpo_dp_link_enc\n", __func__);
return;
}
link_res->hpo_dp_link_enc->funcs->link_disable(link_res->hpo_dp_link_enc);
link_res->hpo_dp_link_enc->funcs->disable_link_phy(
link_res->hpo_dp_link_enc, signal);
if (link->dc->res_pool->dccg->funcs->set_symclk32_le_root_clock_gating)
link->dc->res_pool->dccg->funcs->set_symclk32_le_root_clock_gating(
link->dc->res_pool->dccg,
link_res->hpo_dp_link_enc->inst,
false);
}
static void set_hpo_dp_link_test_pattern(struct dc_link *link,
const struct link_resource *link_res,
struct encoder_set_dp_phy_pattern_param *tp_params)
{
link_res->hpo_dp_link_enc->funcs->set_link_test_pattern(
link_res->hpo_dp_link_enc, tp_params);
link->dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_SET_SOURCE_PATTERN);
}
static void set_hpo_dp_lane_settings(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX])
{
(void)link;
link_res->hpo_dp_link_enc->funcs->set_ffe(
link_res->hpo_dp_link_enc,
link_settings,
lane_settings[0].FFE_PRESET.raw);
}
void update_hpo_dp_stream_allocation_table(struct dc_link *link,
const struct link_resource *link_res,
const struct link_mst_stream_allocation_table *table)
{
(void)link;
link_res->hpo_dp_link_enc->funcs->update_stream_allocation_table(
link_res->hpo_dp_link_enc,
table);
}
void setup_hpo_dp_audio_output(struct pipe_ctx *pipe_ctx,
struct audio_output *audio_output, uint32_t audio_inst)
{
(void)audio_output;
pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_audio_setup(
pipe_ctx->stream_res.hpo_dp_stream_enc,
audio_inst,
&pipe_ctx->stream->audio_info);
}
void enable_hpo_dp_audio_packet(struct pipe_ctx *pipe_ctx)
{
pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_audio_enable(
pipe_ctx->stream_res.hpo_dp_stream_enc);
}
void disable_hpo_dp_audio_packet(struct pipe_ctx *pipe_ctx)
{
if (pipe_ctx->stream_res.audio)
pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_audio_disable(
pipe_ctx->stream_res.hpo_dp_stream_enc);
}
static const struct link_hwss hpo_dp_link_hwss = {
.setup_stream_encoder = setup_hpo_dp_stream_encoder,
.reset_stream_encoder = reset_hpo_dp_stream_encoder,
.setup_stream_attribute = setup_hpo_dp_stream_attribute,
.disable_link_output = disable_hpo_dp_link_output,
.setup_audio_output = setup_hpo_dp_audio_output,
.enable_audio_packet = enable_hpo_dp_audio_packet,
.disable_audio_packet = disable_hpo_dp_audio_packet,
.ext = {
.set_throttled_vcp_size = set_hpo_dp_throttled_vcp_size,
.set_hblank_min_symbol_width = set_hpo_dp_hblank_min_symbol_width,
.enable_dp_link_output = enable_hpo_dp_link_output,
.set_dp_link_test_pattern = set_hpo_dp_link_test_pattern,
.set_dp_lane_settings = set_hpo_dp_lane_settings,
.update_stream_allocation_table = update_hpo_dp_stream_allocation_table,
},
};
bool can_use_hpo_dp_link_hwss(const struct dc_link *link,
const struct link_resource *link_res)
{
(void)link;
return link_res->hpo_dp_link_enc != NULL;
}
const struct link_hwss *get_hpo_dp_link_hwss(void)
{
return &hpo_dp_link_hwss;
}
@@ -0,0 +1,62 @@
/*
* 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_DP_H__
#define __LINK_HWSS_HPO_DP_H__
#include "link_hwss.h"
#include "link_service.h"
void set_hpo_dp_throttled_vcp_size(struct pipe_ctx *pipe_ctx,
struct fixed31_32 throttled_vcp_size);
void set_hpo_dp_hblank_min_symbol_width(struct pipe_ctx *pipe_ctx,
const struct dc_link_settings *link_settings,
struct fixed31_32 throttled_vcp_size);
void set_hpo_dp_hblank_min_symbol_width(struct pipe_ctx *pipe_ctx,
const struct dc_link_settings *link_settings,
struct fixed31_32 throttled_vcp_size);
void setup_hpo_dp_stream_encoder(struct pipe_ctx *pipe_ctx);
void reset_hpo_dp_stream_encoder(struct pipe_ctx *pipe_ctx);
void setup_hpo_dp_stream_attribute(struct pipe_ctx *pipe_ctx);
void enable_hpo_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings);
void disable_hpo_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal);
void update_hpo_dp_stream_allocation_table(struct dc_link *link,
const struct link_resource *link_res,
const struct link_mst_stream_allocation_table *table);
void setup_hpo_dp_audio_output(struct pipe_ctx *pipe_ctx,
struct audio_output *audio_output, uint32_t audio_inst);
void enable_hpo_dp_audio_packet(struct pipe_ctx *pipe_ctx);
void disable_hpo_dp_audio_packet(struct pipe_ctx *pipe_ctx);
const struct link_hwss *get_hpo_dp_link_hwss(void);
bool can_use_hpo_dp_link_hwss(const struct dc_link *link,
const struct link_resource *link_res);
#endif /* __LINK_HWSS_HPO_DP_H__ */
@@ -0,0 +1,234 @@
/*
* 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
*
*/
#include "link_hwss_hpo_dp.h"
#include "link_hwss_hpo_fixed_vs_pe_retimer_dp.h"
#include "link_hwss_dio_fixed_vs_pe_retimer.h"
static void dp_hpo_fixed_vs_pe_retimer_set_tx_ffe(struct dc_link *link,
const struct dc_lane_settings *hw_lane_settings)
{
const uint8_t vendor_ffe_preset_table[16] = {
0x01, 0x41, 0x61, 0x81,
0xB1, 0x05, 0x35, 0x65,
0x85, 0xA5, 0x09, 0x39,
0x59, 0x89, 0x0F, 0x24};
const uint8_t ffe_mask[4] = {
(hw_lane_settings[0].FFE_PRESET.settings.no_deemphasis != 0 ? 0x0F : 0xFF)
& (hw_lane_settings[0].FFE_PRESET.settings.no_preshoot != 0 ? 0xF1 : 0xFF),
(hw_lane_settings[1].FFE_PRESET.settings.no_deemphasis != 0 ? 0x0F : 0xFF)
& (hw_lane_settings[1].FFE_PRESET.settings.no_preshoot != 0 ? 0xF1 : 0xFF),
(hw_lane_settings[2].FFE_PRESET.settings.no_deemphasis != 0 ? 0x0F : 0xFF)
& (hw_lane_settings[2].FFE_PRESET.settings.no_preshoot != 0 ? 0xF1 : 0xFF),
(hw_lane_settings[3].FFE_PRESET.settings.no_deemphasis != 0 ? 0x0F : 0xFF)
& (hw_lane_settings[3].FFE_PRESET.settings.no_preshoot != 0 ? 0xF1 : 0xFF)};
const uint8_t ffe_cfg[4] = {
vendor_ffe_preset_table[hw_lane_settings[0].FFE_PRESET.settings.level] & ffe_mask[0],
vendor_ffe_preset_table[hw_lane_settings[1].FFE_PRESET.settings.level] & ffe_mask[1],
vendor_ffe_preset_table[hw_lane_settings[2].FFE_PRESET.settings.level] & ffe_mask[2],
vendor_ffe_preset_table[hw_lane_settings[3].FFE_PRESET.settings.level] & ffe_mask[3]};
const uint8_t dp_type = dp_dio_fixed_vs_pe_retimer_lane_cfg_to_hw_cfg(link);
const uint8_t vendor_lttpr_write_data_ffe1[4] = {0x01, 0x50, dp_type, 0x0F};
const uint8_t vendor_lttpr_write_data_ffe2[4] = {0x01, 0x55, dp_type, ffe_cfg[0]};
const uint8_t vendor_lttpr_write_data_ffe3[4] = {0x01, 0x56, dp_type, ffe_cfg[1]};
const uint8_t vendor_lttpr_write_data_ffe4[4] = {0x01, 0x57, dp_type, ffe_cfg[2]};
const uint8_t vendor_lttpr_write_data_ffe5[4] = {0x01, 0x58, dp_type, ffe_cfg[3]};
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_ffe1[0], sizeof(vendor_lttpr_write_data_ffe1));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_ffe2[0], sizeof(vendor_lttpr_write_data_ffe2));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_ffe3[0], sizeof(vendor_lttpr_write_data_ffe3));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_ffe4[0], sizeof(vendor_lttpr_write_data_ffe4));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_ffe5[0], sizeof(vendor_lttpr_write_data_ffe5));
}
static void dp_hpo_fixed_vs_pe_retimer_program_override_test_pattern(struct dc_link *link,
struct encoder_set_dp_phy_pattern_param *tp_params)
{
(void)tp_params;
uint8_t clk_src = 0xC4;
uint8_t pattern = 0x4F; /* SQ128 */
const uint8_t vendor_lttpr_write_data_pg0[4] = {0x1, 0x11, 0x0, 0x0};
const uint8_t vendor_lttpr_write_data_pg1[4] = {0x1, 0x50, 0x50, clk_src};
const uint8_t vendor_lttpr_write_data_pg2[4] = {0x1, 0x51, 0x50, clk_src};
const uint8_t vendor_lttpr_write_data_pg3[4] = {0x1, 0x10, 0x58, 0x21};
const uint8_t vendor_lttpr_write_data_pg4[4] = {0x1, 0x10, 0x59, 0x21};
const uint8_t vendor_lttpr_write_data_pg5[4] = {0x1, 0x1C, 0x58, pattern};
const uint8_t vendor_lttpr_write_data_pg6[4] = {0x1, 0x1C, 0x59, pattern};
const uint8_t vendor_lttpr_write_data_pg7[4] = {0x1, 0x30, 0x51, 0x20};
const uint8_t vendor_lttpr_write_data_pg8[4] = {0x1, 0x30, 0x52, 0x20};
const uint8_t vendor_lttpr_write_data_pg9[4] = {0x1, 0x30, 0x54, 0x20};
const uint8_t vendor_lttpr_write_data_pg10[4] = {0x1, 0x30, 0x55, 0x20};
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg0[0], sizeof(vendor_lttpr_write_data_pg0));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg1[0], sizeof(vendor_lttpr_write_data_pg1));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg2[0], sizeof(vendor_lttpr_write_data_pg2));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg3[0], sizeof(vendor_lttpr_write_data_pg3));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg4[0], sizeof(vendor_lttpr_write_data_pg4));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg5[0], sizeof(vendor_lttpr_write_data_pg5));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg6[0], sizeof(vendor_lttpr_write_data_pg6));
if (link->cur_link_settings.lane_count == LANE_COUNT_FOUR)
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg7[0], sizeof(vendor_lttpr_write_data_pg7));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg8[0], sizeof(vendor_lttpr_write_data_pg8));
if (link->cur_link_settings.lane_count == LANE_COUNT_FOUR)
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg9[0], sizeof(vendor_lttpr_write_data_pg9));
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pg10[0], sizeof(vendor_lttpr_write_data_pg10));
}
static bool dp_hpo_fixed_vs_pe_retimer_set_override_test_pattern(struct dc_link *link,
const struct link_resource *link_res, struct encoder_set_dp_phy_pattern_param *tp_params,
const struct link_hwss *link_hwss)
{
struct encoder_set_dp_phy_pattern_param hw_tp_params = { 0 };
const uint8_t vendor_lttpr_exit_manual_automation_0[4] = {0x1, 0x11, 0x0, 0x06};
if (!link->dpcd_caps.lttpr_caps.main_link_channel_coding.bits.DP_128b_132b_SUPPORTED)
return false;
if (tp_params == NULL)
return false;
if (!IS_DP_PHY_SQUARE_PATTERN(tp_params->dp_phy_pattern)) {
// Deprogram overrides from previously set square wave override
if (link->current_test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM ||
link->current_test_pattern == DP_TEST_PATTERN_D102)
link->dc->link_srv->configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_exit_manual_automation_0[0],
sizeof(vendor_lttpr_exit_manual_automation_0));
else if (IS_DP_PHY_SQUARE_PATTERN(link->current_test_pattern))
dp_dio_fixed_vs_pe_retimer_exit_manual_automation(link);
return false;
}
hw_tp_params.dp_phy_pattern = DP_TEST_PATTERN_PRBS31;
hw_tp_params.dp_panel_mode = tp_params->dp_panel_mode;
if (link_hwss->ext.set_dp_link_test_pattern)
link_hwss->ext.set_dp_link_test_pattern(link, link_res, &hw_tp_params);
dp_hpo_fixed_vs_pe_retimer_program_override_test_pattern(link, tp_params);
return true;
}
static void set_hpo_fixed_vs_pe_retimer_dp_link_test_pattern(struct dc_link *link,
const struct link_resource *link_res,
struct encoder_set_dp_phy_pattern_param *tp_params)
{
if (!dp_hpo_fixed_vs_pe_retimer_set_override_test_pattern(
link, link_res, tp_params, get_hpo_dp_link_hwss())) {
link_res->hpo_dp_link_enc->funcs->set_link_test_pattern(
link_res->hpo_dp_link_enc, tp_params);
}
link->dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_SET_SOURCE_PATTERN);
// Give retimer extra time to lock before updating DP_TRAINING_PATTERN_SET to TPS1 or phy pattern
if (tp_params->dp_phy_pattern != DP_TEST_PATTERN_128b_132b_TPS2_TRAINING_MODE)
msleep(50);
}
static void set_hpo_fixed_vs_pe_retimer_dp_lane_settings(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX])
{
// Don't update our HW FFE when outputting phy test patterns
if (IS_DP_PHY_PATTERN(link->pending_test_pattern)) {
// Directly program FIXED_VS retimer FFE for SQ128 override
if (IS_DP_PHY_SQUARE_PATTERN(link->pending_test_pattern)) {
dp_hpo_fixed_vs_pe_retimer_set_tx_ffe(link, &lane_settings[0]);
}
} else {
link_res->hpo_dp_link_enc->funcs->set_ffe(
link_res->hpo_dp_link_enc,
link_settings,
lane_settings[0].FFE_PRESET.raw);
}
}
static void enable_hpo_fixed_vs_pe_retimer_dp_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings)
{
if (link_settings->lane_count == LANE_COUNT_FOUR)
enable_dio_fixed_vs_pe_retimer_program_4lane_output(link);
enable_hpo_dp_link_output(link, link_res, signal, clock_source, link_settings);
}
static const struct link_hwss hpo_fixed_vs_pe_retimer_dp_link_hwss = {
.setup_stream_encoder = setup_hpo_dp_stream_encoder,
.reset_stream_encoder = reset_hpo_dp_stream_encoder,
.setup_stream_attribute = setup_hpo_dp_stream_attribute,
.disable_link_output = disable_hpo_dp_link_output,
.setup_audio_output = setup_hpo_dp_audio_output,
.enable_audio_packet = enable_hpo_dp_audio_packet,
.disable_audio_packet = disable_hpo_dp_audio_packet,
.ext = {
.set_throttled_vcp_size = set_hpo_dp_throttled_vcp_size,
.set_hblank_min_symbol_width = set_hpo_dp_hblank_min_symbol_width,
.enable_dp_link_output = enable_hpo_fixed_vs_pe_retimer_dp_link_output,
.set_dp_link_test_pattern = set_hpo_fixed_vs_pe_retimer_dp_link_test_pattern,
.set_dp_lane_settings = set_hpo_fixed_vs_pe_retimer_dp_lane_settings,
.update_stream_allocation_table = update_hpo_dp_stream_allocation_table,
},
};
bool requires_fixed_vs_pe_retimer_hpo_link_hwss(const struct dc_link *link)
{
return requires_fixed_vs_pe_retimer_dio_link_hwss(link);
}
const struct link_hwss *get_hpo_fixed_vs_pe_retimer_dp_link_hwss(void)
{
return &hpo_fixed_vs_pe_retimer_dp_link_hwss;
}
@@ -0,0 +1,33 @@
/*
* 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_HWSS_HPO_FIXED_VS_PE_RETIMER_DP_H__
#define __LINK_HWSS_HPO_FIXED_VS_PE_RETIMER_DP_H__
#include "link_service.h"
bool requires_fixed_vs_pe_retimer_hpo_link_hwss(const struct dc_link *link);
const struct link_hwss *get_hpo_fixed_vs_pe_retimer_dp_link_hwss(void);
#endif /* __LINK_HWSS_HPO_FIXED_VS_PE_RETIMER_DP_H__ */
@@ -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;
}
@@ -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__ */
@@ -0,0 +1,62 @@
/*
* 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_virtual.h"
void virtual_setup_stream_encoder(struct pipe_ctx *pipe_ctx)
{
(void)pipe_ctx;
}
void virtual_setup_stream_attribute(struct pipe_ctx *pipe_ctx)
{
(void)pipe_ctx;
}
void virtual_reset_stream_encoder(struct pipe_ctx *pipe_ctx)
{
(void)pipe_ctx;
}
static void virtual_disable_link_output(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal)
{
(void)link;
(void)link_res;
(void)signal;
}
static const struct link_hwss virtual_link_hwss = {
.setup_stream_encoder = virtual_setup_stream_encoder,
.reset_stream_encoder = virtual_reset_stream_encoder,
.setup_stream_attribute = virtual_setup_stream_attribute,
.disable_link_output = virtual_disable_link_output,
};
const struct link_hwss *get_virtual_link_hwss(void)
{
return &virtual_link_hwss;
}
@@ -0,0 +1,35 @@
/*
* 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 __DC_LINK_HWSS_VIRTUAL_H__
#define __DC_LINK_HWSS_VIRTUAL_H__
#include "core_types.h"
void virtual_setup_stream_encoder(struct pipe_ctx *pipe_ctx);
void virtual_setup_stream_attribute(struct pipe_ctx *pipe_ctx);
void virtual_reset_stream_encoder(struct pipe_ctx *pipe_ctx);
const struct link_hwss *get_virtual_link_hwss(void);
#endif /* __DC_LINK_HWSS_VIRTUAL_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,43 @@
/*
* 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 __DC_LINK_DETECTION_H__
#define __DC_LINK_DETECTION_H__
#include "link_service.h"
bool link_detect(struct dc_link *link, enum dc_detect_reason reason);
bool link_detect_connection_type(struct dc_link *link,
enum dc_connection_type *type);
struct dc_sink *link_add_remote_sink(
struct dc_link *link,
const uint8_t *edid,
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);
const struct dc_link_status *link_get_status(const struct dc_link *link);
bool link_is_hdcp14(struct dc_link *link, enum signal_type signal);
bool link_is_hdcp22(struct dc_link *link, enum signal_type signal);
void link_clear_dprx_states(struct dc_link *link);
#endif /* __DC_LINK_DETECTION_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
/*
* 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 __DC_LINK_DPMS_H__
#define __DC_LINK_DPMS_H__
#include "link_service.h"
void link_set_dpms_on(
struct dc_state *state,
struct pipe_ctx *pipe_ctx);
void 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);
void link_blank_dp_stream(struct dc_link *link, bool hw_init);
void link_set_all_streams_dpms_off_for_link(struct dc_link *link);
void link_get_master_pipes_with_dpms_on(const struct dc_link *link,
struct dc_state *state,
uint8_t *count,
struct pipe_ctx *pipes[MAX_PIPES]);
enum dc_status link_increase_mst_payload(struct pipe_ctx *pipe_ctx, uint32_t req_pbn);
enum dc_status link_reduce_mst_payload(struct pipe_ctx *pipe_ctx, uint32_t req_pbn);
bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx,
bool enable, bool immediate_update);
struct fixed31_32 link_calculate_sst_avg_time_slots_per_mtp(
const struct dc_stream_state *stream,
const struct dc_link *link);
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__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
/*
* 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_FACTORY_H__
#define __LINK_FACTORY_H__
#include "link_service.h"
struct dc_link *link_create(const struct link_init_data *init_params);
void link_destroy(struct dc_link **link);
#endif /* __LINK_FACTORY_H__ */
@@ -0,0 +1,62 @@
/*
* 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 "virtual/virtual_link_hwss.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;
int odm_combine_num_segments = 1;
/* 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,
&stream->timing,
&stream->link->frl_link_settings.borrow_params,
odm_combine_num_segments);
}
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,
};
bool can_use_hpo_frl_link_hwss(const struct dc_link *link,
const struct link_resource *link_res)
{
return link_res->hpo_frl_link_enc != NULL;
}
const struct link_hwss *get_hpo_frl_link_hwss(void)
{
return &hpo_frl_link_hwss;
}
@@ -0,0 +1,114 @@
/*
* 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
*
*/
/* FILE POLICY AND INTENDED USAGE:
* This file implements accessors to link resource.
*/
#include "link_resource.h"
#include "protocols/link_dp_capability.h"
void link_get_cur_link_res(const struct dc_link *link,
struct link_resource *link_res)
{
int i;
struct pipe_ctx *pipe = NULL;
memset(link_res, 0, sizeof(*link_res));
for (i = 0; i < MAX_PIPES; i++) {
pipe = &link->dc->current_state->res_ctx.pipe_ctx[i];
if (pipe->stream && pipe->stream->link && pipe->top_pipe == NULL) {
if (pipe->stream->link == link) {
*link_res = pipe->link_res;
break;
}
}
}
}
void link_get_cur_res_map(const struct dc *dc, uint32_t *map)
{
struct dc_link *link;
uint32_t i;
uint32_t hpo_dp_recycle_map = 0;
*map = 0;
if (dc->caps.dp_hpo) {
for (i = 0; i < dc->caps.max_links; i++) {
link = dc->links[i];
if (link->link_status.link_active &&
link_dp_get_encoding_format(&link->reported_link_cap) == DP_128b_132b_ENCODING &&
link_dp_get_encoding_format(&link->cur_link_settings) != DP_128b_132b_ENCODING)
/* hpo dp link encoder is considered as recycled, when RX reports 128b/132b encoding capability
* but current link doesn't use it.
*/
hpo_dp_recycle_map |= (1 << i);
}
*map |= (hpo_dp_recycle_map << LINK_RES_HPO_DP_REC_MAP__SHIFT);
}
}
void link_restore_res_map(const struct dc *dc, uint32_t *map)
{
struct dc_link *link;
uint32_t i;
unsigned int available_hpo_dp_count;
uint32_t hpo_dp_recycle_map = (*map & LINK_RES_HPO_DP_REC_MAP__MASK)
>> LINK_RES_HPO_DP_REC_MAP__SHIFT;
if (dc->caps.dp_hpo) {
available_hpo_dp_count = dc->res_pool->hpo_dp_link_enc_count;
/* remove excess 128b/132b encoding support for not recycled links */
for (i = 0; i < dc->caps.max_links; i++) {
if ((hpo_dp_recycle_map & (1 << i)) == 0) {
link = dc->links[i];
if (link->type != dc_connection_none &&
link_dp_get_encoding_format(&link->verified_link_cap) == DP_128b_132b_ENCODING) {
if (available_hpo_dp_count > 0)
available_hpo_dp_count--;
else
/* remove 128b/132b encoding capability by limiting verified link rate to HBR3 */
link->verified_link_cap.link_rate = LINK_RATE_HIGH3;
}
}
}
/* remove excess 128b/132b encoding support for recycled links */
for (i = 0; i < dc->caps.max_links; i++) {
if ((hpo_dp_recycle_map & (1 << i)) != 0) {
link = dc->links[i];
if (link->type != dc_connection_none &&
link_dp_get_encoding_format(&link->verified_link_cap) == DP_128b_132b_ENCODING) {
if (available_hpo_dp_count > 0)
available_hpo_dp_count--;
else
/* remove 128b/132b encoding capability by limiting verified link rate to HBR3 */
link->verified_link_cap.link_rate = LINK_RATE_HIGH3;
}
}
}
}
}
@@ -0,0 +1,32 @@
/*
* 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_RESOURCE_H__
#define __LINK_RESOURCE_H__
#include "link_service.h"
void link_get_cur_res_map(const struct dc *dc, uint32_t *map);
void link_restore_res_map(const struct dc *dc, uint32_t *map);
void link_get_cur_link_res(const struct dc_link *link,
struct link_resource *link_res);
#endif /* __LINK_RESOURCE_H__ */
@@ -0,0 +1,927 @@
/*
* 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
*
*/
/* FILE POLICY AND INTENDED USAGE:
* This file owns timing validation against various link limitations. (ex.
* link bandwidth, receiver capability or our hardware capability) It also
* provides helper functions exposing bandwidth formulas used in validation.
*/
#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)
static uint32_t get_tmds_output_pixel_clock_100hz(const struct dc_crtc_timing *timing)
{
uint32_t pxl_clk = timing->pix_clk_100hz;
if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
pxl_clk /= 2;
else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422)
pxl_clk = pxl_clk * 2 / 3;
if (timing->display_color_depth == COLOR_DEPTH_101010)
pxl_clk = pxl_clk * 10 / 8;
else if (timing->display_color_depth == COLOR_DEPTH_121212)
pxl_clk = pxl_clk * 12 / 8;
return pxl_clk;
}
static bool dp_active_dongle_validate_timing(
const struct dc_crtc_timing *timing,
const struct dpcd_caps *dpcd_caps)
{
const struct dc_dongle_caps *dongle_caps = &dpcd_caps->dongle_caps;
switch (dpcd_caps->dongle_type) {
case DISPLAY_DONGLE_DP_VGA_CONVERTER:
case DISPLAY_DONGLE_DP_DVI_CONVERTER:
case DISPLAY_DONGLE_DP_DVI_DONGLE:
if (timing->pixel_encoding == PIXEL_ENCODING_RGB)
return true;
else
return false;
default:
break;
}
if (dpcd_caps->dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER &&
dongle_caps->extendedCapValid == true) {
/* Check Pixel Encoding */
switch (timing->pixel_encoding) {
case PIXEL_ENCODING_RGB:
case PIXEL_ENCODING_YCBCR444:
break;
case PIXEL_ENCODING_YCBCR422:
if (!dongle_caps->is_dp_hdmi_ycbcr422_pass_through)
return false;
break;
case PIXEL_ENCODING_YCBCR420:
if (!dongle_caps->is_dp_hdmi_ycbcr420_pass_through)
return false;
break;
case PIXEL_ENCODING_UNDEFINED:
/* These color depths are currently not supported */
ASSERT(false);
break;
default:
/* Invalid Pixel Encoding*/
return false;
}
switch (timing->display_color_depth) {
case COLOR_DEPTH_666:
case COLOR_DEPTH_888:
/*888 and 666 should always be supported*/
break;
case COLOR_DEPTH_101010:
if (dongle_caps->dp_hdmi_max_bpc < 10)
return false;
break;
case COLOR_DEPTH_121212:
if (dongle_caps->dp_hdmi_max_bpc < 12)
return false;
break;
case COLOR_DEPTH_UNDEFINED:
/* These color depths are currently not supported */
ASSERT(false);
break;
case COLOR_DEPTH_141414:
case COLOR_DEPTH_161616:
default:
/* These color depths are currently not supported */
return false;
}
/* Check 3D format */
switch (timing->timing_3d_format) {
case TIMING_3D_FORMAT_NONE:
case TIMING_3D_FORMAT_FRAME_ALTERNATE:
/*Only frame alternate 3D is supported on active dongle*/
break;
default:
/*other 3D formats are not supported due to bad infoframe translation */
return false;
}
if (dongle_caps->dp_hdmi_frl_max_link_bw_in_kbps > 0) { // DP to HDMI FRL converter
struct dc_crtc_timing outputTiming = *timing;
if (timing->flags.DSC && !timing->dsc_cfg.is_frl)
/* DP input has DSC, HDMI FRL output doesn't have DSC, remove DSC from output timing */
outputTiming.flags.DSC = 0;
if (dc_bandwidth_in_kbps_from_timing(&outputTiming, DC_LINK_ENCODING_HDMI_FRL) >
dongle_caps->dp_hdmi_frl_max_link_bw_in_kbps)
return false;
} else { // DP to HDMI TMDS converter
if (get_tmds_output_pixel_clock_100hz(timing) > (dongle_caps->dp_hdmi_max_pixel_clk_in_khz * 10))
return false;
}
}
if (dpcd_caps->channel_coding_cap.bits.DP_128b_132b_SUPPORTED == 0 &&
dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT == 0 &&
dongle_caps->dfp_cap_ext.supported) {
if (dongle_caps->dfp_cap_ext.max_pixel_rate_in_mps < (timing->pix_clk_100hz / 10000))
return false;
if (dongle_caps->dfp_cap_ext.max_video_h_active_width < timing->h_addressable)
return false;
if (dongle_caps->dfp_cap_ext.max_video_v_active_height < timing->v_addressable)
return false;
if (timing->pixel_encoding == PIXEL_ENCODING_RGB) {
if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb)
return false;
if (timing->display_color_depth == COLOR_DEPTH_666 &&
!dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_6bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_888 &&
!dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_8bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_101010 &&
!dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_10bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_121212 &&
!dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_12bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_161616 &&
!dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_16bpc)
return false;
} else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb)
return false;
if (timing->display_color_depth == COLOR_DEPTH_888 &&
!dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_8bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_101010 &&
!dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_10bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_121212 &&
!dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_12bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_161616 &&
!dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_16bpc)
return false;
} else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb)
return false;
if (timing->display_color_depth == COLOR_DEPTH_888 &&
!dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_8bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_101010 &&
!dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_10bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_121212 &&
!dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_12bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_161616 &&
!dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_16bpc)
return false;
} else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) {
if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb)
return false;
if (timing->display_color_depth == COLOR_DEPTH_888 &&
!dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_8bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_101010 &&
!dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_10bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_121212 &&
!dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_12bpc)
return false;
else if (timing->display_color_depth == COLOR_DEPTH_161616 &&
!dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_16bpc)
return false;
}
}
return true;
}
uint32_t dp_link_bandwidth_kbps(
const struct dc_link *link,
const struct dc_link_settings *link_settings)
{
uint32_t total_data_bw_efficiency_x10000 = 0;
uint32_t link_rate_per_lane_kbps = 0;
switch (link_dp_get_encoding_format(link_settings)) {
case DP_8b_10b_ENCODING:
/* For 8b/10b encoding:
* link rate is defined in the unit of LINK_RATE_REF_FREQ_IN_KHZ per DP byte per lane.
* data bandwidth efficiency is 80% with additional 3% overhead if FEC is supported.
*/
link_rate_per_lane_kbps = link_settings->link_rate * LINK_RATE_REF_FREQ_IN_KHZ * BITS_PER_DP_BYTE;
total_data_bw_efficiency_x10000 = DATA_EFFICIENCY_8b_10b_x10000;
if (dp_should_enable_fec(link)) {
total_data_bw_efficiency_x10000 /= 100;
total_data_bw_efficiency_x10000 *= DATA_EFFICIENCY_8b_10b_FEC_EFFICIENCY_x100;
}
break;
case DP_128b_132b_ENCODING:
/* For 128b/132b encoding:
* link rate is defined in the unit of 10mbps per lane.
* total data bandwidth efficiency is always 96.71%.
*/
link_rate_per_lane_kbps = link_settings->link_rate * 10000;
total_data_bw_efficiency_x10000 = DATA_EFFICIENCY_128b_132b_x10000;
break;
default:
break;
}
/* overall effective link bandwidth = link rate per lane * lane count * total data bandwidth efficiency */
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)
{
return dc_bandwidth_in_kbps_from_timing(timing,
dc_link_get_highest_encoding_format(link));
}
static bool dp_validate_mode_timing(
struct dc_link *link,
const struct dc_crtc_timing *timing)
{
uint32_t req_bw;
uint32_t max_bw;
const struct dc_link_settings *link_setting;
/* According to spec, VSC SDP should be used if pixel format is YCbCr420 */
if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 &&
!link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED &&
dal_graphics_object_id_get_connector_id(link->link_id) != CONNECTOR_ID_VIRTUAL)
return false;
/*always DP fail safe mode*/
if ((timing->pix_clk_100hz / 10) == (uint32_t) 25175 &&
timing->h_addressable == (uint32_t) 640 &&
timing->v_addressable == (uint32_t) 480)
return true;
link_setting = dp_get_verified_link_cap(link);
/* TODO: DYNAMIC_VALIDATION needs to be implemented */
/*if (flags.DYNAMIC_VALIDATION == 1 &&
link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN)
link_setting = &link->verified_link_cap;
*/
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 > max_uncompressed_pixel_rate_100hz;
if (is_max_uncompressed_pixel_rate_exceeded && !timing->flags.DSC) {
return false;
}
if (req_bw <= max_bw) {
/* 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
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,
const struct dc_crtc_timing *timing)
{
uint32_t max_pix_clk = stream->link->dongle_max_pix_clk * 10;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
/* A hack to avoid failing any modes for EDID override feature on
* topology change such as lower quality cable for DP or different dongle
*/
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;
/* Active Dongle*/
if (!dp_active_dongle_validate_timing(timing, dpcd_caps))
return DC_EXCEED_DONGLE_CAP;
switch (stream->signal) {
case SIGNAL_TYPE_EDP:
case SIGNAL_TYPE_DISPLAY_PORT:
if (!dp_validate_mode_timing(
link,
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;
}
return DC_OK;
}
static const struct dc_tunnel_settings *get_dp_tunnel_settings(const struct dc_state *context,
const struct dc_stream_state *stream)
{
int i;
const struct dc_tunnel_settings *dp_tunnel_settings = NULL;
for (i = 0; i < MAX_PIPES; i++) {
if (context->res_ctx.pipe_ctx[i].stream && (context->res_ctx.pipe_ctx[i].stream == stream)) {
dp_tunnel_settings = &context->res_ctx.pipe_ctx[i].link_config.dp_tunnel_settings;
break;
}
}
return dp_tunnel_settings;
}
/*
* Calculates the DP tunneling bandwidth required for the stream timing
* and aggregates the stream bandwidth for the respective DP tunneling link
*
* return: dc_status
*/
enum dc_status link_validate_dp_tunnel_bandwidth(const struct dc *dc, const struct dc_state *new_ctx)
{
(void)dc;
struct dc_validation_dpia_set dpia_link_sets[MAX_DPIA_NUM] = { 0 };
uint8_t link_count = 0;
enum dc_status result = DC_OK;
// Iterate through streams in the new context
for (uint8_t i = 0; (i < MAX_PIPES && i < new_ctx->stream_count); i++) {
const struct dc_stream_state *stream = new_ctx->streams[i];
const struct dc_link *link;
const struct dc_tunnel_settings *dp_tunnel_settings;
uint32_t timing_bw;
if (stream == NULL)
continue;
link = stream->link;
if (!(link && (stream->signal == SIGNAL_TYPE_DISPLAY_PORT
|| stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)))
continue;
if ((link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) && (link->hpd_status == false))
continue;
dp_tunnel_settings = get_dp_tunnel_settings(new_ctx, stream);
if ((dp_tunnel_settings == NULL) || (dp_tunnel_settings->should_use_dp_bw_allocation == false))
continue;
timing_bw = dp_get_timing_bandwidth_kbps(&stream->timing, link);
// Find an existing entry for this 'link' in 'dpia_link_sets'
for (uint8_t j = 0; j < MAX_DPIA_NUM; j++) {
bool is_new_slot = false;
if (dpia_link_sets[j].link == NULL) {
is_new_slot = true;
link_count++;
dpia_link_sets[j].required_bw = 0;
dpia_link_sets[j].link = link;
}
if (is_new_slot || (dpia_link_sets[j].link == link)) {
dpia_link_sets[j].tunnel_settings = dp_tunnel_settings;
dpia_link_sets[j].required_bw += timing_bw;
break;
}
}
}
if (link_count && link_dpia_validate_dp_tunnel_bandwidth(dpia_link_sets, link_count) == false)
result = DC_FAIL_DP_TUNNEL_BW_VALIDATE;
return result;
}
struct dp_audio_layout_config {
uint8_t layouts_per_sample_denom;
uint8_t symbols_per_layout;
uint8_t max_layouts_per_audio_sdp;
};
static void get_audio_layout_config(
uint32_t channel_count,
enum dp_link_encoding encoding,
struct dp_audio_layout_config *output)
{
memset(output, 0, sizeof(struct dp_audio_layout_config));
/* Assuming L-PCM audio. Current implementation uses max 1 layout per SDP,
* with each layout being the same size (8ch layout).
*/
if (encoding == DP_8b_10b_ENCODING) {
if (channel_count == 2) {
output->layouts_per_sample_denom = 4;
output->symbols_per_layout = 40;
output->max_layouts_per_audio_sdp = 1;
} else if (channel_count == 8 || channel_count == 6) {
output->layouts_per_sample_denom = 1;
output->symbols_per_layout = 40;
output->max_layouts_per_audio_sdp = 1;
}
} else if (encoding == DP_128b_132b_ENCODING) {
if (channel_count == 2) {
output->layouts_per_sample_denom = 4;
output->symbols_per_layout = 10;
output->max_layouts_per_audio_sdp = 1;
} else if (channel_count == 8 || channel_count == 6) {
output->layouts_per_sample_denom = 1;
output->symbols_per_layout = 10;
output->max_layouts_per_audio_sdp = 1;
}
}
}
static uint32_t get_av_stream_map_lane_count(
enum dp_link_encoding encoding,
enum dc_lane_count lane_count,
bool is_mst)
{
uint32_t av_stream_map_lane_count = 0;
if (encoding == DP_8b_10b_ENCODING) {
if (!is_mst)
av_stream_map_lane_count = lane_count;
else
av_stream_map_lane_count = 4;
} else if (encoding == DP_128b_132b_ENCODING) {
av_stream_map_lane_count = 4;
}
ASSERT(av_stream_map_lane_count != 0);
return av_stream_map_lane_count;
}
static uint32_t get_audio_sdp_overhead(
enum dp_link_encoding encoding,
enum dc_lane_count lane_count,
bool is_mst)
{
uint32_t audio_sdp_overhead = 0;
if (encoding == DP_8b_10b_ENCODING) {
if (is_mst)
audio_sdp_overhead = 16; /* 4 * 2 + 8 */
else
audio_sdp_overhead = lane_count * 2 + 8;
} else if (encoding == DP_128b_132b_ENCODING) {
audio_sdp_overhead = 10; /* 4 x 2.5 */
}
ASSERT(audio_sdp_overhead != 0);
return audio_sdp_overhead;
}
/* Current calculation only applicable for 8b/10b MST and 128b/132b SST/MST.
*/
static uint32_t calculate_overhead_hblank_bw_in_symbols(
uint32_t max_slice_h)
{
uint32_t overhead_hblank_bw = 0; /* in stream symbols */
overhead_hblank_bw += max_slice_h * 4; /* EOC overhead */
overhead_hblank_bw += 12; /* Main link overhead (VBID, BS/BE) */
return overhead_hblank_bw;
}
uint32_t dp_required_hblank_size_bytes(
const struct dc_link *link,
struct dp_audio_bandwidth_params *audio_params)
{
/* Main logic from dce_audio is duplicated here, with the main
* difference being:
* - Pre-determined lane count of 4
* - Assumed 16 dsc slices for worst case
* - Assumed SDP split disabled for worst case
* TODO: Unify logic from dce_audio to prevent duplicated logic.
*/
const struct dc_crtc_timing *timing = audio_params->crtc_timing;
const uint32_t channel_count = audio_params->channel_count;
const uint32_t sample_rate_hz = audio_params->sample_rate_hz;
const enum dp_link_encoding link_encoding = audio_params->link_encoding;
// 8b/10b MST and 128b/132b are always 4 logical lanes.
const uint32_t lane_count = 4;
const bool is_mst = (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT);
// Maximum slice count is with ODM 4:1, 4 slices per DSC
const uint32_t max_slices_h = 16;
const uint32_t av_stream_map_lane_count = get_av_stream_map_lane_count(
link_encoding, lane_count, is_mst);
const uint32_t audio_sdp_overhead = get_audio_sdp_overhead(
link_encoding, lane_count, is_mst);
struct dp_audio_layout_config layout_config;
if (link_encoding == DP_8b_10b_ENCODING && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT)
return 0;
get_audio_layout_config(
channel_count, link_encoding, &layout_config);
/* DP spec recommends between 1.05 to 1.1 safety margin to prevent sample under-run */
struct fixed31_32 audio_sdp_margin = dc_fixpt_from_fraction(110, 100);
struct fixed31_32 horizontal_line_freq_khz = dc_fixpt_from_fraction(
timing->pix_clk_100hz, (long long)timing->h_total * 10);
struct fixed31_32 samples_per_line;
struct fixed31_32 layouts_per_line;
struct fixed31_32 symbols_per_sdp_max_layout;
struct fixed31_32 remainder;
uint32_t num_sdp_with_max_layouts;
uint32_t required_symbols_per_hblank;
uint32_t required_bytes_per_hblank = 0;
samples_per_line = dc_fixpt_from_fraction(sample_rate_hz, 1000);
samples_per_line = dc_fixpt_div(samples_per_line, horizontal_line_freq_khz);
layouts_per_line = dc_fixpt_div_int(samples_per_line, layout_config.layouts_per_sample_denom);
// HBlank expansion usage assumes SDP split disabled to allow for worst case.
layouts_per_line = dc_fixpt_from_int(dc_fixpt_ceil(layouts_per_line));
num_sdp_with_max_layouts = dc_fixpt_floor(
dc_fixpt_div_int(layouts_per_line, layout_config.max_layouts_per_audio_sdp));
symbols_per_sdp_max_layout = dc_fixpt_from_int(
layout_config.max_layouts_per_audio_sdp * layout_config.symbols_per_layout);
symbols_per_sdp_max_layout = dc_fixpt_add_int(symbols_per_sdp_max_layout, audio_sdp_overhead);
symbols_per_sdp_max_layout = dc_fixpt_mul(symbols_per_sdp_max_layout, audio_sdp_margin);
required_symbols_per_hblank = num_sdp_with_max_layouts;
required_symbols_per_hblank *= ((dc_fixpt_ceil(symbols_per_sdp_max_layout) + av_stream_map_lane_count) /
av_stream_map_lane_count) * av_stream_map_lane_count;
if (num_sdp_with_max_layouts != dc_fixpt_ceil(
dc_fixpt_div_int(layouts_per_line, layout_config.max_layouts_per_audio_sdp))) {
remainder = dc_fixpt_sub_int(layouts_per_line,
num_sdp_with_max_layouts * layout_config.max_layouts_per_audio_sdp);
remainder = dc_fixpt_mul_int(remainder, layout_config.symbols_per_layout);
remainder = dc_fixpt_add_int(remainder, audio_sdp_overhead);
remainder = dc_fixpt_mul(remainder, audio_sdp_margin);
required_symbols_per_hblank += ((dc_fixpt_ceil(remainder) + av_stream_map_lane_count) /
av_stream_map_lane_count) * av_stream_map_lane_count;
}
required_symbols_per_hblank += calculate_overhead_hblank_bw_in_symbols(max_slices_h);
if (link_encoding == DP_8b_10b_ENCODING)
required_bytes_per_hblank = required_symbols_per_hblank; // 8 bits per 8b/10b symbol
else if (link_encoding == DP_128b_132b_ENCODING)
required_bytes_per_hblank = required_symbols_per_hblank * 4; // 32 bits per 128b/132b symbol
return required_bytes_per_hblank;
}
@@ -0,0 +1,58 @@
/*
* 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_VALIDATION_H__
#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,
const struct dc_crtc_timing *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(
const struct dc_link *link,
struct dp_audio_bandwidth_params *audio_params);
#endif /* __LINK_VALIDATION_H__ */
@@ -0,0 +1,702 @@
/*
* Copyright 2012-15 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 generic display communication protocols such as i2c, aux
* and scdc. The file should not contain any specific applications of these
* protocols such as display capability query, detection, or handshaking such as
* link training.
*/
#include "link_ddc.h"
#include "link_hdmi_frl.h"
#include "vector.h"
#include "dce/dce_aux.h"
#include "dal_asic_id.h"
#include "link_dpcd.h"
#include "dm_helpers.h"
#include "atomfirmware.h"
#define DC_LOGGER \
ddc_service->ctx->logger
#define DC_LOGGER_INIT(logger)
static const uint8_t DP_VGA_DONGLE_BRANCH_DEV_NAME[] = "DpVga";
/* DP to Dual link DVI converter */
static const uint8_t DP_DVI_CONVERTER_ID_4[] = "m2DVIa";
static const uint8_t DP_DVI_CONVERTER_ID_5[] = "3393N2";
struct i2c_payloads {
struct vector payloads;
};
static bool i2c_payloads_create(
struct dc_context *ctx,
struct i2c_payloads *payloads,
uint32_t count)
{
if (dal_vector_construct(
&payloads->payloads, ctx, count, sizeof(struct i2c_payload)))
return true;
return false;
}
static struct i2c_payload *i2c_payloads_get(struct i2c_payloads *p)
{
return (struct i2c_payload *)p->payloads.container;
}
static uint32_t i2c_payloads_get_count(struct i2c_payloads *p)
{
return p->payloads.count;
}
static void i2c_payloads_destroy(struct i2c_payloads *p)
{
if (!p)
return;
dal_vector_destruct(&p->payloads);
}
#define DDC_MIN(a, b) (((a) < (b)) ? (a) : (b))
static void i2c_payloads_add(
struct i2c_payloads *payloads,
uint32_t address,
uint32_t len,
uint8_t *data,
bool write)
{
uint32_t payload_size = EDID_SEGMENT_SIZE;
uint32_t pos;
for (pos = 0; pos < len; pos += payload_size) {
struct i2c_payload payload = {
.write = write,
.address = (uint8_t)address,
.length = DDC_MIN(payload_size, len - pos),
.data = data + pos };
dal_vector_append(&payloads->payloads, &payload);
}
}
static void ddc_service_construct(
struct ddc_service *ddc_service,
struct ddc_service_init_data *init_data)
{
enum connector_id connector_id =
dal_graphics_object_id_get_connector_id(init_data->id);
struct gpio_service *gpio_service = init_data->ctx->gpio_service;
struct graphics_object_i2c_info i2c_info;
struct gpio_ddc_hw_info hw_info;
struct dc_bios *dcb = init_data->ctx->dc_bios;
ddc_service->link = init_data->link;
ddc_service->ctx = init_data->ctx;
if (ddc_service->link && ddc_service->ctx->dc->config.dp_connector_no_native_i2c &&
ddc_service->link->no_ddc_pin) {
// 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;
ddc_service->flags.FORCE_READ_REPEATED_START = false;
ddc_service->flags.EDID_STRESS_READ = false;
ddc_service->flags.IS_INTERNAL_DISPLAY =
connector_id == CONNECTOR_ID_EDP ||
connector_id == CONNECTOR_ID_LVDS;
ddc_service->wa.raw = 0;
}
struct ddc_service *link_create_ddc_service(
struct ddc_service_init_data *init_data)
{
struct ddc_service *ddc_service;
ddc_service = kzalloc_obj(struct ddc_service);
if (!ddc_service)
return NULL;
ddc_service_construct(ddc_service, init_data);
return ddc_service;
}
static void ddc_service_destruct(struct ddc_service *ddc)
{
if (ddc->ddc_pin)
dal_gpio_destroy_ddc(&ddc->ddc_pin);
}
void link_destroy_ddc_service(struct ddc_service **ddc)
{
if (!ddc || !*ddc) {
BREAK_TO_DEBUGGER();
return;
}
ddc_service_destruct(*ddc);
kfree(*ddc);
*ddc = NULL;
}
void set_ddc_transaction_type(
struct ddc_service *ddc,
enum ddc_transaction_type type)
{
ddc->transaction_type = type;
}
bool link_is_in_aux_transaction_mode(struct ddc_service *ddc)
{
switch (ddc->transaction_type) {
case DDC_TRANSACTION_TYPE_I2C_OVER_AUX:
case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER:
case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER:
return true;
default:
break;
}
return false;
}
void set_dongle_type(struct ddc_service *ddc,
enum display_dongle_type dongle_type)
{
ddc->dongle_type = dongle_type;
}
static uint32_t defer_delay_converter_wa(
struct ddc_service *ddc,
uint32_t defer_delay)
{
struct dc_link *link = ddc->link;
if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER &&
link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_0080E1 &&
(link->dpcd_caps.branch_fw_revision[0] < 0x01 ||
(link->dpcd_caps.branch_fw_revision[0] == 0x01 &&
link->dpcd_caps.branch_fw_revision[1] < 0x40)) &&
!memcmp(link->dpcd_caps.branch_dev_name,
DP_VGA_DONGLE_BRANCH_DEV_NAME,
sizeof(link->dpcd_caps.branch_dev_name)))
return defer_delay > DPVGA_DONGLE_AUX_DEFER_WA_DELAY ?
defer_delay : DPVGA_DONGLE_AUX_DEFER_WA_DELAY;
if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_0080E1 &&
!memcmp(link->dpcd_caps.branch_dev_name,
DP_DVI_CONVERTER_ID_4,
sizeof(link->dpcd_caps.branch_dev_name)))
return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY ?
defer_delay : I2C_OVER_AUX_DEFER_WA_DELAY;
if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_006037 &&
!memcmp(link->dpcd_caps.branch_dev_name,
DP_DVI_CONVERTER_ID_5,
sizeof(link->dpcd_caps.branch_dev_name)))
return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY_1MS ?
I2C_OVER_AUX_DEFER_WA_DELAY_1MS : defer_delay;
return defer_delay;
}
#define DP_TRANSLATOR_DELAY 5
uint32_t link_get_aux_defer_delay(struct ddc_service *ddc)
{
uint32_t defer_delay = 0;
switch (ddc->transaction_type) {
case DDC_TRANSACTION_TYPE_I2C_OVER_AUX:
if ((DISPLAY_DONGLE_DP_VGA_CONVERTER == ddc->dongle_type) ||
(DISPLAY_DONGLE_DP_DVI_CONVERTER == ddc->dongle_type) ||
(DISPLAY_DONGLE_DP_HDMI_CONVERTER ==
ddc->dongle_type)) {
defer_delay = DP_TRANSLATOR_DELAY;
defer_delay =
defer_delay_converter_wa(ddc, defer_delay);
} else /*sink has a delay different from an Active Converter*/
defer_delay = 0;
break;
case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER:
defer_delay = DP_TRANSLATOR_DELAY;
break;
default:
break;
}
return defer_delay;
}
static bool submit_aux_command(struct ddc_service *ddc,
struct aux_payload *payload)
{
uint32_t retrieved = 0;
bool ret = false;
if (!ddc)
return false;
if (!payload)
return false;
do {
struct aux_payload current_payload;
bool is_end_of_payload = (retrieved + DEFAULT_AUX_MAX_DATA_SIZE) >=
payload->length;
uint32_t payload_length = is_end_of_payload ?
payload->length - retrieved : DEFAULT_AUX_MAX_DATA_SIZE;
current_payload.address = payload->address;
current_payload.data = &payload->data[retrieved];
current_payload.defer_delay = payload->defer_delay;
current_payload.i2c_over_aux = payload->i2c_over_aux;
current_payload.length = payload_length;
/* set mot (middle of transaction) to false if it is the last payload */
current_payload.mot = is_end_of_payload ? payload->mot:true;
current_payload.write_status_update = false;
current_payload.reply = payload->reply;
current_payload.write = payload->write;
ret = link_aux_transfer_with_retries_no_mutex(ddc, &current_payload);
retrieved += payload_length;
} while (retrieved < payload->length && ret == true);
return ret;
}
bool link_query_ddc_data(
struct ddc_service *ddc,
uint32_t address,
uint8_t *write_buf,
uint32_t write_size,
uint8_t *read_buf,
uint32_t read_size)
{
bool success = true;
uint32_t payload_size =
link_is_in_aux_transaction_mode(ddc) ?
DEFAULT_AUX_MAX_DATA_SIZE : EDID_SEGMENT_SIZE;
uint32_t write_payloads =
(write_size + payload_size - 1) / payload_size;
uint32_t read_payloads =
(read_size + payload_size - 1) / payload_size;
uint32_t payloads_num = write_payloads + read_payloads;
if (!payloads_num)
return false;
if (link_is_in_aux_transaction_mode(ddc)) {
struct aux_payload payload;
payload.i2c_over_aux = true;
payload.address = address;
payload.reply = NULL;
payload.defer_delay = link_get_aux_defer_delay(ddc);
payload.write_status_update = false;
if (write_size != 0) {
payload.write = true;
/* should not set mot (middle of transaction) to 0
* if there are pending read payloads
*/
payload.mot = !(read_size == 0);
payload.length = write_size;
payload.data = write_buf;
success = submit_aux_command(ddc, &payload);
}
if (read_size != 0 && success) {
payload.write = false;
/* should set mot (middle of transaction) to 0
* since it is the last payload to send
*/
payload.mot = false;
payload.length = read_size;
payload.data = read_buf;
success = submit_aux_command(ddc, &payload);
}
} else {
struct i2c_command command = {0};
struct i2c_payloads payloads;
if (!i2c_payloads_create(ddc->ctx, &payloads, payloads_num))
return false;
command.payloads = i2c_payloads_get(&payloads);
command.number_of_payloads = 0;
command.engine = DDC_I2C_COMMAND_ENGINE;
command.speed = ddc->ctx->dc->caps.i2c_speed_in_khz;
i2c_payloads_add(
&payloads, address, write_size, write_buf, true);
i2c_payloads_add(
&payloads, address, read_size, read_buf, false);
command.number_of_payloads = (uint8_t)i2c_payloads_get_count(&payloads);
success = dm_helpers_submit_i2c(
ddc->ctx,
ddc->link,
&command);
i2c_payloads_destroy(&payloads);
}
return success;
}
int link_aux_transfer_raw(struct ddc_service *ddc,
struct aux_payload *payload,
enum aux_return_code_type *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)
{
uint32_t vendor_lttpr_write_address = 0xF004F;
uint8_t offset;
switch (link->dpcd_caps.lttpr_caps.phy_repeater_cnt) {
case 0x80: // 1 lttpr repeater
offset = 1;
break;
case 0x40: // 2 lttpr repeaters
offset = 2;
break;
case 0x20: // 3 lttpr repeaters
offset = 3;
break;
case 0x10: // 4 lttpr repeaters
offset = 4;
break;
case 0x08: // 5 lttpr repeaters
offset = 5;
break;
case 0x04: // 6 lttpr repeaters
offset = 6;
break;
case 0x02: // 7 lttpr repeaters
offset = 7;
break;
case 0x01: // 8 lttpr repeaters
offset = 8;
break;
default:
offset = 0xFF;
}
if (offset != 0xFF) {
vendor_lttpr_write_address +=
((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (offset - 1));
}
return vendor_lttpr_write_address;
}
uint32_t link_get_fixed_vs_pe_retimer_read_address(struct dc_link *link)
{
return link_get_fixed_vs_pe_retimer_write_address(link) + 4;
}
bool link_configure_fixed_vs_pe_retimer(struct ddc_service *ddc, const uint8_t *data, uint32_t length)
{
struct aux_payload write_payload = {
.i2c_over_aux = false,
.write = true,
.address = link_get_fixed_vs_pe_retimer_write_address(ddc->link),
.length = length,
.data = (uint8_t *) data,
.reply = NULL,
.mot = I2C_MOT_UNDEF,
.write_status_update = false,
.defer_delay = 0,
};
return link_aux_transfer_with_retries_no_mutex(ddc,
&write_payload);
}
bool link_query_fixed_vs_pe_retimer(struct ddc_service *ddc, uint8_t *data, uint32_t length)
{
struct aux_payload read_payload = {
.i2c_over_aux = false,
.write = false,
.address = link_get_fixed_vs_pe_retimer_read_address(ddc->link),
.length = length,
.data = data,
.reply = NULL,
.mot = I2C_MOT_UNDEF,
.write_status_update = false,
.defer_delay = 0,
};
return link_aux_transfer_with_retries_no_mutex(ddc,
&read_payload);
}
bool link_aux_transfer_with_retries_no_mutex(struct ddc_service *ddc,
struct aux_payload *payload)
{
return dce_aux_transfer_with_retries(ddc, payload);
}
bool try_to_configure_aux_timeout(struct ddc_service *ddc,
uint32_t timeout)
{
bool result = false;
struct ddc *ddc_pin = ddc->ddc_pin;
if (((ddc->link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) &&
!ddc->link->dc->debug.disable_fixed_vs_aux_timeout_wa &&
ddc->ctx->dce_version == DCN_VERSION_3_1) {
/* Fixed VS workaround for AUX timeout */
const uint32_t fixed_vs_address = 0xF004F;
const uint8_t fixed_vs_data[4] = {0x1, 0x22, 0x63, 0xc};
core_link_write_dpcd(ddc->link,
fixed_vs_address,
fixed_vs_data,
sizeof(fixed_vs_data));
timeout = 3072;
}
/* Do not try to access nonexistent DDC pin. */
if (ddc->link->ep_type != DISPLAY_ENDPOINT_PHY)
return true;
if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) {
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;
}
struct ddc *get_ddc_pin(struct ddc_service *ddc_service)
{
return ddc_service->ddc_pin;
}
void write_scdc_data(struct ddc_service *ddc_service,
uint32_t pix_clk,
bool lte_340_scramble)
{
bool over_340_mhz = pix_clk > 340000 ? 1 : 0;
uint8_t slave_address = HDMI_SCDC_ADDRESS;
uint8_t offset = HDMI_SCDC_SINK_VERSION;
uint8_t sink_version = 0;
uint8_t write_buffer[2] = {0};
/*Lower than 340 Scramble bit from SCDC caps*/
if (ddc_service->link->local_sink &&
(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));
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);
/*Read Request from SCDC caps*/
}
write_buffer[0] = HDMI_SCDC_TMDS_CONFIG;
if (over_340_mhz) {
write_buffer[1] = 3;
} else if (lte_340_scramble) {
write_buffer[1] = 1;
} else {
write_buffer[1] = 0;
}
link_query_ddc_data(ddc_service, slave_address, write_buffer,
sizeof(write_buffer), NULL, 0);
}
void read_scdc_data(struct ddc_service *ddc_service)
{
uint8_t slave_address = HDMI_SCDC_ADDRESS;
uint8_t offset = HDMI_SCDC_TMDS_CONFIG;
uint8_t tmds_config = 0;
if (ddc_service->link->local_sink &&
ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite)
return;
link_query_ddc_data(ddc_service, slave_address, &offset,
sizeof(offset), &tmds_config, sizeof(tmds_config));
if (tmds_config & 0x1) {
union hdmi_scdc_status_flags_data status_data = {0};
uint8_t scramble_status = 0;
offset = HDMI_SCDC_SCRAMBLER_STATUS;
link_query_ddc_data(ddc_service, slave_address,
&offset, sizeof(offset), &scramble_status,
sizeof(scramble_status));
offset = HDMI_SCDC_STATUS_FLAGS;
link_query_ddc_data(ddc_service, slave_address,
&offset, sizeof(offset), &status_data.byte,
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;
}
@@ -0,0 +1,110 @@
/*
* Copyright 2012-15 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 __DAL_DDC_SERVICE_H__
#define __DAL_DDC_SERVICE_H__
#include "link_service.h"
#define AUX_POWER_UP_WA_DELAY 500
#define I2C_OVER_AUX_DEFER_WA_DELAY 70
#define DPVGA_DONGLE_AUX_DEFER_WA_DELAY 40
#define I2C_OVER_AUX_DEFER_WA_DELAY_1MS 1
#define LINK_AUX_DEFAULT_LTTPR_TIMEOUT_PERIOD 3200 /*us*/
#define LINK_AUX_DEFAULT_TIMEOUT_PERIOD 552 /*us*/
#define EDID_SEGMENT_SIZE 256
struct ddc_service *link_create_ddc_service(
struct ddc_service_init_data *ddc_init_data);
void link_destroy_ddc_service(struct ddc_service **ddc);
void set_ddc_transaction_type(
struct ddc_service *ddc,
enum ddc_transaction_type type);
uint32_t link_get_aux_defer_delay(struct ddc_service *ddc);
bool link_is_in_aux_transaction_mode(struct ddc_service *ddc);
bool try_to_configure_aux_timeout(struct ddc_service *ddc,
uint32_t timeout);
bool link_query_ddc_data(
struct ddc_service *ddc,
uint32_t address,
uint8_t *write_buf,
uint32_t write_size,
uint8_t *read_buf,
uint32_t read_size);
/* Attempt to submit an aux payload, retrying on timeouts, defers, and busy
* states as outlined in the DP spec. Returns true if the request was
* successful.
*
* NOTE: The function requires explicit mutex on DM side in order to prevent
* potential race condition. DC components should call the dpcd read/write
* function in dm_helpers in order to access dpcd safely
*/
bool link_aux_transfer_with_retries_no_mutex(struct ddc_service *ddc,
struct aux_payload *payload);
bool link_configure_fixed_vs_pe_retimer(
struct ddc_service *ddc,
const uint8_t *data,
uint32_t length);
bool link_query_fixed_vs_pe_retimer(
struct ddc_service *ddc,
uint8_t *data,
uint32_t length);
uint32_t link_get_fixed_vs_pe_retimer_read_address(struct dc_link *link);
uint32_t link_get_fixed_vs_pe_retimer_write_address(struct dc_link *link);
void write_scdc_data(
struct ddc_service *ddc_service,
uint32_t pix_clk,
bool lte_340_scramble);
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);
struct ddc *get_ddc_pin(struct ddc_service *ddc_service);
int link_aux_transfer_raw(struct ddc_service *ddc,
struct aux_payload *payload,
enum aux_return_code_type *operation_result);
#endif /* __DAL_DDC_SERVICE_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,117 @@
/*
* 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 __DC_LINK_DP_CAPABILITY_H__
#define __DC_LINK_DP_CAPABILITY_H__
#include "link_service.h"
bool detect_dp_sink_caps(struct dc_link *link);
void detect_edp_sink_caps(struct dc_link *link);
struct dc_link_settings dp_get_max_link_cap(struct dc_link *link);
bool dp_get_max_link_enc_cap(const struct dc_link *link,
struct dc_link_settings *max_link_enc_cap);
const struct dc_link_settings *dp_get_verified_link_cap(
const struct dc_link *link);
enum dp_link_encoding link_dp_get_encoding_format(
const struct dc_link_settings *link_settings);
enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link);
/* Convert PHY repeater count read from DPCD uint8_t. */
uint8_t dp_parse_lttpr_repeater_count(uint8_t lttpr_repeater_count);
/* Calculate embedded LTTPR address offset for vendor-specific behaviour */
uint32_t dp_get_closest_lttpr_offset(uint8_t lttpr_count);
bool dp_is_sink_present(struct dc_link *link);
bool dp_is_lttpr_present(struct dc_link *link);
bool dp_is_fec_supported(const struct dc_link *link);
bool is_dp_active_dongle(const struct dc_link *link);
bool is_dp_branch_device(const struct dc_link *link);
void dpcd_write_cable_id_to_dprx(struct dc_link *link);
bool dp_should_enable_fec(const struct dc_link *link);
bool dp_is_128b_132b_signal(struct pipe_ctx *pipe_ctx);
/* Initialize output parameter lt_settings. */
void dp_decide_training_settings(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
struct link_training_settings *lt_settings);
bool link_decide_link_settings(
struct dc_stream_state *stream,
struct dc_link_settings *link_setting);
bool edp_decide_link_settings(struct dc_link *link,
struct dc_link_settings *link_setting, uint32_t req_bw);
bool decide_edp_link_settings_with_dsc(struct dc_link *link,
struct dc_link_settings *link_setting,
uint32_t req_bw,
enum dc_link_rate max_link_rate);
enum dp_link_encoding mst_decide_link_encoding_format(const struct dc_link *link);
void dpcd_set_source_specific_data(struct dc_link *link);
/*query dpcd for version and mst cap addresses*/
bool read_is_mst_supported(struct dc_link *link);
bool decide_fallback_link_setting(
struct dc_link *link,
struct dc_link_settings *max,
struct dc_link_settings *cur,
enum link_training_result training_result);
bool dp_verify_link_cap_with_retries(
struct dc_link *link,
struct dc_link_settings *known_limit_link_setting,
int attempts);
uint32_t link_bw_kbps_from_raw_frl_link_rate_data(uint8_t bw);
bool dp_overwrite_extended_receiver_cap(struct dc_link *link);
uint8_t dp_get_lttpr_count(struct dc_link *link);
void edp_get_alpm_support(struct dc_link *link,
bool *auxless_support,
bool *auxwake_support);
#endif /* __DC_LINK_DP_CAPABILITY_H__ */
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2021 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 "dc.h"
#include "inc/core_status.h"
#include "dpcd_defs.h"
#include "link_dp_dpia.h"
#include "link_hwss.h"
#include "dm_helpers.h"
#include "dmub/inc/dmub_cmd.h"
#include "link_dpcd.h"
#include "link_dp_training.h"
#include "dc_dmub_srv.h"
#define DC_LOGGER \
link->ctx->logger
/** @note Can remove once DP tunneling registers in upstream include/drm/drm_dp_helper.h */
/* DPCD DP Tunneling over USB4 */
#define DP_TUNNELING_CAPABILITIES_SUPPORT 0xe000d
#define DP_IN_ADAPTER_INFO 0xe000e
#define DP_USB4_DRIVER_ID 0xe000f
#define DP_USB4_ROUTER_TOPOLOGY_ID 0xe001b
enum dc_status dpcd_get_tunneling_device_data(struct dc_link *link)
{
enum dc_status status = DC_OK;
uint8_t dpcd_dp_tun_data[3] = {0};
uint8_t dpcd_topology_data[DPCD_USB4_TOPOLOGY_ID_LEN] = {0};
uint8_t i = 0;
status = core_link_read_dpcd(
link,
DP_TUNNELING_CAPABILITIES_SUPPORT,
dpcd_dp_tun_data,
sizeof(dpcd_dp_tun_data));
if (status != DC_OK)
goto err;
link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.raw =
dpcd_dp_tun_data[DP_TUNNELING_CAPABILITIES_SUPPORT - DP_TUNNELING_CAPABILITIES_SUPPORT];
if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling == false)
goto err;
link->dpcd_caps.usb4_dp_tun_info.dpia_info.raw =
dpcd_dp_tun_data[DP_IN_ADAPTER_INFO - DP_TUNNELING_CAPABILITIES_SUPPORT];
link->dpcd_caps.usb4_dp_tun_info.usb4_driver_id =
dpcd_dp_tun_data[DP_USB4_DRIVER_ID - DP_TUNNELING_CAPABILITIES_SUPPORT] & 0x0F;
if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc) {
status = core_link_read_dpcd(link, USB4_DRIVER_BW_CAPABILITY,
dpcd_dp_tun_data, 2);
if (status != DC_OK)
goto err;
link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.raw =
dpcd_dp_tun_data[USB4_DRIVER_BW_CAPABILITY - USB4_DRIVER_BW_CAPABILITY];
link->dpcd_caps.usb4_dp_tun_info.dpia_tunnel_info.raw =
dpcd_dp_tun_data[DP_IN_ADAPTER_TUNNEL_INFO - USB4_DRIVER_BW_CAPABILITY];
}
DC_LOG_DEBUG("%s: Link[%d] DP tunneling support (RouterId=%d AdapterId=%d) "
"DPIA_BW_Alloc_support=%d "
"CM_BW_Alloc_support=%d ",
__func__, link->link_index,
link->dpcd_caps.usb4_dp_tun_info.usb4_driver_id,
link->dpcd_caps.usb4_dp_tun_info.dpia_info.bits.dpia_num,
link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc,
link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support);
status = core_link_read_dpcd(
link,
DP_USB4_ROUTER_TOPOLOGY_ID,
dpcd_topology_data,
sizeof(dpcd_topology_data));
if (status != DC_OK)
goto err;
for (i = 0; i < DPCD_USB4_TOPOLOGY_ID_LEN; i++)
link->dpcd_caps.usb4_dp_tun_info.usb4_topology_id[i] = dpcd_topology_data[i];
err:
return status;
}
bool dpia_query_hpd_status(struct dc_link *link)
{
union dmub_rb_cmd cmd = {0};
struct dc_dmub_srv *dmub_srv = link->ctx->dmub_srv;
/* 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 = (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 */
if (dc_wake_and_execute_dmub_cmd(dmub_srv->ctx, &cmd,
DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) &&
cmd.query_hpd.data.status == AUX_RET_SUCCESS) {
DC_LOG_DEBUG("%s: for link(%d) dpia(%d) success, current_hpd_status(%d) new_hpd_status(%d)\n",
__func__,
link->link_index,
link->link_id.enum_id - ENUM_ID_1,
link->hpd_status,
cmd.query_hpd.data.result);
link->hpd_status = cmd.query_hpd.data.result;
} else {
DC_LOG_ERROR("%s: for link(%d) dpia(%d) failed with status(%d), current_hpd_status(%d) new_hpd_status(0)\n",
__func__,
link->link_index,
link->link_id.enum_id - ENUM_ID_1,
cmd.query_hpd.data.status,
link->hpd_status);
link->hpd_status = false;
}
return link->hpd_status;
}
void link_decide_dp_tunnel_settings(struct dc_stream_state *stream,
struct dc_tunnel_settings *dp_tunnel_setting)
{
struct dc_link *link = stream->link;
memset(dp_tunnel_setting, 0, sizeof(*dp_tunnel_setting));
if ((stream->signal == SIGNAL_TYPE_DISPLAY_PORT) || (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)) {
dp_tunnel_setting->should_enable_dp_tunneling =
link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling;
if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc
&& link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support) {
dp_tunnel_setting->should_use_dp_bw_allocation = true;
dp_tunnel_setting->cm_id = link->dpcd_caps.usb4_dp_tun_info.usb4_driver_id & 0x0F;
dp_tunnel_setting->group_id = link->dpcd_caps.usb4_dp_tun_info.dpia_tunnel_info.bits.group_id;
dp_tunnel_setting->estimated_bw = link->dpia_bw_alloc_config.estimated_bw;
dp_tunnel_setting->allocated_bw = link->dpia_bw_alloc_config.allocated_bw;
dp_tunnel_setting->bw_granularity = link->dpia_bw_alloc_config.bw_granularity;
}
}
}
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2021 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 __DC_LINK_DPIA_H__
#define __DC_LINK_DPIA_H__
#include "link_service.h"
/* Read tunneling device capability from DPCD and update link capability
* accordingly.
*/
enum dc_status dpcd_get_tunneling_device_data(struct dc_link *link);
/* Query hot plug status of USB4 DP tunnel.
* Returns true if HPD high.
*/
bool dpia_query_hpd_status(struct dc_link *link);
/* Decide the DP tunneling settings based on the DPCD capabilities
*/
void link_decide_dp_tunnel_settings(struct dc_stream_state *stream,
struct dc_tunnel_settings *dp_tunnel_setting);
#endif /* __DC_LINK_DPIA_H__ */
@@ -0,0 +1,448 @@
/*
* 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
*
*/
/*********************************************************************/
// USB4 DPIA BANDWIDTH ALLOCATION LOGIC
/*********************************************************************/
#include "link_dp_dpia_bw.h"
#include "link_dpcd.h"
#include "dc_dmub_srv.h"
#define DC_LOGGER \
link->ctx->logger
#define Kbps_TO_Gbps (1000 * 1000)
#define MST_TIME_SLOT_COUNT 64
// ------------------------------------------------------------------
// PRIVATE FUNCTIONS
// ------------------------------------------------------------------
/*
* Always Check the following:
* - Is it USB4 link?
* - Is HPD HIGH?
* - Is BW Allocation Support Mode enabled on DP-Tx?
*/
static bool link_dp_is_bw_alloc_available(struct dc_link *link)
{
return (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling
&& link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc
&& link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support);
}
static void reset_bw_alloc_struct(struct dc_link *link)
{
link->dpia_bw_alloc_config.bw_alloc_enabled = false;
link->dpia_bw_alloc_config.link_verified_bw = 0;
link->dpia_bw_alloc_config.link_max_bw = 0;
link->dpia_bw_alloc_config.allocated_bw = 0;
link->dpia_bw_alloc_config.estimated_bw = 0;
link->dpia_bw_alloc_config.bw_granularity = 0;
link->dpia_bw_alloc_config.dp_overhead = 0;
link->dpia_bw_alloc_config.nrd_max_lane_count = 0;
link->dpia_bw_alloc_config.nrd_max_link_rate = 0;
for (int i = 0; i < MAX_SINKS_PER_LINK; i++)
link->dpia_bw_alloc_config.remote_sink_req_bw[i] = 0;
DC_LOG_DEBUG("reset usb4 bw alloc of link(%d)\n", link->link_index);
}
#define BW_GRANULARITY_0 4 // 0.25 Gbps
#define BW_GRANULARITY_1 2 // 0.5 Gbps
#define BW_GRANULARITY_2 1 // 1 Gbps
static uint8_t get_bw_granularity(struct dc_link *link)
{
uint8_t bw_granularity = 0;
core_link_read_dpcd(
link,
DP_BW_GRANULALITY,
&bw_granularity,
sizeof(uint8_t));
switch (bw_granularity & 0x3) {
case 0:
bw_granularity = BW_GRANULARITY_0;
break;
case 1:
bw_granularity = BW_GRANULARITY_1;
break;
case 2:
default:
bw_granularity = BW_GRANULARITY_2;
break;
}
return bw_granularity;
}
static int get_estimated_bw(struct dc_link *link)
{
uint8_t bw_estimated_bw = 0;
core_link_read_dpcd(
link,
ESTIMATED_BW,
&bw_estimated_bw,
sizeof(uint8_t));
return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
}
static uint8_t get_non_reduced_max_link_rate(struct dc_link *link)
{
uint8_t nrd_max_link_rate = 0;
core_link_read_dpcd(
link,
DP_TUNNELING_MAX_LINK_RATE,
&nrd_max_link_rate,
sizeof(uint8_t));
return nrd_max_link_rate;
}
static uint8_t get_non_reduced_max_lane_count(struct dc_link *link)
{
uint8_t nrd_max_lane_count = 0;
core_link_read_dpcd(
link,
DP_TUNNELING_MAX_LANE_COUNT,
&nrd_max_lane_count,
sizeof(uint8_t));
return nrd_max_lane_count;
}
/*
* Read all New BW alloc configuration ex: estimated_bw, allocated_bw,
* granuality, Driver_ID, CM_Group, & populate the BW allocation structs
* for host router and dpia
*/
static void retrieve_usb4_dp_bw_allocation_info(struct dc_link *link)
{
reset_bw_alloc_struct(link);
/* init the known values */
link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
link->dpia_bw_alloc_config.nrd_max_link_rate = get_non_reduced_max_link_rate(link);
link->dpia_bw_alloc_config.nrd_max_lane_count = get_non_reduced_max_lane_count(link);
DC_LOG_DEBUG("%s: bw_granularity(%d), estimated_bw(%d)\n",
__func__, link->dpia_bw_alloc_config.bw_granularity,
link->dpia_bw_alloc_config.estimated_bw);
DC_LOG_DEBUG("%s: nrd_max_link_rate(%d), nrd_max_lane_count(%d)\n",
__func__, link->dpia_bw_alloc_config.nrd_max_link_rate,
link->dpia_bw_alloc_config.nrd_max_lane_count);
}
/*
* Cleanup function for when the dpia is unplugged to reset struct
* and perform any required clean up
*
* @link: pointer to the dc_link struct instance
*
* return: none
*/
static void dpia_bw_alloc_unplug(struct dc_link *link)
{
if (link) {
DC_LOG_DEBUG("%s: resetting BW alloc config for link(%d)\n",
__func__, link->link_index);
reset_bw_alloc_struct(link);
}
}
static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw)
{
uint32_t request_reg_val;
uint32_t temp, request_bw;
if (link->dpia_bw_alloc_config.bw_granularity == 0) {
DC_LOG_ERROR("%s: Link[%d]: bw_granularity is zero!", __func__, link->link_index);
return;
}
temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
request_reg_val = temp / Kbps_TO_Gbps;
/* Always make sure to add more to account for floating points */
if (temp % Kbps_TO_Gbps)
++request_reg_val;
request_bw = request_reg_val * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
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);
req_bw = link->dpia_bw_alloc_config.estimated_bw;
temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
request_reg_val = temp / Kbps_TO_Gbps;
if (temp % Kbps_TO_Gbps)
++request_reg_val;
}
link->dpia_bw_alloc_config.allocated_bw = request_bw;
DC_LOG_DC("%s: Link[%d]: Request BW: %d", __func__, link->link_index, request_bw);
uint8_t requested_bw_dpcd = (uint8_t)request_reg_val;
core_link_write_dpcd(link, REQUESTED_BW, &requested_bw_dpcd,
sizeof(uint8_t));
}
// ------------------------------------------------------------------
// PUBLIC FUNCTIONS
// ------------------------------------------------------------------
bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link)
{
bool ret = false;
uint8_t val;
val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ;
if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) {
DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode enabled", __func__, link->link_index);
retrieve_usb4_dp_bw_allocation_info(link);
if (
link->dpia_bw_alloc_config.nrd_max_link_rate
&& link->dpia_bw_alloc_config.nrd_max_lane_count) {
link->reported_link_cap.link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate;
link->reported_link_cap.lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count;
}
link->dpia_bw_alloc_config.bw_alloc_enabled = true;
ret = true;
if (link->dc->debug.dpia_debug.bits.enable_usb4_bw_zero_alloc_patch) {
/*
* During DP tunnel creation, the CM preallocates BW
* and reduces the estimated BW of other DPIAs.
* The CM releases the preallocation only when the allocation is complete.
* Perform a zero allocation to make the CM release the preallocation
* and correctly update the estimated BW for all DPIAs per host router.
*/
link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0);
}
} else
DC_LOG_DEBUG("%s: link[%d] failed to enable DPTX BW allocation mode", __func__, link->link_index);
return ret;
}
/*
* Handle DP BW allocation status register
*
* @link: pointer to the dc_link struct instance
* @status: content of DP tunneling status DPCD register
*
* return: none
*/
void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status)
{
if (status & DP_TUNNELING_BW_REQUEST_SUCCEEDED) {
DC_LOG_DEBUG("%s: BW Allocation request succeeded on link(%d)",
__func__, link->link_index);
}
if (status & DP_TUNNELING_BW_REQUEST_FAILED) {
DC_LOG_DEBUG("%s: BW Allocation request failed on link(%d) allocated/estimated BW=%d",
__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
link_dpia_send_bw_alloc_request(link, link->dpia_bw_alloc_config.estimated_bw);
}
if (status & DP_TUNNELING_BW_ALLOC_CAP_CHANGED) {
link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
DC_LOG_DEBUG("%s: Granularity changed on link(%d) new granularity=%d",
__func__, link->link_index, link->dpia_bw_alloc_config.bw_granularity);
}
if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) {
link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
DC_LOG_DEBUG("%s: Estimated BW changed on link(%d) new estimated BW=%d",
__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
}
core_link_write_dpcd(
link, DP_TUNNELING_STATUS,
&status, sizeof(status));
}
/*
* Handle the DP Bandwidth allocation for DPIA
*
*/
void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw)
{
if (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling
&& link->dpia_bw_alloc_config.bw_alloc_enabled) {
if (peak_bw > 0) {
// If DP over USB4 then we need to check BW allocation
link->dpia_bw_alloc_config.link_max_bw = peak_bw;
link_dpia_send_bw_alloc_request(link, peak_bw);
} else
dpia_bw_alloc_unplug(link);
}
}
void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw)
{
link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
DC_LOG_DEBUG("%s: ENTER: link[%d] hpd(%d) Allocated_BW: %d Estimated_BW: %d Req_BW: %d",
__func__, link->link_index, link->hpd_status,
link->dpia_bw_alloc_config.allocated_bw,
link->dpia_bw_alloc_config.estimated_bw,
req_bw);
if (link_dp_is_bw_alloc_available(link))
link_dpia_send_bw_alloc_request(link, req_bw);
else
DC_LOG_DEBUG("%s: BW Allocation mode not available", __func__);
}
uint32_t link_dpia_get_dp_overhead(const struct dc_link *link)
{
uint32_t link_dp_overhead = 0;
if ((link->type == dc_connection_mst_branch) &&
!link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) {
/* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH
* MST overhead is 1/64 of link bandwidth (excluding any overhead)
*/
const struct dc_link_settings *link_cap = dc_link_get_link_cap(link);
if (link_cap) {
uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate *
(uint32_t)link_cap->lane_count *
LINK_RATE_REF_FREQ_IN_KHZ * 8;
link_dp_overhead = (link_bw_in_kbps / MST_TIME_SLOT_COUNT)
+ ((link_bw_in_kbps % MST_TIME_SLOT_COUNT) ? 1 : 0);
}
}
return link_dp_overhead;
}
/*
* Aggregates the DPIA bandwidth usage for the respective USB4 Router.
* And then validate if the required bandwidth is within the router's capacity.
*
* @dc_validation_dpia_set: pointer to the dc_validation_dpia_set
* @count: number of DPIA validation sets
*
* return: true if validation is succeeded
*/
bool link_dpia_validate_dp_tunnel_bandwidth(const struct dc_validation_dpia_set *dpia_link_sets, uint8_t count)
{
uint32_t granularity_Gbps;
const struct dc_link *link;
uint32_t link_bw_granularity;
uint32_t link_required_bw;
struct usb4_router_validation_set router_sets[MAX_HOST_ROUTERS_NUM] = { 0 };
uint8_t i;
bool is_success = true;
uint8_t router_count = 0;
if ((dpia_link_sets == NULL) || (count == 0))
return is_success;
// Iterate through each DP tunneling link (DPIA).
// Aggregate its bandwidth requirements onto the respective USB4 router.
for (i = 0; i < count; i++) {
link = dpia_link_sets[i].link;
link_required_bw = dpia_link_sets[i].required_bw;
const struct dc_tunnel_settings *dp_tunnel_settings = dpia_link_sets[i].tunnel_settings;
if ((link == NULL) || (dp_tunnel_settings == NULL) || dp_tunnel_settings->bw_granularity == 0)
break;
if (link->type == dc_connection_mst_branch)
link_required_bw += link_dpia_get_dp_overhead(link);
granularity_Gbps = (Kbps_TO_Gbps / dp_tunnel_settings->bw_granularity);
link_bw_granularity = (link_required_bw / granularity_Gbps) * granularity_Gbps +
((link_required_bw % granularity_Gbps) ? granularity_Gbps : 0);
// Find or add the USB4 router associated with the current DPIA link
for (uint8_t j = 0; j < MAX_HOST_ROUTERS_NUM; j++) {
if (router_sets[j].is_valid == false) {
router_sets[j].is_valid = true;
router_sets[j].cm_id = dp_tunnel_settings->cm_id;
router_count++;
}
if (router_sets[j].cm_id == dp_tunnel_settings->cm_id) {
uint32_t remaining_bw =
dp_tunnel_settings->estimated_bw - dp_tunnel_settings->allocated_bw;
router_sets[j].allocated_bw += dp_tunnel_settings->allocated_bw;
if (remaining_bw > router_sets[j].remaining_bw)
router_sets[j].remaining_bw = remaining_bw;
// Get the max estimated BW within the same CM_ID
if (dp_tunnel_settings->estimated_bw > router_sets[j].estimated_bw)
router_sets[j].estimated_bw = dp_tunnel_settings->estimated_bw;
router_sets[j].required_bw += link_bw_granularity;
router_sets[j].dpia_count++;
break;
}
}
}
// Validate bandwidth for each unique router found.
for (i = 0; i < router_count; i++) {
uint32_t total_bw = 0;
if (router_sets[i].is_valid == false)
break;
// Determine the total available bandwidth for the current router based on aggregated data
if ((router_sets[i].dpia_count == 1) || (router_sets[i].allocated_bw == 0))
total_bw = router_sets[i].estimated_bw;
else
total_bw = router_sets[i].allocated_bw + router_sets[i].remaining_bw;
if (router_sets[i].required_bw > total_bw) {
is_success = false;
break;
}
}
return is_success;
}
@@ -0,0 +1,112 @@
/*
* Copyright 2021 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 DC_INC_LINK_DP_DPIA_BW_H_
#define DC_INC_LINK_DP_DPIA_BW_H_
#include "link_service.h"
/*
* Host Router BW type
*/
enum bw_type {
HOST_ROUTER_BW_ESTIMATED,
HOST_ROUTER_BW_ALLOCATED,
HOST_ROUTER_BW_INVALID,
};
struct usb4_router_validation_set {
bool is_valid;
uint8_t cm_id;
uint8_t dpia_count;
uint32_t required_bw;
uint32_t allocated_bw;
uint32_t estimated_bw;
uint32_t remaining_bw;
};
/*
* Enable USB4 DP BW allocation mode
*
* @link: pointer to the dc_link struct instance
*
* return: SUCCESS or FAILURE
*/
bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link);
/*
* Allocates only what the stream needs for bw, so if:
* If (stream_req_bw < or > already_allocated_bw_at_HPD)
* => Deallocate Max Bw & then allocate only what the stream needs
*
* @link: pointer to the dc_link struct instance
* @req_bw: Bw requested by the stream
*
*/
void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw);
/*
* Handle the USB4 BW Allocation related functionality here:
* Plug => Try to allocate max bw from timing parameters supported by the sink
* Unplug => de-allocate bw
*
* @link: pointer to the dc_link struct instance
* @peak_bw: Peak bw used by the link/sink
*
*/
void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw);
/*
* Obtain all the DP overheads in dp tunneling for the dpia link
*
* @link: pointer to the dc_link struct instance
*
* return: DP overheads in DP tunneling
*/
uint32_t link_dpia_get_dp_overhead(const struct dc_link *link);
/*
* Handle DP BW allocation status register
*
* @link: pointer to the dc_link struct instance
* @status: content of DP tunneling status register
*
* return: none
*/
void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status);
/*
* Aggregates the DPIA bandwidth usage for the respective USB4 Router.
*
* @dc_validation_dpia_set: pointer to the dc_validation_dpia_set
* @count: number of DPIA validation sets
*
* return: true if validation is succeeded
*/
bool link_dpia_validate_dp_tunnel_bandwidth(const struct dc_validation_dpia_set *dpia_link_sets, uint8_t count);
#endif /* DC_INC_LINK_DP_DPIA_BW_H_ */
@@ -0,0 +1,618 @@
/*
* 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 DP HPD short pulse handling sequence according to DP
* specifications
*
*/
#include "link_dp_irq_handler.h"
#include "link_dpcd.h"
#include "link_dp_training.h"
#include "link_dp_capability.h"
#include "link_edp_panel_control.h"
#include "link_dp_panel_replay.h"
#include "link/accessories/link_dp_trace.h"
#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
#define DC_LOGGER_INIT(logger)
bool dp_parse_link_loss_status(
struct dc_link *link,
union hpd_irq_data *hpd_irq_dpcd_data)
{
uint8_t irq_reg_rx_power_state = 0;
enum dc_status dpcd_result = DC_ERROR_UNEXPECTED;
union lane_status lane_status;
uint32_t lane;
bool sink_status_changed;
bool return_code;
sink_status_changed = false;
return_code = false;
if (link->cur_link_settings.lane_count == 0)
return return_code;
/*1. Check that Link Status changed, before re-training.*/
/*parse lane status*/
for (lane = 0; lane < (uint32_t)link->cur_link_settings.lane_count; lane++) {
/* check status of lanes 0,1
* changed DpcdAddress_Lane01Status (0x202)
*/
lane_status.raw = dp_get_nibble_at_index(
&hpd_irq_dpcd_data->bytes.lane01_status.raw,
lane);
if (!lane_status.bits.CHANNEL_EQ_DONE_0 ||
!lane_status.bits.CR_DONE_0 ||
!lane_status.bits.SYMBOL_LOCKED_0) {
/* if one of the channel equalization, clock
* recovery or symbol lock is dropped
* consider it as (link has been
* dropped) dp sink status has changed
*/
sink_status_changed = true;
break;
}
}
/* Check interlane align.*/
if (link_dp_get_encoding_format(&link->cur_link_settings) == DP_128b_132b_ENCODING &&
(!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b ||
!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b)) {
sink_status_changed = true;
} else if (!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) {
sink_status_changed = true;
}
if (sink_status_changed) {
DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__);
return_code = true;
/*2. Check that we can handle interrupt: Not in FS DOS,
* Not in "Display Timeout" state, Link is trained.
*/
dpcd_result = core_link_read_dpcd(link,
DP_SET_POWER,
&irq_reg_rx_power_state,
sizeof(irq_reg_rx_power_state));
if (dpcd_result != DC_OK) {
DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n",
__func__);
} else {
if (irq_reg_rx_power_state != DP_SET_POWER_D0)
return_code = false;
}
}
return return_code;
}
static bool handle_hpd_irq_psr_sink(struct dc_link *link)
{
union dpcd_psr_configuration psr_configuration = {0};
if (!link->psr_settings.psr_feature_enabled)
return false;
dm_helpers_dp_read_dpcd(
link->ctx,
link,
368,/*DpcdAddress_PSR_Enable_Cfg*/
&psr_configuration.raw,
sizeof(psr_configuration.raw));
if (psr_configuration.bits.ENABLE) {
unsigned char dpcdbuf[3] = {0};
union psr_error_status psr_error_status;
union psr_sink_psr_status psr_sink_psr_status;
dm_helpers_dp_read_dpcd(
link->ctx,
link,
0x2006, /*DpcdAddress_PSR_Error_Status*/
(unsigned char *) dpcdbuf,
sizeof(dpcdbuf));
/*DPCD 2006h ERROR STATUS*/
psr_error_status.raw = dpcdbuf[0];
/*DPCD 2008h SINK PANEL SELF REFRESH STATUS*/
psr_sink_psr_status.raw = dpcdbuf[2];
if (psr_error_status.bits.LINK_CRC_ERROR ||
psr_error_status.bits.RFB_STORAGE_ERROR ||
psr_error_status.bits.VSC_SDP_ERROR) {
bool allow_active;
/* Acknowledge and clear error bits */
dm_helpers_dp_write_dpcd(
link->ctx,
link,
8198,/*DpcdAddress_PSR_Error_Status*/
&psr_error_status.raw,
sizeof(psr_error_status.raw));
/* PSR error, disable and re-enable PSR */
if (link->psr_settings.psr_allow_active) {
allow_active = false;
edp_set_psr_allow_active(link, &allow_active, true, false, NULL);
allow_active = true;
edp_set_psr_allow_active(link, &allow_active, true, false, NULL);
}
return true;
} else if (psr_sink_psr_status.bits.SINK_SELF_REFRESH_STATUS ==
PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB){
/* No error is detect, PSR is active.
* We should return with IRQ_HPD handled without
* checking for loss of sync since PSR would have
* powered down main link.
*/
return true;
}
}
return false;
}
static void handle_hpd_irq_vesa_replay_sink(struct dc_link *link)
{
union pr_error_status pr_error_status = {0};
if (!link->replay_settings.replay_feature_enabled ||
link->replay_settings.config.replay_version != DC_VESA_PANEL_REPLAY)
return;
dm_helpers_dp_read_dpcd(
link->ctx,
link,
DP_PR_ERROR_STATUS,
&pr_error_status.raw,
sizeof(pr_error_status.raw));
if (pr_error_status.bits.LINK_CRC_ERROR ||
pr_error_status.bits.RFB_STORAGE_ERROR ||
pr_error_status.bits.VSC_SDP_ERROR ||
pr_error_status.bits.ASSDP_MISSING_ERROR) {
/* Acknowledge and clear error bits */
dm_helpers_dp_write_dpcd(
link->ctx,
link,
DP_PR_ERROR_STATUS, /*DpcdAddress_PR_Error_Status*/
&pr_error_status.raw,
sizeof(pr_error_status.raw));
/* Replay error, disable and re-enable Replay */
if (link->replay_settings.replay_allow_active) {
dp_pr_enable(link, false);
dp_pr_enable(link, true);
}
}
}
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};
/*AMD Replay version reuse DP_PSR_ERROR_STATUS for REPLAY_ERROR status.*/
union psr_error_status replay_error_status = {0};
bool ret = false;
int retries = 0;
if (!link->replay_settings.replay_feature_enabled)
return;
if (link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY) {
handle_hpd_irq_vesa_replay_sink(link);
return;
}
while (retries < 10) {
ret = dm_helpers_dp_read_dpcd(
link->ctx,
link,
DP_SINK_PR_REPLAY_STATUS,
&replay_configuration.raw,
sizeof(replay_configuration.raw));
if (ret)
break;
retries++;
}
if (!ret)
DC_LOG_WARNING("[%s][%d] DPCD read addr.0x%x failed with %d retries\n",
__func__, __LINE__,
DP_SINK_PR_REPLAY_STATUS, retries);
dm_helpers_dp_read_dpcd(
link->ctx,
link,
DP_PSR_ERROR_STATUS,
&replay_error_status.raw,
sizeof(replay_error_status.raw));
dm_helpers_dp_read_dpcd(
link->ctx,
link,
DP_PR_REPLAY_SINK_STATUS,
&replay_sink_status.raw,
1);
if (replay_error_status.bits.LINK_CRC_ERROR ||
replay_configuration.bits.DESYNC_ERROR_STATUS ||
replay_configuration.bits.STATE_TRANSITION_ERROR_STATUS ||
replay_sink_status.bits.SINK_DEVICE_REPLAY_STATUS == 0x7) {
bool allow_active;
link->replay_settings.config.replay_error_status.raw |= replay_error_status.raw;
/* Increment desync error counter if a desync error is detected */
if (replay_configuration.bits.DESYNC_ERROR_STATUS)
link->replay_settings.replay_desync_error_fail_count++;
if (link->replay_settings.config.force_disable_desync_error_check)
return;
/* Acknowledge and clear configuration bits */
dm_helpers_dp_write_dpcd(
link->ctx,
link,
DP_SINK_PR_REPLAY_STATUS,
&replay_configuration.raw,
sizeof(replay_configuration.raw));
/* Acknowledge and clear error bits */
dm_helpers_dp_write_dpcd(
link->ctx,
link,
DP_PSR_ERROR_STATUS,/*DpcdAddress_REPLAY_Error_Status*/
&replay_error_status.raw,
sizeof(replay_error_status.raw));
/* Replay error, disable and re-enable Replay */
if (link->replay_settings.replay_allow_active) {
allow_active = false;
edp_set_replay_allow_active(link, &allow_active, true, false, NULL);
*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)
{
struct pipe_ctx *pipes[MAX_PIPES];
struct dc_state *state = link->dc->current_state;
uint8_t count;
int i;
link_get_master_pipes_with_dpms_on(link, state, &count, pipes);
for (i = 0; i < count; i++)
link_set_dpms_off(pipes[i]);
for (i = count - 1; i >= 0; i--) {
// Always use max settings here for DP 1.4a LL Compliance CTS
if (link->skip_fallback_on_link_loss) {
pipes[i]->link_config.dp_link_settings.lane_count =
link->verified_link_cap.lane_count;
pipes[i]->link_config.dp_link_settings.link_rate =
link->verified_link_cap.link_rate;
pipes[i]->link_config.dp_link_settings.link_spread =
link->verified_link_cap.link_spread;
}
link_set_dpms_on(link->dc->current_state, pipes[i]);
}
}
static void dp_handle_tunneling_irq(struct dc_link *link)
{
enum dc_status retval;
uint8_t tunneling_status = 0;
retval = core_link_read_dpcd(
link, DP_TUNNELING_STATUS,
&tunneling_status,
sizeof(tunneling_status));
if (retval == DC_OK) {
DC_LOG_HW_HPD_IRQ("%s: Got DP tunneling status on link %d status=0x%x",
__func__, link->link_index, tunneling_status);
if (tunneling_status & DP_TUNNELING_BW_ALLOC_BITS_MASK)
link_dp_dpia_handle_bw_alloc_status(link, tunneling_status);
}
tunneling_status = DP_TUNNELING_IRQ;
core_link_write_dpcd(
link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0,
&tunneling_status, 1);
}
static void read_dpcd204h_on_irq_hpd(struct dc_link *link, union hpd_irq_data *irq_data)
{
enum dc_status retval;
union lane_align_status_updated dpcd_lane_status_updated = {0};
retval = core_link_read_dpcd(
link,
DP_LANE_ALIGN_STATUS_UPDATED,
&dpcd_lane_status_updated.raw,
sizeof(union lane_align_status_updated));
if (retval == DC_OK) {
irq_data->bytes.lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b =
dpcd_lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b;
irq_data->bytes.lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b =
dpcd_lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b;
}
}
enum dc_status dp_read_hpd_rx_irq_data(
struct dc_link *link,
union hpd_irq_data *irq_data)
{
static enum dc_status retval;
/* The HW reads 16 bytes from 200h on HPD,
* but if we get an AUX_DEFER, the HW cannot retry
* and this causes the CTS tests 4.3.2.1 - 3.2.4 to
* fail, so we now explicitly read 6 bytes which is
* the req from the above mentioned test cases.
*
* For DP 1.4 we need to read those from 2002h range.
*/
if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) {
retval = core_link_read_dpcd(
link,
DP_SINK_COUNT,
irq_data->raw,
DP_SINK_STATUS - DP_SINK_COUNT + 1);
if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling) {
retval = core_link_read_dpcd(
link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0,
&irq_data->bytes.link_service_irq_esi0.raw, 1);
}
} else {
/* Read 14 bytes in a single read and then copy only the required fields.
* This is more efficient than doing it in two separate AUX reads. */
uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1] = {0};
retval = core_link_read_dpcd(
link,
DP_SINK_COUNT_ESI,
tmp,
sizeof(tmp));
if (retval != DC_OK)
return retval;
irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI];
irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI];
irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI];
irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI];
irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI];
irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI];
irq_data->bytes.link_service_irq_esi0.raw = tmp[DP_LINK_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI];
/*
* This display doesn't have correct values in DPCD200Eh.
* Read and check DPCD204h instead.
*/
if (link->wa_flags.read_dpcd204h_on_irq_hpd)
read_dpcd204h_on_irq_hpd(link, irq_data);
}
return retval;
}
/*************************Short Pulse IRQ***************************/
bool dp_should_allow_hpd_rx_irq(const struct dc_link *link)
{
/*
* Don't handle RX IRQ unless one of following is met:
* 1) The link is established (cur_link_settings != unknown)
* 2) We know we're dealing with a branch device, SST or MST
* 3) The link is bw_alloc enabled.
*/
if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
is_dp_branch_device(link) ||
link->dpia_bw_alloc_config.bw_alloc_enabled)
return true;
return false;
}
bool dp_handle_hpd_rx_irq(struct dc_link *link,
union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss,
bool defer_handling, bool *has_left_work)
{
union hpd_irq_data hpd_irq_dpcd_data = {0};
union device_service_irq device_service_clear = {0};
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;
if (has_left_work)
*has_left_work = false;
/* For use cases related to down stream connection status change,
* PSR and device auto test, refer to function handle_sst_hpd_irq
* in DAL2.1*/
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
* dal_dpsst_ls_read_hpd_irq_data
* Order of calls is important too
*/
result = dp_read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data);
if (out_hpd_irq_dpcd_data)
*out_hpd_irq_dpcd_data = hpd_irq_dpcd_data;
if (result != DC_OK) {
DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain irq data\n",
__func__);
return false;
}
if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
// Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC
if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
!link->dc->config.enable_dpia_pre_training)
link->skip_fallback_on_link_loss = true;
device_service_clear.bits.AUTOMATED_TEST = 1;
core_link_write_dpcd(
link,
DP_DEVICE_SERVICE_IRQ_VECTOR,
&device_service_clear.raw,
sizeof(device_service_clear.raw));
device_service_clear.raw = 0;
if (defer_handling && has_left_work)
*has_left_work = true;
else
dc_link_dp_handle_automated_test(link);
return false;
}
if (!dp_should_allow_hpd_rx_irq(link)) {
DC_LOG_HW_HPD_IRQ("%s: skipping HPD handling on %d\n",
__func__, link->link_index);
return false;
}
if (handle_hpd_irq_psr_sink(link))
/* PSR-related error was detected and handled */
return true;
/* If PSR-related error handled, Main link may be off,
* so do not handle as a normal sink status change interrupt.
*/
if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) {
if (defer_handling && has_left_work)
*has_left_work = true;
return true;
}
/* check if we have MST msg and return since we poll for it */
if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
if (defer_handling && has_left_work)
*has_left_work = true;
return false;
}
/* Handle 'Downstream port status' case for all DP link types.
* If we got sink count changed it means
* 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 (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) {
if (hpd_irq_dpcd_data.bytes.link_service_irq_esi0.bits.DP_LINK_TUNNELING_IRQ)
dp_handle_tunneling_irq(link);
}
if (link->type == dc_connection_sst_branch &&
hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
!= link->dpcd_sink_count)
status = true;
if (replay_re_enable_needed) {
bool allow_active = true;
edp_set_replay_allow_active(link, &allow_active, true, false, NULL);
}
/* reasons for HPD RX:
* 1. Link Loss - ie Re-train the Link
* 2. MST sideband message
* 3. Automated Test - ie. Internal Commit
* 4. CP (copy protection) - (not interesting for DM???)
* 5. DRR
* 6. Downstream Port status changed
* -ie. Detect - this the only one
* which is interesting for DM because
* it must call dc_link_detect.
*/
return status;
}
@@ -0,0 +1,41 @@
/*
* 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 __DC_LINK_DP_IRQ_HANDLER_H__
#define __DC_LINK_DP_IRQ_HANDLER_H__
#include "link_service.h"
bool dp_parse_link_loss_status(
struct dc_link *link,
union hpd_irq_data *hpd_irq_dpcd_data);
bool dp_should_allow_hpd_rx_irq(const struct dc_link *link);
void dp_handle_link_loss(struct dc_link *link);
enum dc_status dp_read_hpd_rx_irq_data(
struct dc_link *link,
union hpd_irq_data *irq_data);
bool dp_handle_hpd_rx_irq(struct dc_link *link,
union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss,
bool defer_handling, bool *has_left_work);
#endif /* __DC_LINK_DP_IRQ_HANDLER_H__ */
@@ -0,0 +1,428 @@
/*
* Copyright 2025 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_dp_panel_replay.h"
#include "link_edp_panel_control.h"
#include "link_dpcd.h"
#include "dm_helpers.h"
#include "dc/dc_dmub_srv.h"
#include "dce/dmub_replay.h"
#define DC_LOGGER \
link->ctx->logger
#define DP_SINK_PR_ENABLE_AND_CONFIGURATION 0x37B
#define DP_SINK_ENABLE_FRAME_SKIPPING_MODE_SHIFT (5)
static unsigned int dp_pr_calc_num_static_frames(unsigned int vsync_rate_hz)
{
// at least 2 frames for static screen
unsigned int num_frames = 2;
// get number of frames for at least 50ms
if (vsync_rate_hz > 40)
num_frames = (vsync_rate_hz + 10) / 20;
return num_frames;
}
static void dp_pr_set_static_screen_param(struct dc_link *link)
{
struct dc_static_screen_params params = {0};
struct dc *dc = link->ctx->dc;
// only support DP sst for now
if (!dc_is_dp_sst_signal(link->connector_signal))
return;
for (int i = 0; i < MAX_PIPES; i++) {
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 = (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;
params.num_frames = dp_pr_calc_num_static_frames(vsync_rate_hz);
dc_stream_set_static_screen_params(dc, &stream, 1, &params);
break;
}
}
}
static bool dp_setup_panel_replay(struct dc_link *link, const struct dc_stream_state *stream)
{
/* To-do: Setup Replay */
struct dc *dc;
struct dmub_replay *replay;
int i;
unsigned int panel_inst;
struct replay_context replay_context = { 0 };
unsigned int lineTimeInNs = 0;
union panel_replay_enable_and_configuration_1 pr_config_1 = { 0 };
union panel_replay_enable_and_configuration_2 pr_config_2 = { 0 };
union dpcd_alpm_configuration alpm_config;
uint8_t data = 0;
replay_context.controllerId = CONTROLLER_ID_UNDEFINED;
if (!link)
return false;
//Clear Panel Replay enable & config
dm_helpers_dp_write_dpcd(link->ctx, link,
DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1,
(uint8_t *)&(pr_config_1.raw), sizeof(uint8_t));
dm_helpers_dp_write_dpcd(link->ctx, link,
DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2,
(uint8_t *)&(pr_config_2.raw), sizeof(uint8_t));
if (!(link->replay_settings.config.replay_supported))
return false;
dc = link->ctx->dc;
//not sure should keep or not
replay = dc->res_pool->replay;
if (!replay)
return false;
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.digbe_inst = link->link_enc->transmitter;
replay_context.digfe_inst = link->link_enc->preferred_engine;
for (i = 0; i < MAX_PIPES; i++) {
if (dc->current_state->res_ctx.pipe_ctx[i].stream
== stream) {
/* dmcu -1 for all controller id values,
* therefore +1 here
*/
replay_context.controllerId =
dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg->inst + 1;
break;
}
}
lineTimeInNs =
((stream->timing.h_total * 1000000) /
(stream->timing.pix_clk_100hz / 10)) + 1;
replay_context.line_time_in_ns = lineTimeInNs;
link->replay_settings.replay_feature_enabled = dp_pr_copy_settings(link, &replay_context);
if (link->replay_settings.replay_feature_enabled) {
if (dc_is_embedded_signal(link->connector_signal)) {
pr_config_1.bits.PANEL_REPLAY_ENABLE = 1;
pr_config_1.bits.PANEL_REPLAY_CRC_ENABLE = 1;
pr_config_1.bits.IRQ_HPD_ASSDP_MISSING = 1;
pr_config_1.bits.IRQ_HPD_VSCSDP_UNCORRECTABLE_ERROR = 1;
pr_config_1.bits.IRQ_HPD_RFB_ERROR = 1;
pr_config_1.bits.IRQ_HPD_ACTIVE_FRAME_CRC_ERROR = 1;
pr_config_1.bits.PANEL_REPLAY_SELECTIVE_UPDATE_ENABLE = 1;
pr_config_1.bits.PANEL_REPLAY_EARLY_TRANSPORT_ENABLE = 1;
} else {
pr_config_1.bits.PANEL_REPLAY_ENABLE = 1;
}
pr_config_2.bits.SINK_REFRESH_RATE_UNLOCK_GRANTED = 0;
if (link->dpcd_caps.vesa_replay_caps.bits.SU_Y_GRANULARITY_EXT_CAP_SUPPORTED)
pr_config_2.bits.SU_Y_GRANULARITY_EXT_VALUE_ENABLED = 1;
pr_config_2.bits.SU_REGION_SCAN_LINE_CAPTURE_INDICATION = 0;
dm_helpers_dp_write_dpcd(link->ctx, link,
DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1,
(uint8_t *)&(pr_config_1.raw), sizeof(uint8_t));
dm_helpers_dp_write_dpcd(link->ctx, link,
DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2,
(uint8_t *)&(pr_config_2.raw), sizeof(uint8_t));
//ALPM Setup
memset(&alpm_config, 0, sizeof(alpm_config));
alpm_config.bits.ENABLE = link->replay_settings.config.alpm_mode != DC_ALPM_UNSUPPORTED ? 1 : 0;
if (link->replay_settings.config.alpm_mode == DC_ALPM_AUXLESS) {
alpm_config.bits.ALPM_MODE_SEL = 1;
alpm_config.bits.ACDS_PERIOD_DURATION = 1;
}
dm_helpers_dp_write_dpcd(
link->ctx,
link,
DP_RECEIVER_ALPM_CONFIG,
&alpm_config.raw,
sizeof(alpm_config.raw));
//Enable frame skipping
if (link->replay_settings.config.frame_skip_supported)
data = data | (1 << DP_SINK_ENABLE_FRAME_SKIPPING_MODE_SHIFT);
dm_helpers_dp_write_dpcd(link->ctx, link,
DP_SINK_PR_ENABLE_AND_CONFIGURATION,
(uint8_t *)&(data), sizeof(uint8_t));
}
return true;
}
bool dp_pr_get_panel_inst(const struct dc *dc,
const struct dc_link *link,
unsigned int *inst_out)
{
if (!dc || !link || !inst_out)
return false;
if (dc->config.frame_update_cmd_version2 == false)
return dc_get_edp_link_panel_inst(dc, link, inst_out);
if (!dc_is_dp_sst_signal(link->connector_signal)) /* only supoprt DP sst (eDP included) for now */
return false;
for (unsigned int i = 0; i < MAX_PIPES; i++) {
if (dc->current_state->res_ctx.pipe_ctx[i].stream &&
dc->current_state->res_ctx.pipe_ctx[i].stream->link == link) {
/* *inst_out is equal to otg number */
if (dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg)
*inst_out = dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg->inst;
else
*inst_out = 0;
return true;
}
}
return false;
}
bool dp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream)
{
if (!link)
return false;
if (link->replay_settings.config.replay_version == DC_VESA_PANEL_REPLAY)
return dp_setup_panel_replay(link, stream);
else if (link->replay_settings.config.replay_version == DC_FREESYNC_REPLAY)
return edp_setup_freesync_replay(link, stream);
else
return false;
}
bool dp_pr_enable(struct dc_link *link, bool enable)
{
struct dc *dc = link->ctx->dc;
unsigned int panel_inst = 0;
union dmub_rb_cmd cmd;
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
if (link->replay_settings.replay_allow_active == enable)
return true;
if (enable && !dc_is_embedded_signal(link->connector_signal))
dp_pr_set_static_screen_param(link);
// for sending PR enable commands to DMUB
memset(&cmd, 0, sizeof(cmd));
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 = (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);
link->replay_settings.replay_allow_active = enable;
return true;
}
bool dp_pr_copy_settings(struct dc_link *link, struct replay_context *replay_context)
{
struct dc *dc = link->ctx->dc;
unsigned int panel_inst = 0;
union dmub_rb_cmd cmd;
struct pipe_ctx *pipe_ctx = NULL;
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
for (unsigned int i = 0; i < MAX_PIPES; i++) {
if (dc->current_state->res_ctx.pipe_ctx[i].stream &&
dc->current_state->res_ctx.pipe_ctx[i].stream->link &&
dc->current_state->res_ctx.pipe_ctx[i].stream->link == link &&
dc_is_dp_sst_signal(dc->current_state->res_ctx.pipe_ctx[i].stream->link->connector_signal)) {
pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
/* todo: need update for MST */
break;
}
}
if (!pipe_ctx)
return false;
memset(&cmd, 0, sizeof(cmd));
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 = (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 = (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 = (uint8_t)pipe_ctx->stream_res.tg->inst;
else
cmd.pr_copy_settings.data.otg_inst = 0;
cmd.pr_copy_settings.data.dpphy_inst = link->link_enc->transmitter;
cmd.pr_copy_settings.data.line_time_in_ns = replay_context->line_time_in_ns;
cmd.pr_copy_settings.data.flags.bitfields.fec_enable_status = (link->fec_state == dc_link_fec_enabled);
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;
cmd.pr_copy_settings.data.su_y_granularity_extended_caps =
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 = (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);
if (dc_is_embedded_signal(link->connector_signal))
cmd.pr_copy_settings.data.main_link_activity_option = OPTION_1C;
else
// For external DP, use option 1-A
cmd.pr_copy_settings.data.main_link_activity_option = OPTION_1A;
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
return true;
}
bool dp_pr_update_state(struct dc_link *link, struct dmub_cmd_pr_update_state_data *update_state_data)
{
struct dc *dc = link->ctx->dc;
unsigned int panel_inst = 0;
union dmub_rb_cmd cmd;
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
memset(&cmd, 0, sizeof(cmd));
memcpy(&cmd.pr_update_state.data, update_state_data, sizeof(struct dmub_cmd_pr_update_state_data));
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 = (uint8_t)panel_inst;
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
return true;
}
bool dp_pr_set_general_cmd(struct dc_link *link, struct dmub_cmd_pr_general_cmd_data *general_cmd_data)
{
struct dc *dc = link->ctx->dc;
unsigned int panel_inst = 0;
union dmub_rb_cmd cmd;
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
memset(&cmd, 0, sizeof(cmd));
memcpy(&cmd.pr_general_cmd.data, general_cmd_data, sizeof(struct dmub_cmd_pr_general_cmd_data));
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 = (uint8_t)panel_inst;
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
return true;
}
bool dp_pr_get_state(const struct dc_link *link, uint64_t *state)
{
const struct dc *dc = link->ctx->dc;
unsigned int panel_inst = 0;
uint32_t retry_count = 0;
uint32_t replay_state = PR_STATE_INVALID;
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
do {
// Send gpint command and wait for ack
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;
}
/* Copy 32-bit result into 64-bit output */
*state = replay_state;
} while (++retry_count <= 1000 && *state == PR_STATE_INVALID);
// Assert if max retry hit
if (retry_count >= 1000 && *state == PR_STATE_INVALID) {
ASSERT(0);
/* To-do: Add retry fail log */
}
return true;
}
@@ -0,0 +1,38 @@
/*
* Copyright 2025 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 __DC_LINK_DP_PANEL_REPLAY_H__
#define __DC_LINK_DP_PANEL_REPLAY_H__
#include "link_service.h"
bool dp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream);
bool dp_pr_get_panel_inst(const struct dc *dc, const struct dc_link *link, unsigned int *inst_out);
bool dp_pr_enable(struct dc_link *link, bool enable);
bool dp_pr_copy_settings(struct dc_link *link, struct replay_context *replay_context);
bool dp_pr_update_state(struct dc_link *link, struct dmub_cmd_pr_update_state_data *update_state_data);
bool dp_pr_set_general_cmd(struct dc_link *link, struct dmub_cmd_pr_general_cmd_data *general_cmd_data);
bool dp_pr_get_state(const struct dc_link *link, uint64_t *state);
#endif /* __DC_LINK_DP_PANEL_REPLAY_H__ */
@@ -0,0 +1,210 @@
/*
* 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 basic dp phy functionality such as enable/disable phy
* output and set lane/drive settings. This file is responsible for maintaining
* and update software state representing current phy status such as current
* link settings.
*/
#include "link_dp_phy.h"
#include "link_dpcd.h"
#include "link_dp_training.h"
#include "link_dp_capability.h"
#include "clk_mgr.h"
#include "resource.h"
#include "link_enc_cfg.h"
#include "atomfirmware.h"
#define DC_LOGGER \
link->ctx->logger
void dpcd_write_rx_power_ctrl(struct dc_link *link, bool on)
{
uint8_t state;
state = on ? DP_POWER_STATE_D0 : DP_POWER_STATE_D3;
if (link->sync_lt_in_progress)
return;
core_link_write_dpcd(link, DP_SET_POWER, &state,
sizeof(state));
}
void dp_enable_link_phy(
struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings)
{
link->cur_link_settings = *link_settings;
link->dc->hwss.enable_dp_link_output(link, link_res, signal,
clock_source, link_settings);
dpcd_write_rx_power_ctrl(link, true);
}
void dp_disable_link_phy(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal)
{
struct dc *dc = link->ctx->dc;
if (!link->wa_flags.dp_keep_receiver_powered &&
!link->skip_implict_edp_power_control &&
link->type != dc_connection_none)
dpcd_write_rx_power_ctrl(link, false);
dc->hwss.disable_link_output(link, link_res, signal);
/* Clear current link setting.*/
memset(&link->cur_link_settings, 0,
sizeof(link->cur_link_settings));
if (dc->clk_mgr->funcs->notify_link_rate_change)
dc->clk_mgr->funcs->notify_link_rate_change(dc->clk_mgr, link);
}
static inline bool is_immediate_downstream(struct dc_link *link, uint32_t offset)
{
return (dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt) ==
offset);
}
void dp_set_hw_lane_settings(
struct dc_link *link,
const struct link_resource *link_res,
const struct link_training_settings *link_settings,
uint32_t offset)
{
const struct link_hwss *link_hwss = get_link_hwss(link, link_res);
// Don't return here if using FIXED_VS link HWSS and encoding is 128b/132b
if ((link_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) &&
!is_immediate_downstream(link, offset) &&
(!((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) ||
link_dp_get_encoding_format(&link_settings->link_settings) == DP_8b_10b_ENCODING))
return;
if (link_hwss->ext.set_dp_lane_settings)
link_hwss->ext.set_dp_lane_settings(link, link_res,
&link_settings->link_settings,
link_settings->hw_lane_settings);
memmove(link->cur_lane_setting,
link_settings->hw_lane_settings,
sizeof(link->cur_lane_setting));
}
void dp_set_drive_settings(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
/* program ASIC PHY settings*/
dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
dp_hw_to_dpcd_lane_settings(lt_settings,
lt_settings->hw_lane_settings,
lt_settings->dpcd_lane_settings);
/* Notify DP sink the PHY settings from source */
dpcd_set_lane_settings(link, lt_settings, DPRX);
}
enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource *link_res, bool ready)
{
/* FEC has to be "set ready" before the link training.
* The policy is to always train with FEC
* if the sink supports it and leave it enabled on link.
* If FEC is not supported, disable it.
*/
struct link_encoder *link_enc = link_res->dio_link_enc;
enum dc_status status = DC_OK;
uint8_t fec_config = 0;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
ASSERT(link_enc);
if (link_enc->funcs->fec_set_ready == NULL)
return DC_NOT_SUPPORTED;
if (ready && dp_should_enable_fec(link)) {
fec_config = 1;
status = core_link_write_dpcd(link, DP_FEC_CONFIGURATION,
&fec_config, sizeof(fec_config));
if (status == DC_OK) {
link_enc->funcs->fec_set_ready(link_enc, true);
link->fec_state = dc_link_fec_ready;
}
} else {
if (link->fec_state == dc_link_fec_ready) {
fec_config = 0;
if (link->type != dc_connection_none)
core_link_write_dpcd(link, DP_FEC_CONFIGURATION,
&fec_config, sizeof(fec_config));
link_enc->funcs->fec_set_ready(link_enc, false);
link->fec_state = dc_link_fec_not_ready;
}
}
return status;
}
void dp_set_fec_enable(struct dc_link *link, const struct link_resource *link_res, bool enable)
{
struct link_encoder *link_enc = link_res->dio_link_enc;
if (!link->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
if (link_enc == NULL || link_enc->funcs == NULL || link_enc->funcs->fec_set_enable == NULL)
return;
if (enable && dp_should_enable_fec(link)) {
if (link->fec_state == dc_link_fec_ready) {
/* According to DP spec, FEC enable sequence can first
* be transmitted anytime after 1000 LL codes have
* been transmitted on the link after link training
* completion. Using 1 lane RBR should have the maximum
* time for transmitting 1000 LL codes which is 6.173 us.
* So use 7 microseconds delay instead.
*/
udelay(7);
link_enc->funcs->fec_set_enable(link_enc, true);
link->fec_state = dc_link_fec_enabled;
}
} else {
if (link->fec_state == dc_link_fec_enabled) {
link_enc->funcs->fec_set_enable(link_enc, false);
link->fec_state = dc_link_fec_ready;
}
}
}
@@ -0,0 +1,60 @@
/*
* 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 __DC_LINK_DP_PHY_H__
#define __DC_LINK_DP_PHY_H__
#include "link_service.h"
void dp_enable_link_phy(
struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings);
void dp_disable_link_phy(struct dc_link *link,
const struct link_resource *link_res,
enum signal_type signal);
void dp_set_hw_lane_settings(
struct dc_link *link,
const struct link_resource *link_res,
const struct link_training_settings *link_settings,
uint32_t offset);
void dp_set_drive_settings(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
enum dc_status dp_set_fec_ready(struct dc_link *link,
const struct link_resource *link_res, bool ready);
void dp_set_fec_enable(struct dc_link *link,
const struct link_resource *link_res, bool enable);
void dpcd_write_rx_power_ctrl(struct dc_link *link, bool on);
#endif /* __DC_LINK_DP_PHY_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,201 @@
/*
* 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 __DC_LINK_DP_TRAINING_H__
#define __DC_LINK_DP_TRAINING_H__
#include "link_service.h"
bool perform_link_training_with_retries(
const struct dc_link_settings *link_setting,
bool skip_video_pattern,
int attempts,
struct pipe_ctx *pipe_ctx,
enum signal_type signal,
bool do_fallback);
enum link_training_result dp_perform_link_training(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
bool skip_video_pattern);
bool dp_set_hw_training_pattern(
struct dc_link *link,
const struct link_resource *link_res,
enum dc_dp_training_pattern pattern,
uint32_t offset);
void dp_set_hw_test_pattern(
struct dc_link *link,
const struct link_resource *link_res,
enum dp_test_pattern test_pattern,
uint8_t *custom_pattern,
uint32_t custom_pattern_size);
enum dc_status dpcd_set_training_pattern(
struct dc_link *link,
enum dc_dp_training_pattern training_pattern);
/* Write DPCD drive settings. */
enum dc_status dpcd_set_lane_settings(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
uint32_t offset);
/* Write DPCD link configuration data. */
enum dc_status dpcd_set_link_settings(
struct dc_link *link,
const struct link_training_settings *lt_settings);
void dpcd_set_lt_pattern_and_lane_settings(
struct dc_link *link,
const struct link_training_settings *lt_settings,
enum dc_dp_training_pattern pattern,
uint32_t offset);
/* Read training status and adjustment requests from DPCD. */
enum dc_status dp_get_lane_status_and_lane_adjust(
struct dc_link *link,
const struct link_training_settings *link_training_setting,
union lane_status ln_status[LANE_COUNT_DP_MAX],
union lane_align_status_updated *ln_align,
union lane_adjust ln_adjust[LANE_COUNT_DP_MAX],
uint32_t offset);
enum dc_status dpcd_configure_lttpr_mode(
struct dc_link *link,
struct link_training_settings *lt_settings);
enum dc_status configure_lttpr_mode_transparent(struct dc_link *link);
enum dc_status dpcd_configure_channel_coding(
struct dc_link *link,
struct link_training_settings *lt_settings);
void repeater_training_done(struct dc_link *link, uint32_t offset);
void start_clock_recovery_pattern_early(struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset);
void dp_decide_training_settings(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
struct link_training_settings *lt_settings);
void dp_decide_lane_settings(
const struct link_training_settings *lt_settings,
const union lane_adjust ln_adjust[LANE_COUNT_DP_MAX],
struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX],
union dpcd_training_lane *dpcd_lane_settings);
enum dc_dp_training_pattern decide_cr_training_pattern(
const struct dc_link_settings *link_settings);
enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings);
enum lttpr_mode dp_decide_lttpr_mode(struct dc_link *link,
struct dc_link_settings *link_setting);
void dp_get_lttpr_mode_override(struct dc_link *link,
enum lttpr_mode *override);
void override_training_settings(
struct dc_link *link,
const struct dc_link_training_overrides *overrides,
struct link_training_settings *lt_settings);
/* Check DPCD training status registers to detect link loss. */
enum link_training_result dp_check_link_loss_status(
struct dc_link *link,
const struct link_training_settings *link_training_setting);
bool dp_is_cr_done(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_ch_eq_done(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_symbol_locked(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
bool dp_is_interlane_aligned(union lane_align_status_updated align_status);
bool is_repeater(const struct link_training_settings *lt_settings, uint32_t offset);
bool dp_is_max_vs_reached(
const struct link_training_settings *lt_settings);
uint8_t get_dpcd_link_rate(const struct dc_link_settings *link_settings);
enum link_training_result dp_get_cr_failure(enum dc_lane_count ln_count,
union lane_status *dpcd_lane_status);
void dp_hw_to_dpcd_lane_settings(
const struct link_training_settings *lt_settings,
const struct dc_lane_settings hw_lane_settings[LANE_COUNT_DP_MAX],
union dpcd_training_lane dpcd_lane_settings[LANE_COUNT_DP_MAX]);
void dp_wait_for_training_aux_rd_interval(
struct dc_link *link,
uint32_t wait_in_micro_secs);
enum dpcd_training_patterns
dp_training_pattern_to_dpcd_training_pattern(
struct dc_link *link,
enum dc_dp_training_pattern pattern);
uint8_t dp_initialize_scrambling_data_symbols(
struct dc_link *link,
enum dc_dp_training_pattern pattern);
void dp_log_training_result(
struct dc_link *link,
const struct link_training_settings *lt_settings,
enum link_training_result status);
uint32_t dp_translate_training_aux_read_interval(
uint32_t dpcd_aux_read_interval);
uint8_t dp_get_nibble_at_index(const uint8_t *buf,
uint32_t index);
bool dp_check_interlane_aligned(union lane_align_status_updated align_status,
struct dc_link *link,
uint8_t retries);
uint32_t dp_get_eq_aux_rd_interval(
const struct dc_link *link,
const struct link_training_settings *lt_settings,
uint32_t offset,
uint8_t retries);
bool dp_check_dpcd_reqeust_status(const struct dc_link *link,
enum dc_status status);
#endif /* __DC_LINK_DP_TRAINING_H__ */
@@ -0,0 +1,268 @@
/*
* 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 dp 128b/132b link training software policies and
* sequences.
*/
#include "link_dp_training_128b_132b.h"
#include "link_dp_training_8b_10b.h"
#include "link_dpcd.h"
#include "link_dp_phy.h"
#include "link_dp_capability.h"
#define DC_LOGGER \
link->ctx->logger
static enum dc_status dpcd_128b_132b_set_lane_settings(
struct dc_link *link,
const struct link_training_settings *link_training_setting)
{
enum dc_status status = core_link_write_dpcd(link,
DP_TRAINING_LANE0_SET,
(uint8_t *)(link_training_setting->dpcd_lane_settings),
sizeof(link_training_setting->dpcd_lane_settings));
DC_LOG_HW_LINK_TRAINING("%s:\n 0x%X TX_FFE_PRESET_VALUE = %x\n",
__func__,
DP_TRAINING_LANE0_SET,
link_training_setting->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE);
return status;
}
static void dpcd_128b_132b_get_aux_rd_interval(struct dc_link *link,
uint32_t *interval_in_us)
{
union dp_128b_132b_training_aux_rd_interval dpcd_interval;
uint32_t interval_unit = 0;
dpcd_interval.raw = 0;
core_link_read_dpcd(link, DP_128B132B_TRAINING_AUX_RD_INTERVAL,
&dpcd_interval.raw, sizeof(dpcd_interval.raw));
interval_unit = dpcd_interval.bits.UNIT ? 1 : 2; /* 0b = 2 ms, 1b = 1 ms */
/* (128b/132b_TRAINING_AUX_RD_INTERVAL value + 1) *
* INTERVAL_UNIT. The maximum is 256 ms
*/
*interval_in_us = (dpcd_interval.bits.VALUE + 1) * interval_unit * 1000;
}
static enum link_training_result dp_perform_128b_132b_channel_eq_done_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
uint8_t loop_count;
uint32_t aux_rd_interval = 0;
uint32_t wait_time = 0;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
enum dc_status status = DC_OK;
enum link_training_result result = LINK_TRAINING_SUCCESS;
/* Transmit 128b/132b_TPS1 over Main-Link */
dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_cr, DPRX);
/* Set TRAINING_PATTERN_SET to 01h */
dpcd_set_training_pattern(link, lt_settings->pattern_for_cr);
/* Adjust TX_FFE_PRESET_VALUE and Transmit 128b/132b_TPS2 over Main-Link */
dpcd_128b_132b_get_aux_rd_interval(link, &aux_rd_interval);
dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_eq, DPRX);
/* Set loop counter to start from 1 */
loop_count = 1;
/* Set TRAINING_PATTERN_SET to 02h and TX_FFE_PRESET_VALUE in one AUX transaction */
dpcd_set_lt_pattern_and_lane_settings(link, lt_settings,
lt_settings->pattern_for_eq, DPRX);
/* poll for channel EQ done */
while (result == LINK_TRAINING_SUCCESS) {
dp_wait_for_training_aux_rd_interval(link, aux_rd_interval);
wait_time += aux_rd_interval;
status = dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
dpcd_128b_132b_get_aux_rd_interval(link, &aux_rd_interval);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
} else if (dp_is_ch_eq_done(lt_settings->link_settings.lane_count,
dpcd_lane_status)) {
/* pass */
break;
} else if (loop_count >= lt_settings->eq_loop_count_limit) {
result = DP_128b_132b_MAX_LOOP_COUNT_REACHED;
} else if (dpcd_lane_status_updated.bits.LT_FAILED_128b_132b) {
result = DP_128b_132b_LT_FAILED;
} else {
dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
dpcd_128b_132b_set_lane_settings(link, lt_settings);
}
loop_count++;
}
/* poll for EQ interlane align done */
while (result == LINK_TRAINING_SUCCESS) {
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
} else if (dpcd_lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b) {
/* pass */
break;
} else if (wait_time >= lt_settings->eq_wait_time_limit) {
result = DP_128b_132b_CHANNEL_EQ_DONE_TIMEOUT;
} else if (dpcd_lane_status_updated.bits.LT_FAILED_128b_132b) {
result = DP_128b_132b_LT_FAILED;
} else {
dp_wait_for_training_aux_rd_interval(link,
lt_settings->eq_pattern_time);
wait_time += lt_settings->eq_pattern_time;
status = dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
}
}
return result;
}
static enum link_training_result dp_perform_128b_132b_cds_done_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
(void)link_res;
/* Assumption: assume hardware has transmitted eq pattern */
enum dc_status status = DC_OK;
enum link_training_result result = LINK_TRAINING_SUCCESS;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
uint32_t wait_time = 0;
/* initiate CDS done sequence */
dpcd_set_training_pattern(link, lt_settings->pattern_for_cds);
/* poll for CDS interlane align done and symbol lock */
while (result == LINK_TRAINING_SUCCESS) {
dp_wait_for_training_aux_rd_interval(link,
lt_settings->cds_pattern_time);
wait_time += lt_settings->cds_pattern_time;
status = dp_get_lane_status_and_lane_adjust(link, lt_settings, dpcd_lane_status,
&dpcd_lane_status_updated, dpcd_lane_adjust, DPRX);
if (status != DC_OK) {
result = LINK_TRAINING_ABORT;
} else if (dp_is_symbol_locked(lt_settings->link_settings.lane_count, dpcd_lane_status) &&
dpcd_lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b) {
/* pass */
break;
} else if (dpcd_lane_status_updated.bits.LT_FAILED_128b_132b) {
result = DP_128b_132b_LT_FAILED;
} else if (wait_time >= lt_settings->cds_wait_time_limit) {
result = DP_128b_132b_CDS_DONE_TIMEOUT;
}
}
return result;
}
enum link_training_result dp_perform_128b_132b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result result = LINK_TRAINING_SUCCESS;
/* TODO - DP2.0 Link: remove legacy_dp2_lt logic */
if (link->dc->debug.legacy_dp2_lt) {
struct link_training_settings legacy_settings;
decide_8b_10b_training_settings(link,
link_res,
&lt_settings->link_settings,
&legacy_settings);
return dp_perform_8b_10b_link_training(link, link_res, &legacy_settings);
}
dpcd_set_link_settings(link, lt_settings);
if (result == LINK_TRAINING_SUCCESS) {
result = dp_perform_128b_132b_channel_eq_done_sequence(link, link_res, lt_settings);
if (result == LINK_TRAINING_SUCCESS)
DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__);
}
if (result == LINK_TRAINING_SUCCESS) {
result = dp_perform_128b_132b_cds_done_sequence(link, link_res, lt_settings);
if (result == LINK_TRAINING_SUCCESS)
DC_LOG_HW_LINK_TRAINING("%s: CDS done.\n", __func__);
}
return result;
}
void decide_128b_132b_training_settings(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
struct link_training_settings *lt_settings)
{
memset(lt_settings, 0, sizeof(*lt_settings));
lt_settings->link_settings = *link_settings;
/* TODO: should decide link spread when populating link_settings */
lt_settings->link_settings.link_spread = link->dp_ss_off ? LINK_SPREAD_DISABLED :
LINK_SPREAD_05_DOWNSPREAD_30KHZ;
lt_settings->pattern_for_cr = decide_cr_training_pattern(link_settings);
lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_res, link_settings);
lt_settings->eq_pattern_time = 2500;
lt_settings->eq_wait_time_limit = 400000;
lt_settings->eq_loop_count_limit = 20;
lt_settings->pattern_for_cds = DP_128b_132b_TPS2_CDS;
lt_settings->cds_pattern_time = 2500;
lt_settings->cds_wait_time_limit = (dp_parse_lttpr_repeater_count(
link->dpcd_caps.lttpr_caps.phy_repeater_cnt) + 1) * 20000;
lt_settings->disallow_per_lane_settings = true;
lt_settings->lttpr_mode = dp_decide_128b_132b_lttpr_mode(link);
dp_hw_to_dpcd_lane_settings(lt_settings,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
enum lttpr_mode dp_decide_128b_132b_lttpr_mode(struct dc_link *link)
{
enum lttpr_mode mode = LTTPR_MODE_NON_LTTPR;
if (dp_is_lttpr_present(link))
mode = LTTPR_MODE_NON_TRANSPARENT;
DC_LOG_DC("128b_132b chose LTTPR_MODE %d.\n", mode);
return mode;
}
@@ -0,0 +1,43 @@
/*
* 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 __DC_LINK_DP_TRAINING_128B_132B_H__
#define __DC_LINK_DP_TRAINING_128B_132B_H__
#include "link_dp_training.h"
enum link_training_result dp_perform_128b_132b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
void decide_128b_132b_training_settings(struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_settings,
struct link_training_settings *lt_settings);
enum lttpr_mode dp_decide_128b_132b_lttpr_mode(struct dc_link *link);
#endif /* __DC_LINK_DP_TRAINING_128B_132B_H__ */
@@ -0,0 +1,491 @@
/*
* 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 dp 8b/10b link training software policies and
* sequences.
*/
#include "link_dp_training_8b_10b.h"
#include "link_dpcd.h"
#include "link_dp_phy.h"
#include "link_dp_capability.h"
#define DC_LOGGER \
link->ctx->logger
static void get_default_8b_10b_lttpr_aux_rd_interval(
union training_aux_rd_interval *training_rd_interval)
{
/* LTTPR are required to program DPCD 0000Eh to 0x4 (16ms) upon AUX
* read reply to this register. Since old sinks with DPCD rev 1.1
* and earlier may not support this register, assume the mandatory
* value is programmed by the LTTPR to avoid AUX timeout issues.
*/
training_rd_interval->raw = 0x4;
}
static int32_t get_cr_training_aux_rd_interval(struct dc_link *link,
const struct dc_link_settings *link_settings,
enum lttpr_mode lttpr_mode)
{
union training_aux_rd_interval training_rd_interval;
uint32_t wait_in_micro_secs = 100;
memset(&training_rd_interval, 0, sizeof(training_rd_interval));
if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) {
if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12)
core_link_read_dpcd(
link,
DP_TRAINING_AUX_RD_INTERVAL,
(uint8_t *)&training_rd_interval,
sizeof(training_rd_interval));
else if (dp_is_lttpr_present(link))
get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval);
if (training_rd_interval.raw != 0) {
if (lttpr_mode != LTTPR_MODE_NON_TRANSPARENT)
wait_in_micro_secs = 400;
if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL)
wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000;
}
}
return wait_in_micro_secs;
}
static uint32_t get_eq_training_aux_rd_interval(
struct dc_link *link,
const struct dc_link_settings *link_settings)
{
union training_aux_rd_interval training_rd_interval;
memset(&training_rd_interval, 0, sizeof(training_rd_interval));
if (link_dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) {
core_link_read_dpcd(
link,
DP_128B132B_TRAINING_AUX_RD_INTERVAL,
(uint8_t *)&training_rd_interval,
sizeof(training_rd_interval));
} else if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) {
if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12)
core_link_read_dpcd(
link,
DP_TRAINING_AUX_RD_INTERVAL,
(uint8_t *)&training_rd_interval,
sizeof(training_rd_interval));
else if (dp_is_lttpr_present(link))
get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval);
}
switch (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) {
case 0: return 400;
case 1: return 4000;
case 2: return 8000;
case 3: return 12000;
case 4: return 16000;
case 5: return 32000;
case 6: return 64000;
default: return 400;
}
}
void decide_8b_10b_training_settings(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
struct link_training_settings *lt_settings)
{
memset(lt_settings, '\0', sizeof(struct link_training_settings));
/* Initialize link settings */
lt_settings->link_settings.use_link_rate_set = link_setting->use_link_rate_set;
lt_settings->link_settings.link_rate_set = link_setting->link_rate_set;
lt_settings->link_settings.link_rate = link_setting->link_rate;
lt_settings->link_settings.lane_count = link_setting->lane_count;
/* TODO hard coded to SS for now
* lt_settings.link_settings.link_spread =
* dal_display_path_is_ss_supported(
* path_mode->display_path) ?
* LINK_SPREAD_05_DOWNSPREAD_30KHZ :
* LINK_SPREAD_DISABLED;
*/
lt_settings->link_settings.link_spread = link->dp_ss_off ?
LINK_SPREAD_DISABLED : LINK_SPREAD_05_DOWNSPREAD_30KHZ;
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;
lt_settings->should_set_fec_ready = true;
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 = (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
* This allows these LTTPRs to minimize freq/phase and skew variation during lock and deskew sequences
*/
if ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) ==
AMD_EXT_DISPLAY_PATH_CAPS__DP_EARLY_8B10B_TPS2) {
lt_settings->lttpr_early_tps2 = true;
}
}
enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link)
{
bool is_lttpr_present = dp_is_lttpr_present(link);
bool vbios_lttpr_force_non_transparent = link->dc->caps.vbios_lttpr_enable;
bool vbios_lttpr_aware = link->dc->caps.vbios_lttpr_aware;
if (!is_lttpr_present)
return LTTPR_MODE_NON_LTTPR;
if (vbios_lttpr_aware) {
if (vbios_lttpr_force_non_transparent) {
DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT due to VBIOS DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n");
return LTTPR_MODE_NON_TRANSPARENT;
} else {
DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default due to VBIOS not set DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n");
return LTTPR_MODE_TRANSPARENT;
}
}
if (link->dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A &&
link->dc->caps.extended_aux_timeout_support) {
DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default and dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A set to 1.\n");
return LTTPR_MODE_NON_TRANSPARENT;
}
DC_LOG_DC("chose LTTPR_MODE_NON_LTTPR.\n");
return LTTPR_MODE_NON_LTTPR;
}
static void set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t lttpr_count)
{
/* Vendor-specific LTTPR early TPS2 sequence:
* 1. Output TPS2
* 2. Wait 400us
* 3. Set link settings as usual
* 4. Write TPS1 to DP_TRAINING_PATTERN_SET_PHY_REPEATERx targeting LTTPR closest to host
* 5. Wait 1ms
* 6. Begin link training as usual
* */
uint32_t closest_lttpr_address_offset = dp_get_closest_lttpr_offset((uint8_t)lttpr_count);
union dpcd_training_pattern dpcd_pattern = {0};
dpcd_pattern.v1_4.TRAINING_PATTERN_SET = 1;
dpcd_pattern.v1_4.SCRAMBLING_DISABLE = 1;
DC_LOG_HW_LINK_TRAINING("%s\n GPU sends TPS2. Wait 400us.\n", __func__);
dp_set_hw_training_pattern(link, link_res, DP_TRAINING_PATTERN_SEQUENCE_2, DPRX);
dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
udelay(400);
dpcd_set_link_settings(link, lt_settings);
core_link_write_dpcd(link, DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + closest_lttpr_address_offset, &dpcd_pattern.raw, 1);
udelay(1000);
}
enum link_training_result perform_8b_10b_clock_recovery_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset)
{
enum dc_status status;
uint32_t retries_cr;
uint32_t retry_count;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
union lane_align_status_updated dpcd_lane_status_updated;
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
retries_cr = 0;
retry_count = 0;
memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status));
memset(&dpcd_lane_status_updated, '\0',
sizeof(dpcd_lane_status_updated));
if (!link->ctx->dc->work_arounds.lt_early_cr_pattern)
dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_cr, offset);
/* najeeb - The synaptics MST hub can put the LT in
* infinite loop by switching the VS
*/
/* between level 0 and level 1 continuously, here
* we try for CR lock for LinkTrainingMaxCRRetry count*/
while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
/* 1. call HWSS to set lane settings*/
dp_set_hw_lane_settings(
link,
link_res,
lt_settings,
offset);
/* 2. update DPCD of the receiver*/
if (!retry_count)
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration.*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
lt_settings->pattern_for_cr,
offset);
else
dpcd_set_lane_settings(
link,
lt_settings,
offset);
/* 3. wait receiver to lock-on*/
wait_time_microsec = lt_settings->cr_pattern_time;
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested drive
* settings as set by the sink
*/
status = dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
offset);
if (dp_check_dpcd_reqeust_status(link, status))
return LINK_TRAINING_ABORT;
/* 5. check CR done*/
if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__);
return LINK_TRAINING_SUCCESS;
}
/* 6. max VS reached*/
if ((link_dp_get_encoding_format(&lt_settings->link_settings) ==
DP_8b_10b_ENCODING) &&
dp_is_max_vs_reached(lt_settings))
break;
/* 7. same lane settings*/
/* Note: settings are the same for all lanes,
* so comparing first lane is sufficient*/
if ((link_dp_get_encoding_format(&lt_settings->link_settings) == DP_8b_10b_ENCODING) &&
lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
retries_cr++;
else if ((link_dp_get_encoding_format(&lt_settings->link_settings) == DP_128b_132b_ENCODING) &&
lt_settings->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE ==
dpcd_lane_adjust[0].tx_ffe.PRESET_VALUE)
retries_cr++;
else
retries_cr = 0;
/* 8. update VS/PE/PC2 in lt_settings*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
retry_count++;
}
if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
ASSERT(0);
DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue",
__func__,
LINK_TRAINING_MAX_CR_RETRY);
}
return dp_get_cr_failure(lane_count, dpcd_lane_status);
}
enum link_training_result perform_8b_10b_channel_equalization_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset)
{
enum dc_status status;
enum dc_dp_training_pattern tr_pattern;
uint32_t retries_ch_eq;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
/* Note: also check that TPS4 is a supported feature*/
tr_pattern = lt_settings->pattern_for_eq;
if (is_repeater(lt_settings, offset) && link_dp_get_encoding_format(&lt_settings->link_settings) == DP_8b_10b_ENCODING)
tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4;
dp_set_hw_training_pattern(link, link_res, tr_pattern, offset);
for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
retries_ch_eq++) {
dp_set_hw_lane_settings(link, link_res, lt_settings, offset);
/* 2. update DPCD*/
if (!retries_ch_eq)
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration
*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
tr_pattern, offset);
else
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, (uint8_t)retries_ch_eq);
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested
* drive settings as set by the sink*/
status = dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
offset);
if (dp_check_dpcd_reqeust_status(link, status))
return LINK_TRAINING_ABORT;
/* 5. check CR done*/
if (!dp_is_cr_done(lane_count, dpcd_lane_status))
return dpcd_lane_status[0].bits.CR_DONE_0 ?
LINK_TRAINING_EQ_FAIL_CR_PARTIAL :
LINK_TRAINING_EQ_FAIL_CR;
/* 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, (uint8_t)retries_ch_eq))
return LINK_TRAINING_SUCCESS;
/* 7. update VS/PE/PC2 in lt_settings*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
return LINK_TRAINING_EQ_FAIL_EQ;
}
enum link_training_result dp_perform_8b_10b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result status = LINK_TRAINING_SUCCESS;
uint8_t repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
uint8_t repeater_id;
uint8_t lane = 0;
if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX);
/* 1. set link rate, lane count and spread. */
if (lt_settings->lttpr_early_tps2)
set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(link, link_res, lt_settings, repeater_cnt);
else
dpcd_set_link_settings(link, lt_settings);
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
/* 2. perform link training (set link training done
* to false is done as well)
*/
for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS);
repeater_id--) {
status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, repeater_id);
if (status != LINK_TRAINING_SUCCESS) {
repeater_training_done(link, repeater_id);
break;
}
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
repeater_id);
if (status == LINK_TRAINING_SUCCESS)
DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__);
repeater_training_done(link, repeater_id);
if (status != LINK_TRAINING_SUCCESS)
break;
for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
lt_settings->dpcd_lane_settings[lane].raw = 0;
lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0;
lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0;
}
}
}
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, DPRX);
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
DPRX);
if (status == LINK_TRAINING_SUCCESS)
DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__);
}
}
return status;
}
@@ -0,0 +1,62 @@
/*
* 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 __DC_LINK_DP_TRAINING_8B_10B_H__
#define __DC_LINK_DP_TRAINING_8B_10B_H__
#include "link_dp_training.h"
/* to avoid infinite loop where-in the receiver
* switches between different VS
*/
#define LINK_TRAINING_MAX_CR_RETRY 100
#define LINK_TRAINING_MAX_RETRY_COUNT 5
enum link_training_result dp_perform_8b_10b_link_training(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
enum link_training_result perform_8b_10b_clock_recovery_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset);
enum link_training_result perform_8b_10b_channel_equalization_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings,
uint32_t offset);
enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link);
void decide_8b_10b_training_settings(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
struct link_training_settings *lt_settings);
#endif /* __DC_LINK_DP_TRAINING_8B_10B_H__ */
@@ -0,0 +1,80 @@
/*
* 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:
*
*/
#include "link_dp_training_auxless.h"
#include "link_dp_phy.h"
#define DC_LOGGER \
link->ctx->logger
bool dp_perform_link_training_skip_aux(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting)
{
struct link_training_settings lt_settings = {0};
dp_decide_training_settings(
link,
link_res,
link_setting,
&lt_settings);
override_training_settings(
link,
&link->preferred_training_settings,
&lt_settings);
/* 1. Perform_clock_recovery_sequence. */
/* transmit training pattern for clock recovery */
dp_set_hw_training_pattern(link, link_res, lt_settings.pattern_for_cr, DPRX);
/* call HWSS to set lane settings*/
dp_set_hw_lane_settings(link, link_res, &lt_settings, DPRX);
/* wait receiver to lock-on*/
dp_wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time);
/* 2. Perform_channel_equalization_sequence. */
/* transmit training pattern for channel equalization. */
dp_set_hw_training_pattern(link, link_res, lt_settings.pattern_for_eq, DPRX);
/* call HWSS to set lane settings*/
dp_set_hw_lane_settings(link, link_res, &lt_settings, DPRX);
/* wait receiver to lock-on. */
dp_wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time);
/* 3. Perform_link_training_int. */
/* Mainlink output idle pattern. */
dp_set_hw_test_pattern(link, link_res, DP_TEST_PATTERN_VIDEO_MODE, NULL, 0);
dp_log_training_result(link, &lt_settings, LINK_TRAINING_SUCCESS);
return true;
}
@@ -0,0 +1,35 @@
/*
* 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 __DC_LINK_DP_TRAINING_AUXLESS_H__
#define __DC_LINK_DP_TRAINING_AUXLESS_H__
#include "link_dp_training.h"
bool dp_perform_link_training_skip_aux(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting);
#endif /* __DC_LINK_DP_TRAINING_AUXLESS_H__ */
@@ -0,0 +1,60 @@
/*
* 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 __DC_LINK_DP_TRAINING_DPIA_H__
#define __DC_LINK_DP_TRAINING_DPIA_H__
#include "link_dp_training.h"
/* The approximate time (us) it takes to transmit 9 USB4 DP clock sync packets. */
#define DPIA_CLK_SYNC_DELAY 16000
/* Train DP tunneling link for USB4 DPIA display endpoint.
* DPIA equivalent of dc_link_dp_perfrorm_link_training.
* Aborts link training upon detection of sink unplug.
*/
enum link_training_result dpia_perform_link_training(
struct dc_link *link,
const struct link_resource *link_res,
const struct dc_link_settings *link_setting,
bool skip_video_pattern);
void dpia_training_abort(
struct dc_link *link,
struct link_training_settings *lt_settings,
uint32_t hop);
uint32_t dpia_get_eq_aux_rd_interval(
const struct dc_link *link,
const struct link_training_settings *lt_settings,
uint32_t hop);
void dpia_set_tps_notification(
struct dc_link *link,
const struct link_training_settings *lt_settings,
uint8_t pattern,
uint32_t offset);
#endif /* __DC_LINK_DP_TRAINING_DPIA_H__ */
@@ -0,0 +1,553 @@
/*
* 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 8b/10b link training specially modified to support an
* embedded retimer chip. This retimer chip is referred as fixed vs pe retimer.
* Unlike native dp connection this chip requires a modified link training
* protocol based on 8b/10b link training. Since this is a non standard sequence
* and we must support this hardware, we decided to isolate it in its own
* training sequence inside its own file.
*/
#include "link_dp_training_fixed_vs_pe_retimer.h"
#include "link_dp_training_8b_10b.h"
#include "link_dpcd.h"
#include "link_dp_phy.h"
#include "link_dp_capability.h"
#include "link_ddc.h"
#define DC_LOGGER \
link->ctx->logger
void dp_fixed_vs_pe_read_lane_adjust(
struct dc_link *link,
union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX])
{
const uint8_t vendor_lttpr_write_data_vs[3] = {0x0, 0x53, 0x63};
const uint8_t vendor_lttpr_write_data_pe[3] = {0x0, 0x54, 0x63};
uint8_t dprx_vs = 0;
uint8_t dprx_pe = 0;
uint8_t lane;
/* W/A to read lane settings requested by DPRX */
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
link_query_fixed_vs_pe_retimer(link->ddc, &dprx_vs, 1);
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
link_query_fixed_vs_pe_retimer(link->ddc, &dprx_pe, 1);
for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
dpcd_lane_adjust[lane].bits.VOLTAGE_SWING_SET = (dprx_vs >> (2 * lane)) & 0x3;
dpcd_lane_adjust[lane].bits.PRE_EMPHASIS_SET = (dprx_pe >> (2 * lane)) & 0x3;
}
}
void dp_fixed_vs_pe_set_retimer_lane_settings(
struct dc_link *link,
const union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX],
uint8_t lane_count)
{
const uint8_t vendor_lttpr_write_data_reset[4] = {0x1, 0x50, 0x63, 0xFF};
uint8_t vendor_lttpr_write_data_vs[4] = {0x1, 0x51, 0x63, 0x0};
uint8_t vendor_lttpr_write_data_pe[4] = {0x1, 0x52, 0x63, 0x0};
uint8_t lane = 0;
for (lane = 0; lane < lane_count; lane++) {
vendor_lttpr_write_data_vs[3] |=
dpcd_lane_adjust[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
vendor_lttpr_write_data_pe[3] |=
dpcd_lane_adjust[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
}
/* Force LTTPR to output desired VS and PE */
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_reset[0], sizeof(vendor_lttpr_write_data_reset));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
}
static enum link_training_result perform_fixed_vs_pe_nontransparent_training_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
enum link_training_result status = LINK_TRAINING_SUCCESS;
uint8_t lane = 0;
uint8_t toggle_rate = 0x6;
uint8_t target_rate = 0x6;
bool apply_toggle_rate_wa = false;
uint8_t repeater_cnt;
uint8_t repeater_id;
/* Fixed VS/PE specific: Force CR AUX RD Interval to at least 16ms */
if (lt_settings->cr_pattern_time < 16000)
lt_settings->cr_pattern_time = 16000;
/* Fixed VS/PE specific: Toggle link rate */
apply_toggle_rate_wa = ((link->vendor_specific_lttpr_link_rate_wa == target_rate) || (link->vendor_specific_lttpr_link_rate_wa == 0));
target_rate = get_dpcd_link_rate(&lt_settings->link_settings);
toggle_rate = (target_rate == 0x6) ? 0xA : 0x6;
if (apply_toggle_rate_wa)
lt_settings->link_settings.link_rate = toggle_rate;
if (link->ctx->dc->work_arounds.lt_early_cr_pattern)
start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX);
/* 1. set link rate, lane count and spread. */
dpcd_set_link_settings(link, lt_settings);
/* Fixed VS/PE specific: Toggle link rate back*/
if (apply_toggle_rate_wa) {
core_link_write_dpcd(
link,
DP_LINK_BW_SET,
&target_rate,
1);
}
link->vendor_specific_lttpr_link_rate_wa = target_rate;
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
/* 2. perform link training (set link training done
* to false is done as well)
*/
repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS);
repeater_id--) {
status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, repeater_id);
if (status != LINK_TRAINING_SUCCESS) {
repeater_training_done(link, repeater_id);
break;
}
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
repeater_id);
repeater_training_done(link, repeater_id);
if (status != LINK_TRAINING_SUCCESS)
break;
for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) {
lt_settings->dpcd_lane_settings[lane].raw = 0;
lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0;
lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0;
}
}
}
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, DPRX);
if (status == LINK_TRAINING_SUCCESS) {
status = perform_8b_10b_channel_equalization_sequence(link,
link_res,
lt_settings,
DPRX);
}
}
return status;
}
enum link_training_result dp_perform_fixed_vs_pe_training_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings)
{
const uint8_t vendor_lttpr_write_data_reset[4] = {0x1, 0x50, 0x63, 0xFF};
const uint8_t offset = dp_parse_lttpr_repeater_count(
link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
const uint8_t vendor_lttpr_write_data_intercept_en[4] = {0x1, 0x55, 0x63, 0x0};
const uint8_t vendor_lttpr_write_data_intercept_dis[4] = {0x1, 0x55, 0x63, 0x6E};
const uint8_t vendor_lttpr_write_data_adicora_eq1[4] = {0x1, 0x55, 0x63, 0x2E};
const uint8_t vendor_lttpr_write_data_adicora_eq2[4] = {0x1, 0x55, 0x63, 0x01};
const uint8_t vendor_lttpr_write_data_adicora_eq3[4] = {0x1, 0x55, 0x63, 0x68};
uint32_t pre_disable_intercept_delay_ms = 0;
uint8_t vendor_lttpr_write_data_vs[4] = {0x1, 0x51, 0x63, 0x0};
uint8_t vendor_lttpr_write_data_pe[4] = {0x1, 0x52, 0x63, 0x0};
const uint8_t vendor_lttpr_write_data_4lane_1[4] = {0x1, 0x6E, 0xF2, 0x19};
const uint8_t vendor_lttpr_write_data_4lane_2[4] = {0x1, 0x6B, 0xF2, 0x01};
const uint8_t vendor_lttpr_write_data_4lane_3[4] = {0x1, 0x6D, 0xF2, 0x18};
const uint8_t vendor_lttpr_write_data_4lane_4[4] = {0x1, 0x6C, 0xF2, 0x03};
const uint8_t vendor_lttpr_write_data_4lane_5[4] = {0x1, 0x03, 0xF3, 0x06};
const uint8_t vendor_lttpr_write_data_dpmf[4] = {0x1, 0x6, 0x70, 0x87};
enum link_training_result status = LINK_TRAINING_SUCCESS;
uint8_t lane = 0;
union down_spread_ctrl downspread = {0};
union lane_count_set lane_count_set = {0};
uint8_t toggle_rate;
uint8_t rate;
/* Only 8b/10b is supported */
ASSERT(link_dp_get_encoding_format(&lt_settings->link_settings) ==
DP_8b_10b_ENCODING);
if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
status = perform_fixed_vs_pe_nontransparent_training_sequence(link, link_res, lt_settings);
return status;
}
if (offset != 0xFF) {
if (offset == 2) {
pre_disable_intercept_delay_ms = link->dc->debug.fixed_vs_aux_delay_config_wa;
/* Certain display and cable configuration require extra delay */
} else if (offset > 2) {
pre_disable_intercept_delay_ms = link->dc->debug.fixed_vs_aux_delay_config_wa * 2;
}
}
/* Vendor specific: Reset lane settings */
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_reset[0], sizeof(vendor_lttpr_write_data_reset));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
/* Vendor specific: Enable intercept */
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_intercept_en[0], sizeof(vendor_lttpr_write_data_intercept_en));
/* 1. set link rate, lane count and spread. */
downspread.raw = (uint8_t)(lt_settings->link_settings.link_spread);
lane_count_set.bits.LANE_COUNT_SET =
lt_settings->link_settings.lane_count;
lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing;
lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0;
if (lt_settings->pattern_for_eq < DP_TRAINING_PATTERN_SEQUENCE_4) {
lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED;
}
core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL,
&downspread.raw, sizeof(downspread));
core_link_write_dpcd(link, DP_LANE_COUNT_SET,
&lane_count_set.raw, 1);
rate = get_dpcd_link_rate(&lt_settings->link_settings);
// Only perform toggle if FIXED_VS LTTPR reports no IEEE OUI
if (memcmp("\x00\x00\x00", &link->dpcd_caps.lttpr_caps.lttpr_ieee_oui[0], 3) == 0) {
/* Vendor specific: Toggle link rate */
toggle_rate = (rate == 0x6) ? 0xA : 0x6;
if (link->vendor_specific_lttpr_link_rate_wa == rate || link->vendor_specific_lttpr_link_rate_wa == 0) {
core_link_write_dpcd(
link,
DP_LINK_BW_SET,
&toggle_rate,
1);
}
link->vendor_specific_lttpr_link_rate_wa = rate;
}
core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x framing = %x\n %x spread = %x\n",
__func__,
DP_LINK_BW_SET,
lt_settings->link_settings.link_rate,
DP_LANE_COUNT_SET,
lt_settings->link_settings.lane_count,
lt_settings->enhanced_framing,
DP_DOWNSPREAD_CTRL,
lt_settings->link_settings.link_spread);
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_dpmf[0],
sizeof(vendor_lttpr_write_data_dpmf));
if (lt_settings->link_settings.lane_count == LANE_COUNT_FOUR) {
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_1[0], sizeof(vendor_lttpr_write_data_4lane_1));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_2[0], sizeof(vendor_lttpr_write_data_4lane_2));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_3[0], sizeof(vendor_lttpr_write_data_4lane_3));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_4[0], sizeof(vendor_lttpr_write_data_4lane_4));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_4lane_5[0], sizeof(vendor_lttpr_write_data_4lane_5));
}
/* 2. Perform link training */
/* Perform Clock Recovery Sequence */
if (status == LINK_TRAINING_SUCCESS) {
const uint8_t max_vendor_dpcd_retries = 10;
uint32_t retries_cr;
uint32_t retry_count;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX];
union lane_align_status_updated dpcd_lane_status_updated;
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
uint8_t i = 0;
retries_cr = 0;
retry_count = 0;
memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status));
memset(&dpcd_lane_status_updated, '\0',
sizeof(dpcd_lane_status_updated));
while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
/* 1. call HWSS to set lane settings */
dp_set_hw_lane_settings(
link,
link_res,
lt_settings,
0);
/* 2. update DPCD of the receiver */
if (!retry_count) {
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration.
*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
lt_settings->pattern_for_cr,
0);
/* Vendor specific: Disable intercept */
for (i = 0; i < max_vendor_dpcd_retries; i++) {
if (pre_disable_intercept_delay_ms != 0)
msleep(pre_disable_intercept_delay_ms);
if (link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_intercept_dis[0],
sizeof(vendor_lttpr_write_data_intercept_dis)))
break;
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_intercept_en[0],
sizeof(vendor_lttpr_write_data_intercept_en));
}
} else {
vendor_lttpr_write_data_vs[3] = 0;
vendor_lttpr_write_data_pe[3] = 0;
for (lane = 0; lane < lane_count; lane++) {
vendor_lttpr_write_data_vs[3] |=
lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
vendor_lttpr_write_data_pe[3] |=
lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
}
/* Vendor specific: Update VS and PE to DPRX requested value */
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
dpcd_set_lane_settings(
link,
lt_settings,
0);
}
/* 3. wait receiver to lock-on*/
wait_time_microsec = lt_settings->cr_pattern_time;
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested drive
* settings as set by the sink
*/
dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
0);
/* 5. check CR done*/
if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
break;
}
/* 6. max VS reached*/
if (dp_is_max_vs_reached(lt_settings))
break;
/* 7. same lane settings */
/* Note: settings are the same for all lanes,
* so comparing first lane is sufficient
*/
if (lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
retries_cr++;
else
retries_cr = 0;
/* 8. update VS/PE/PC2 in lt_settings*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
retry_count++;
}
if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) {
ASSERT(0);
DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue",
__func__,
LINK_TRAINING_MAX_CR_RETRY);
}
status = dp_get_cr_failure(lane_count, dpcd_lane_status);
}
/* Perform Channel EQ Sequence */
if (status == LINK_TRAINING_SUCCESS) {
enum dc_dp_training_pattern tr_pattern;
uint32_t retries_ch_eq;
uint32_t wait_time_microsec;
enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
union lane_align_status_updated dpcd_lane_status_updated = {0};
union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_adicora_eq1[0],
sizeof(vendor_lttpr_write_data_adicora_eq1));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_adicora_eq2[0],
sizeof(vendor_lttpr_write_data_adicora_eq2));
/* Note: also check that TPS4 is a supported feature*/
tr_pattern = lt_settings->pattern_for_eq;
dp_set_hw_training_pattern(link, link_res, tr_pattern, 0);
status = LINK_TRAINING_EQ_FAIL_EQ;
for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT;
retries_ch_eq++) {
dp_set_hw_lane_settings(link, link_res, lt_settings, 0);
vendor_lttpr_write_data_vs[3] = 0;
vendor_lttpr_write_data_pe[3] = 0;
for (lane = 0; lane < lane_count; lane++) {
vendor_lttpr_write_data_vs[3] |=
lt_settings->dpcd_lane_settings[lane].bits.VOLTAGE_SWING_SET << (2 * lane);
vendor_lttpr_write_data_pe[3] |=
lt_settings->dpcd_lane_settings[lane].bits.PRE_EMPHASIS_SET << (2 * lane);
}
/* Vendor specific: Update VS and PE to DPRX requested value */
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_vs[0], sizeof(vendor_lttpr_write_data_vs));
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_pe[0], sizeof(vendor_lttpr_write_data_pe));
/* 2. update DPCD*/
if (!retries_ch_eq) {
/* EPR #361076 - write as a 5-byte burst,
* but only for the 1-st iteration
*/
dpcd_set_lt_pattern_and_lane_settings(
link,
lt_settings,
tr_pattern, 0);
link_configure_fixed_vs_pe_retimer(link->ddc,
&vendor_lttpr_write_data_adicora_eq3[0],
sizeof(vendor_lttpr_write_data_adicora_eq3));
} else
dpcd_set_lane_settings(link, lt_settings, 0);
/* 3. wait for receiver to lock-on*/
wait_time_microsec = lt_settings->eq_pattern_time;
dp_wait_for_training_aux_rd_interval(
link,
wait_time_microsec);
/* 4. Read lane status and requested
* drive settings as set by the sink
*/
dp_get_lane_status_and_lane_adjust(
link,
lt_settings,
dpcd_lane_status,
&dpcd_lane_status_updated,
dpcd_lane_adjust,
0);
/* 5. check CR done*/
if (!dp_is_cr_done(lane_count, dpcd_lane_status)) {
status = LINK_TRAINING_EQ_FAIL_CR;
break;
}
/* 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_is_interlane_aligned(dpcd_lane_status_updated)) {
status = LINK_TRAINING_SUCCESS;
break;
}
/* 7. update VS/PE/PC2 in lt_settings*/
dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
}
}
return status;
}
@@ -0,0 +1,45 @@
/*
* 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 __DC_LINK_DP_FIXED_VS_PE_RETIMER_H__
#define __DC_LINK_DP_FIXED_VS_PE_RETIMER_H__
#include "link_dp_training.h"
enum link_training_result dp_perform_fixed_vs_pe_training_sequence(
struct dc_link *link,
const struct link_resource *link_res,
struct link_training_settings *lt_settings);
void dp_fixed_vs_pe_set_retimer_lane_settings(
struct dc_link *link,
const union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX],
uint8_t lane_count);
void dp_fixed_vs_pe_read_lane_adjust(
struct dc_link *link,
union dpcd_training_lane dpcd_lane_adjust[LANE_COUNT_DP_MAX]);
#endif /* __DC_LINK_DP_FIXED_VS_PE_RETIMER_H__ */
@@ -0,0 +1,251 @@
/*
* Copyright 2021 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 basic dpcd read/write functionality. It also does basic
* dpcd range check to ensure that every dpcd request is compliant with specs
* range requirements.
*/
#include "link_dpcd.h"
#include <drm/display/drm_dp_helper.h>
#include "dm_helpers.h"
#define END_ADDRESS(start, size) (start + size - 1)
#define ADDRESS_RANGE_SIZE(start, end) (end - start + 1)
struct dpcd_address_range {
uint32_t start;
uint32_t end;
};
static enum dc_status internal_link_read_dpcd(
struct dc_link *link,
uint32_t address,
uint8_t *data,
uint32_t size)
{
if (!link->aux_access_disabled &&
!dm_helpers_dp_read_dpcd(link->ctx,
link, address, data, size)) {
return DC_ERROR_UNEXPECTED;
}
return DC_OK;
}
static enum dc_status internal_link_write_dpcd(
struct dc_link *link,
uint32_t address,
const uint8_t *data,
uint32_t size)
{
if (!link->aux_access_disabled &&
!dm_helpers_dp_write_dpcd(link->ctx,
link, address, data, size)) {
return DC_ERROR_UNEXPECTED;
}
return DC_OK;
}
/*
* Partition the entire DPCD address space
* XXX: This partitioning must cover the entire DPCD address space,
* and must contain no gaps or overlapping address ranges.
*/
static const struct dpcd_address_range mandatory_dpcd_partitions[] = {
{ 0, DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR1) - 1},
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR1), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR2) - 1 },
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR2), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR3) - 1 },
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR3), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR4) - 1 },
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR4), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR5) - 1 },
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR5), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR6) - 1 },
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR6), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR7) - 1 },
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR7), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR8) - 1 },
{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR8), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR1) - 1 },
/*
* The FEC registers are contiguous
*/
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR1), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR1) - 1 },
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR2), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR2) - 1 },
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR3), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR3) - 1 },
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR4), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR4) - 1 },
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR5), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR5) - 1 },
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR6), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR6) - 1 },
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR7), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR7) - 1 },
{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR8), DP_LTTPR_MAX_ADD },
/* all remaining DPCD addresses */
{ DP_LTTPR_MAX_ADD + 1, DP_DPCD_MAX_ADD } };
static inline bool do_addresses_intersect_with_range(
const struct dpcd_address_range *range,
const uint32_t start_address,
const uint32_t end_address)
{
return start_address <= range->end && end_address >= range->start;
}
static uint32_t dpcd_get_next_partition_size(const uint32_t address, const uint32_t size)
{
const uint32_t end_address = END_ADDRESS(address, size);
uint32_t partition_iterator = 0;
/*
* find current partition
* this loop spins forever if partition map above is not surjective
*/
while (!do_addresses_intersect_with_range(&mandatory_dpcd_partitions[partition_iterator],
address, end_address))
partition_iterator++;
if (end_address < mandatory_dpcd_partitions[partition_iterator].end)
return size;
return ADDRESS_RANGE_SIZE(address, mandatory_dpcd_partitions[partition_iterator].end);
}
/*
* Ranges of DPCD addresses that must be read in a single transaction
* XXX: Do not allow any two address ranges in this array to overlap
*/
static const struct dpcd_address_range mandatory_dpcd_blocks[] = {
{ DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV, DP_PHY_REPEATER_128B132B_RATES }};
/*
* extend addresses to read all mandatory blocks together
*/
static void dpcd_extend_address_range(
const uint32_t in_address,
uint8_t * const in_data,
const uint32_t in_size,
uint32_t *out_address,
uint8_t **out_data,
uint32_t *out_size)
{
const uint32_t end_address = END_ADDRESS(in_address, in_size);
const struct dpcd_address_range *addr_range;
struct dpcd_address_range new_addr_range;
uint32_t i;
new_addr_range.start = in_address;
new_addr_range.end = end_address;
for (i = 0; i < ARRAY_SIZE(mandatory_dpcd_blocks); i++) {
addr_range = &mandatory_dpcd_blocks[i];
if (addr_range->start <= in_address && addr_range->end >= in_address)
new_addr_range.start = addr_range->start;
if (addr_range->start <= end_address && addr_range->end >= end_address)
new_addr_range.end = addr_range->end;
}
*out_address = in_address;
*out_size = in_size;
*out_data = in_data;
if (new_addr_range.start != in_address || new_addr_range.end != end_address) {
*out_address = new_addr_range.start;
*out_size = ADDRESS_RANGE_SIZE(new_addr_range.start, new_addr_range.end);
*out_data = kcalloc(*out_size, sizeof(**out_data), GFP_KERNEL);
ASSERT(*out_data);
}
}
/*
* Reduce the AUX reply down to the values the caller requested
*/
static void dpcd_reduce_address_range(
const uint32_t extended_address,
uint8_t * const extended_data,
const uint32_t extended_size,
const uint32_t reduced_address,
uint8_t * const reduced_data,
const uint32_t reduced_size)
{
(void)extended_size;
const uint32_t offset = reduced_address - extended_address;
/*
* If the address is same, address was not extended.
* So we do not need to free any memory.
* The data is in original buffer(reduced_data).
*/
if (extended_data == reduced_data)
return;
memcpy(&extended_data[offset], reduced_data, reduced_size);
kfree(extended_data);
}
enum dc_status core_link_read_dpcd(
struct dc_link *link,
uint32_t address,
uint8_t *data,
uint32_t size)
{
uint32_t extended_address;
uint32_t partitioned_address;
uint8_t *extended_data;
uint32_t extended_size;
/* size of the remaining partitioned address space */
uint32_t size_left_to_read;
enum dc_status status = DC_ERROR_UNEXPECTED;
/* size of the next partition to be read from */
uint32_t partition_size;
uint32_t data_index = 0;
dpcd_extend_address_range(address, data, size, &extended_address, &extended_data, &extended_size);
partitioned_address = extended_address;
size_left_to_read = extended_size;
while (size_left_to_read) {
partition_size = dpcd_get_next_partition_size(partitioned_address, size_left_to_read);
status = internal_link_read_dpcd(link, partitioned_address, &extended_data[data_index], partition_size);
if (status != DC_OK)
break;
partitioned_address += partition_size;
data_index += partition_size;
size_left_to_read -= partition_size;
}
dpcd_reduce_address_range(extended_address, extended_data, extended_size, address, data, size);
return status;
}
enum dc_status core_link_write_dpcd(
struct dc_link *link,
uint32_t address,
const uint8_t *data,
uint32_t size)
{
uint32_t partition_size;
uint32_t data_index = 0;
enum dc_status status = DC_ERROR_UNEXPECTED;
while (size) {
partition_size = dpcd_get_next_partition_size(address, size);
status = internal_link_write_dpcd(link, address, &data[data_index], partition_size);
if (status != DC_OK)
break;
address += partition_size;
data_index += partition_size;
size -= partition_size;
}
return status;
}
@@ -0,0 +1,42 @@
/*
* Copyright 2021 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_DPCD_H__
#define __LINK_DPCD_H__
#include "link_service.h"
#include "dpcd_defs.h"
enum dc_status core_link_read_dpcd(
struct dc_link *link,
uint32_t address,
uint8_t *data,
uint32_t size);
enum dc_status core_link_write_dpcd(
struct dc_link *link,
uint32_t address,
const uint8_t *data,
uint32_t size);
#endif
@@ -0,0 +1,80 @@
/*
* 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 __DC_LINK_EDP_PANEL_CONTROL_H__
#define __DC_LINK_EDP_PANEL_CONTROL_H__
#include "link_service.h"
enum dp_panel_mode dp_get_panel_mode(struct dc_link *link);
void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode);
bool set_default_brightness_aux(struct dc_link *link);
bool is_smartmux_suported(struct dc_link *link);
void edp_panel_backlight_power_on(struct dc_link *link, bool wait_for_hpd);
int edp_get_backlight_level(const struct dc_link *link);
bool edp_get_backlight_level_nits(struct dc_link *link,
uint32_t *backlight_millinits_avg,
uint32_t *backlight_millinits_peak);
bool edp_set_backlight_level(const struct dc_link *link,
struct set_backlight_level_params *backlight_level_params);
bool edp_set_backlight_level_nits(struct dc_link *link,
bool isHDR,
uint32_t backlight_millinits,
uint32_t transition_time_in_ms);
int edp_get_target_backlight_pwm(const struct dc_link *link);
bool edp_get_psr_state(const struct dc_link *link, enum dc_psr_state *state);
bool edp_set_psr_allow_active(struct dc_link *link, const bool *allow_active,
bool wait, bool force_static, const unsigned int *power_opts);
bool edp_setup_psr(struct dc_link *link,
const struct dc_stream_state *stream, struct psr_config *psr_config,
struct psr_context *psr_context);
bool edp_set_sink_vtotal_in_psr_active(const struct dc_link *link,
uint16_t psr_vtotal_idle, uint16_t psr_vtotal_su);
void edp_get_psr_residency(const struct dc_link *link, uint32_t *residency, enum psr_residency_mode mode);
bool edp_set_replay_allow_active(struct dc_link *dc_link, const bool *enable,
bool wait, bool force_static, const unsigned int *power_opts);
bool edp_send_replay_cmd(struct dc_link *link,
enum replay_FW_Message_type msg,
union dmub_replay_cmd_set *cmd_data);
bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal, uint16_t frame_skip_number);
bool edp_replay_residency(const struct dc_link *link,
unsigned int *residency, const bool is_start, const enum pr_residency_mode mode);
bool edp_get_replay_state(const struct dc_link *link, uint64_t *state);
bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link,
const unsigned int *power_opts, uint32_t coasting_vtotal, uint16_t frame_skip_number);
bool edp_wait_for_t12(struct dc_link *link);
bool edp_is_ilr_optimization_required(struct dc_link *link,
struct dc_crtc_timing *crtc_timing);
bool edp_is_ilr_optimization_enabled(struct dc_link *link);
enum dc_link_rate get_max_edp_link_rate(struct dc_link *link);
bool edp_backlight_enable_aux(struct dc_link *link, bool enable);
void edp_add_delay_for_T9(struct dc_link *link);
bool edp_receiver_ready_T9(struct dc_link *link);
bool edp_receiver_ready_T7(struct dc_link *link);
bool edp_power_alpm_dpcd_enable(struct dc_link *link, bool enable);
bool edp_setup_freesync_replay(struct dc_link *link, const struct dc_stream_state *stream);
void edp_set_panel_power(struct dc_link *link, bool powerOn);
void edp_set_panel_assr(struct dc_link *link, struct pipe_ctx *pipe_ctx,
enum dp_panel_mode *panel_mode, bool enable);
#endif /* __DC_LINK_EDP_POWER_CONTROL_H__ */
File diff suppressed because it is too large Load Diff
@@ -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__ */
@@ -0,0 +1,177 @@
/*
* 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 functions that manage basic HPD components such as gpio.
* It also provides wrapper functions to execute HPD related programming. This
* file only manages basic HPD functionality. It doesn't manage detection or
* feature or signal specific HPD behaviors.
*/
#include "link_hpd.h"
#include "gpio_service_interface.h"
bool link_get_hpd_state(struct dc_link *link)
{
if (link->link_enc)
return link->link_enc->funcs->get_hpd_state(link->link_enc);
else
return false;
}
void link_enable_hpd(const struct dc_link *link)
{
if (link->link_enc)
link->link_enc->funcs->enable_hpd(link->link_enc);
}
void link_disable_hpd(const struct dc_link *link)
{
if (link->link_enc)
link->link_enc->funcs->disable_hpd(link->link_enc);
}
void link_enable_hpd_filter(struct dc_link *link, bool enable)
{
if (enable) {
link->is_hpd_filter_disabled = false;
program_hpd_filter(link);
} else {
link->is_hpd_filter_disabled = true;
if (link->link_enc)
link->link_enc->funcs->program_hpd_filter(link->link_enc, 0, 0);
}
}
bool program_hpd_filter(const struct dc_link *link)
{
int delay_on_connect_in_ms = 0;
int delay_on_disconnect_in_ms = 0;
if (link->is_hpd_filter_disabled || !link->link_enc) {
ASSERT(link->link_enc);
return false;
}
/* Verify feature is supported */
switch (link->connector_signal) {
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;
break;
case SIGNAL_TYPE_DISPLAY_PORT:
case SIGNAL_TYPE_DISPLAY_PORT_MST:
/* Program hpd filter to allow DP signal to settle */
/* 500: not able to detect MST <-> SST switch as HPD is low for
* only 100ms on DELL U2413
* 0: some passive dongle still show aux mode instead of i2c
* 20-50: not enough to hide bouncing HPD with passive dongle.
* also see intermittent i2c read issues.
*/
delay_on_connect_in_ms = 80;
delay_on_disconnect_in_ms = 0;
break;
case SIGNAL_TYPE_LVDS:
case SIGNAL_TYPE_EDP:
default:
/* Don't program hpd filter */
return false;
}
return link->link_enc->funcs->program_hpd_filter(link->link_enc, delay_on_connect_in_ms, delay_on_disconnect_in_ms);
}
struct gpio *link_get_hpd_gpio(struct dc_bios *dcb,
struct graphics_object_id link_id,
struct gpio_service *gpio_service)
{
enum bp_result bp_result;
struct graphics_object_hpd_info hpd_info;
struct gpio_pin_info pin_info;
if (dcb->funcs->get_hpd_info(dcb, link_id, &hpd_info) != BP_RESULT_OK)
return NULL;
bp_result = dcb->funcs->get_gpio_pin_info(dcb,
hpd_info.hpd_int_gpio_uid, &pin_info);
if (bp_result != BP_RESULT_OK) {
return NULL;
}
return dal_gpio_service_create_irq(gpio_service,
pin_info.offset,
pin_info.mask);
}
enum hpd_source_id get_hpd_line(struct dc_link *link)
{
struct gpio *hpd;
enum hpd_source_id hpd_id;
hpd_id = HPD_SOURCEID_UNKNOWN;
/* Use GPIO path where supported, otherwise use hardware encoder path */
if (link->ctx && link->ctx->dce_version <= DCN_VERSION_4_01) {
hpd = link_get_hpd_gpio(link->ctx->dc_bios, link->link_id,
link->ctx->gpio_service);
} else {
hpd = NULL;
}
if (hpd) {
switch (dal_irq_get_source(hpd)) {
case DC_IRQ_SOURCE_HPD1:
hpd_id = HPD_SOURCEID1;
break;
case DC_IRQ_SOURCE_HPD2:
hpd_id = HPD_SOURCEID2;
break;
case DC_IRQ_SOURCE_HPD3:
hpd_id = HPD_SOURCEID3;
break;
case DC_IRQ_SOURCE_HPD4:
hpd_id = HPD_SOURCEID4;
break;
case DC_IRQ_SOURCE_HPD5:
hpd_id = HPD_SOURCEID5;
break;
case DC_IRQ_SOURCE_HPD6:
hpd_id = HPD_SOURCEID6;
break;
default:
BREAK_TO_DEBUGGER();
break;
}
dal_gpio_destroy_irq(&hpd);
}
return hpd_id;
}
@@ -0,0 +1,53 @@
/*
* 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 __DC_LINK_HPD_H__
#define __DC_LINK_HPD_H__
#include "link_service.h"
enum hpd_source_id get_hpd_line(struct dc_link *link);
/*
* Function: program_hpd_filter
*
* @brief
* Programs HPD filter on associated HPD line to default values.
*
* @return
* true on success, false otherwise
*/
bool program_hpd_filter(const struct dc_link *link);
/* Query hot plug status of USB4 DP tunnel.
* Returns true if HPD high.
*/
bool dpia_query_hpd_status(struct dc_link *link);
bool link_get_hpd_state(struct dc_link *link);
struct gpio *link_get_hpd_gpio(struct dc_bios *dcb,
struct graphics_object_id link_id,
struct gpio_service *gpio_service);
void link_enable_hpd(const struct dc_link *link);
void link_disable_hpd(const struct dc_link *link);
void link_enable_hpd_filter(struct dc_link *link, bool enable);
#endif /* __DC_LINK_HPD_H__ */