#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"
#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:
return LINK_RATE_UHBR13_5;
default:
return LINK_RATE_UNKNOWN;
}
}
static bool is_dp_phy_sqaure_pattern(enum dp_test_pattern test_pattern)
{
return (DP_TEST_PATTERN_SQUARE_BEGIN <= test_pattern &&
test_pattern <= DP_TEST_PATTERN_SQUARE_END);
}
static bool is_dp_phy_pattern(enum dp_test_pattern test_pattern)
{
if ((DP_TEST_PATTERN_PHY_PATTERN_BEGIN <= test_pattern &&
test_pattern <= DP_TEST_PATTERN_PHY_PATTERN_END) ||
test_pattern == DP_TEST_PATTERN_VIDEO_MODE)
return true;
else
return false;
}
static void dp_retrain_link_dp_test(struct dc_link *link,
struct dc_link_settings *link_setting,
bool skip_video_pattern)
{
struct pipe_ctx *pipes[MAX_PIPES];
struct dc_state *state = link->dc->current_state;
uint8_t count;
int i;
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]);
}
for (i = count-1; i >= 0; i--)
link_set_dpms_on(state, pipes[i]);
}
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);
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;
unsigned int sampling_rate_in_hz = 0;
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);
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;
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]));
}
}
switch (dpcd_test_mode.bits.sampling_rate) {
case AUDIO_SAMPLING_RATE_32KHZ:
sampling_rate_in_hz = 32000;
break;
case AUDIO_SAMPLING_RATE_44_1KHZ:
sampling_rate_in_hz = 44100;
break;
case AUDIO_SAMPLING_RATE_48KHZ:
sampling_rate_in_hz = 48000;
break;
case AUDIO_SAMPLING_RATE_88_2KHZ:
sampling_rate_in_hz = 88200;
break;
case AUDIO_SAMPLING_RATE_96KHZ:
sampling_rate_in_hz = 96000;
break;
case AUDIO_SAMPLING_RATE_176_4KHZ:
sampling_rate_in_hz = 176400;
break;
case AUDIO_SAMPLING_RATE_192KHZ:
sampling_rate_in_hz = 192000;
break;
default:
sampling_rate_in_hz = 0;
break;
}
link->audio_test_data.flags.test_requested = 1;
link->audio_test_data.flags.disable_video = disable_video;
link->audio_test_data.sampling_rate = sampling_rate_in_hz;
link->audio_test_data.channel_count = channel_count;
link->audio_test_data.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;
}
}
}
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));
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));
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 & 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);
core_link_read_dpcd(
link,
DP_ADJUST_REQUEST_POST_CURSOR2,
&dpcd_post_cursor_2_adjustment,
sizeof(dpcd_post_cursor_2_adjustment));
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:
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:
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_sqaure_pattern(test_pattern)) {
test_pattern_size = 1;
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);
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)
{
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;
int odm_cnt = 1;
int h_active = pipe_ctx->stream->timing.h_addressable +
pipe_ctx->stream->timing.h_border_left +
pipe_ctx->stream->timing.h_border_right;
int v_active = pipe_ctx->stream->timing.v_addressable +
pipe_ctx->stream->timing.v_border_bottom +
pipe_ctx->stream->timing.v_border_top;
int odm_slice_width, last_odm_slice_width, offset = 0;
memset(¶ms, 0, sizeof(params));
for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
odm_cnt++;
odm_slice_width = h_active / odm_cnt;
last_odm_slice_width = h_active - odm_slice_width * (odm_cnt - 1);
switch (test_pattern) {
case DP_TEST_PATTERN_COLOR_SQUARES:
controller_test_pattern =
CONTROLLER_DP_TEST_PATTERN_COLORSQUARES;
break;
case DP_TEST_PATTERN_COLOR_SQUARES_CEA:
controller_test_pattern =
CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA;
break;
case DP_TEST_PATTERN_VERTICAL_BARS:
controller_test_pattern =
CONTROLLER_DP_TEST_PATTERN_VERTICALBARS;
break;
case DP_TEST_PATTERN_HORIZONTAL_BARS:
controller_test_pattern =
CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS;
break;
case DP_TEST_PATTERN_COLOR_RAMP:
controller_test_pattern =
CONTROLLER_DP_TEST_PATTERN_COLORRAMP;
break;
default:
controller_test_pattern =
CONTROLLER_DP_TEST_PATTERN_VIDEOMODE;
break;
}
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:
{
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, ¶ms);
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;
switch (test_pattern_color_space) {
case DP_TEST_PATTERN_COLOR_SPACE_RGB:
controller_color_space = CONTROLLER_DP_COLOR_SPACE_RGB;
break;
case DP_TEST_PATTERN_COLOR_SPACE_YCBCR601:
controller_color_space = CONTROLLER_DP_COLOR_SPACE_YCBCR601;
break;
case DP_TEST_PATTERN_COLOR_SPACE_YCBCR709:
controller_color_space = CONTROLLER_DP_COLOR_SPACE_YCBCR709;
break;
case DP_TEST_PATTERN_COLOR_SPACE_UNDEFINED:
default:
controller_color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
DC_LOG_ERROR("%s: Color space must be defined for test pattern", __func__);
ASSERT(0);
break;
}
odm_pipe = pipe_ctx;
while (odm_pipe->next_odm_pipe) {
odm_opp = odm_pipe->stream_res.opp;
odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
link->dc->hwss.set_disp_pattern_generator(link->dc,
odm_pipe,
controller_test_pattern,
controller_color_space,
color_depth,
NULL,
odm_slice_width,
v_active,
offset);
offset += odm_slice_width;
odm_pipe = odm_pipe->next_odm_pipe;
}
odm_opp = odm_pipe->stream_res.opp;
odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
link->dc->hwss.set_disp_pattern_generator(link->dc,
odm_pipe,
controller_test_pattern,
controller_color_space,
color_depth,
NULL,
last_odm_slice_width,
v_active,
offset);
}
}
break;
case DP_TEST_PATTERN_VIDEO_MODE:
{
resource_build_bit_depth_reduction_params(pipe_ctx->stream, ¶ms);
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, ¶ms);
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->next_odm_pipe) {
odm_opp = odm_pipe->stream_res.opp;
odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
link->dc->hwss.set_disp_pattern_generator(link->dc,
odm_pipe,
CONTROLLER_DP_TEST_PATTERN_VIDEOMODE,
CONTROLLER_DP_COLOR_SPACE_UDEFINED,
color_depth,
NULL,
odm_slice_width,
v_active,
offset);
offset += odm_slice_width;
odm_pipe = odm_pipe->next_odm_pipe;
}
odm_opp = odm_pipe->stream_res.opp;
odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
link->dc->hwss.set_disp_pattern_generator(link->dc,
odm_pipe,
CONTROLLER_DP_TEST_PATTERN_VIDEOMODE,
CONTROLLER_DP_COLOR_SPACE_UDEFINED,
color_depth,
NULL,
last_odm_slice_width,
v_active,
offset);
}
}
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) {
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);
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));
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;
}
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)
{
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;
if (link->test_pattern_enabled && test_pattern ==
DP_TEST_PATTERN_VIDEO_MODE) {
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);
link->dc->hwss.unblank_stream(
pipe_ctx,
&link->verified_link_cap);
link->test_pattern_enabled = false;
link->current_test_pattern = test_pattern;
return true;
}
if (is_dp_phy_pattern(test_pattern)) {
if (p_link_settings != NULL) {
if ((link->chip_caps & 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);
}
if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) {
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) {
link->test_pattern_enabled = true;
link->current_test_pattern = test_pattern;
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
)
return false;
if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) {
if (is_dp_phy_sqaure_pattern(test_pattern))
core_link_write_dpcd(link,
DP_LINK_SQUARE_PATTERN,
p_custom_pattern,
1);
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) {
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_res.tg->funcs->lock_doublebuffer_enable) {
if (pipe_ctx->stream && should_use_dmub_lock(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 = 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);
pipe_ctx->stream_res.stream_enc->funcs->dp_set_stream_attribute(pipe_ctx->stream_res.stream_enc,
&pipe_ctx->stream->timing,
color_space,
pipe_ctx->stream->use_vsc_sdp_for_colorimetry,
link->dpcd_caps.dprx_feature.bits.SST_SPLIT_SDP_CAP);
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);
else
pipe_ctx->stream->vsc_infopacket.sb[17] &= ~(1 << 7);
resource_build_info_frame(pipe_ctx);
link->dc->hwss.update_info_frame(pipe_ctx);
}
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 (pipe_ctx->stream && should_use_dmub_lock(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 = 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);
}
link->test_pattern_enabled = true;
link->current_test_pattern = test_pattern;
}
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;
struct dc_link_settings store_settings = *link_setting;
link->preferred_link_setting = store_settings;
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;
}
}
}
if (i == MAX_PIPES)
return;
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);
if (skip_immediate_retrain == false)
dp_set_preferred_link_settings(dc, &link->preferred_link_setting, link);
}