mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 09:04:39 +02:00
drm/amd/display: move dp irq handler functions from dc_link_dp to link_dp_irq_handler
Create new files link_dp_irq_handler.c and link_dp_irq_handler.h, and move DP irq handler functions into them. Reviewed-by: George Shen <George.Shen@amd.com> Acked-by: Alan Liu <HaoPing.Liu@amd.com> Signed-off-by: Wenjing Liu <wenjing.liu@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
e95afc1cf7
commit
c5a31f178e
|
|
@ -1300,7 +1300,7 @@ static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
|
|||
sizeof(test_response));
|
||||
}
|
||||
else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) &&
|
||||
hpd_rx_irq_check_link_loss_status(dc_link, &offload_work->data) &&
|
||||
dc_link_check_link_loss_status(dc_link, &offload_work->data) &&
|
||||
dc_link_dp_allow_hpd_rx_irq(dc_link)) {
|
||||
dc_link_dp_handle_link_loss(dc_link);
|
||||
spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
|
||||
|
|
|
|||
|
|
@ -53,135 +53,6 @@
|
|||
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
|
||||
#include "link/protocols/link_dpcd.h"
|
||||
|
||||
static uint8_t get_nibble_at_index(const uint8_t *buf,
|
||||
uint32_t index)
|
||||
{
|
||||
uint8_t nibble;
|
||||
nibble = buf[index / 2];
|
||||
|
||||
if (index % 2)
|
||||
nibble >>= 4;
|
||||
else
|
||||
nibble &= 0x0F;
|
||||
|
||||
return nibble;
|
||||
}
|
||||
|
||||
enum dc_status 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,
|
||||
sizeof(union hpd_irq_data));
|
||||
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];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool hpd_rx_irq_check_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 < link->cur_link_settings.lane_count; lane++) {
|
||||
/* check status of lanes 0,1
|
||||
* changed DpcdAddress_Lane01Status (0x202)
|
||||
*/
|
||||
lane_status.raw = 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 (sink_status_changed ||
|
||||
!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool dp_validate_mode_timing(
|
||||
struct dc_link *link,
|
||||
const struct dc_crtc_timing *timing)
|
||||
|
|
@ -233,88 +104,6 @@ bool dp_validate_mode_timing(
|
|||
return false;
|
||||
}
|
||||
|
||||
/*************************Short Pulse IRQ***************************/
|
||||
bool dc_link_dp_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
|
||||
*/
|
||||
|
||||
if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
|
||||
is_dp_branch_device(link))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_hpd_irq_psr_sink(struct dc_link *link)
|
||||
{
|
||||
union dpcd_psr_configuration psr_configuration;
|
||||
|
||||
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;
|
||||
dc_link_set_psr_allow_active(link, &allow_active, true, false, NULL);
|
||||
allow_active = true;
|
||||
dc_link_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 enum dc_link_rate get_link_rate_from_test_link_rate(uint8_t test_rate)
|
||||
{
|
||||
switch (test_rate) {
|
||||
|
|
@ -533,7 +322,7 @@ static void dp_test_send_phy_test_pattern(struct dc_link *link)
|
|||
(unsigned int)(link->cur_link_settings.lane_count);
|
||||
lane++) {
|
||||
dpcd_lane_adjust.raw =
|
||||
get_nibble_at_index(&dpcd_lane_adjustment[0].raw, lane);
|
||||
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 =
|
||||
|
|
@ -725,171 +514,6 @@ void dc_link_dp_handle_automated_test(struct dc_link *link)
|
|||
sizeof(test_response));
|
||||
}
|
||||
|
||||
void dc_link_dp_handle_link_loss(struct dc_link *link)
|
||||
{
|
||||
int i;
|
||||
struct pipe_ctx *pipe_ctx;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pipe_ctx == NULL || pipe_ctx->stream == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off &&
|
||||
pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe)
|
||||
core_link_disable_stream(pipe_ctx);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off
|
||||
&& pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe) {
|
||||
// Always use max settings here for DP 1.4a LL Compliance CTS
|
||||
if (link->is_automated) {
|
||||
pipe_ctx->link_config.dp_link_settings.lane_count =
|
||||
link->verified_link_cap.lane_count;
|
||||
pipe_ctx->link_config.dp_link_settings.link_rate =
|
||||
link->verified_link_cap.link_rate;
|
||||
pipe_ctx->link_config.dp_link_settings.link_spread =
|
||||
link->verified_link_cap.link_spread;
|
||||
}
|
||||
core_link_enable_stream(link->dc->current_state, pipe_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool dc_link_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;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
/* 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 = 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
|
||||
link->is_automated = 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 (!dc_link_dp_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;
|
||||
}
|
||||
|
||||
/* For now we only handle 'Downstream port status' case.
|
||||
* If we got sink count changed it means
|
||||
* Downstream port status changed,
|
||||
* then DM should call DC to do the detection.
|
||||
* NOTE: Do not handle link loss on eDP since it is internal link*/
|
||||
if ((link->connector_signal != SIGNAL_TYPE_EDP) &&
|
||||
hpd_rx_irq_check_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
|
||||
dc_link_dp_handle_link_loss(link);
|
||||
|
||||
status = false;
|
||||
if (out_link_loss)
|
||||
*out_link_loss = true;
|
||||
|
||||
dp_trace_link_loss_increment(link);
|
||||
}
|
||||
|
||||
if (link->type == dc_connection_sst_branch &&
|
||||
hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
|
||||
!= link->dpcd_sink_count)
|
||||
status = true;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static bool is_dp_phy_pattern(enum dp_test_pattern test_pattern)
|
||||
{
|
||||
if ((DP_TEST_PATTERN_PHY_PATTERN_BEGIN <= test_pattern &&
|
||||
|
|
@ -1469,7 +1093,7 @@ static void get_lane_status(
|
|||
sizeof(dpcd_buf));
|
||||
|
||||
for (lane = 0; lane < lane_count; lane++) {
|
||||
status[lane].raw = get_nibble_at_index(&dpcd_buf[0], lane);
|
||||
status[lane].raw = dp_get_nibble_at_index(&dpcd_buf[0], lane);
|
||||
}
|
||||
|
||||
status_updated->raw = dpcd_buf[2];
|
||||
|
|
|
|||
|
|
@ -431,7 +431,8 @@ bool dc_link_wait_for_t12(struct dc_link *link);
|
|||
void dc_link_dp_handle_automated_test(struct dc_link *link);
|
||||
void dc_link_dp_handle_link_loss(struct dc_link *link);
|
||||
bool dc_link_dp_allow_hpd_rx_irq(const struct dc_link *link);
|
||||
|
||||
bool dc_link_check_link_loss_status(struct dc_link *link,
|
||||
union hpd_irq_data *hpd_irq_dpcd_data);
|
||||
struct dc_sink_init_data;
|
||||
|
||||
struct dc_sink *dc_link_add_remote_sink(
|
||||
|
|
|
|||
|
|
@ -51,12 +51,6 @@ bool dp_validate_mode_timing(
|
|||
struct dc_link *link,
|
||||
const struct dc_crtc_timing *timing);
|
||||
|
||||
bool hpd_rx_irq_check_link_loss_status(struct dc_link *link,
|
||||
union hpd_irq_data *hpd_irq_dpcd_data);
|
||||
enum dc_status read_hpd_rx_irq_data(
|
||||
struct dc_link *link,
|
||||
union hpd_irq_data *irq_data);
|
||||
|
||||
void dp_enable_mst_on_sink(struct dc_link *link, bool enable);
|
||||
|
||||
enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource *link_res, bool ready);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ 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_edp_panel_control.o link_dp_irq_handler.o
|
||||
|
||||
AMD_DAL_LINK_PROTOCOLS = $(addprefix $(AMDDALPATH)/dc/link/protocols/, \
|
||||
$(LINK_PROTOCOLS))
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "link_dp_dpia.h"
|
||||
#include "link_dp_phy.h"
|
||||
#include "link_edp_panel_control.h"
|
||||
#include "link_dp_irq_handler.h"
|
||||
#include "link/accessories/link_dp_trace.h"
|
||||
#include "link_dp_training.h"
|
||||
#include "atomfirmware.h"
|
||||
|
|
@ -2127,8 +2128,8 @@ static bool dp_verify_link_cap(
|
|||
if (status == LINK_TRAINING_SUCCESS) {
|
||||
success = true;
|
||||
udelay(1000);
|
||||
if (read_hpd_rx_irq_data(link, &irq_data) == DC_OK &&
|
||||
hpd_rx_irq_check_link_loss_status(
|
||||
if (dp_read_hpd_rx_irq_data(link, &irq_data) == DC_OK &&
|
||||
dc_link_check_link_loss_status(
|
||||
link,
|
||||
&irq_data))
|
||||
(*fail_count)++;
|
||||
|
|
|
|||
|
|
@ -104,4 +104,3 @@ bool dc_link_dpia_query_hpd_status(struct dc_link *link)
|
|||
|
||||
return is_hpd_high;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* 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_irq_handler.h"
|
||||
#include "link_dpcd.h"
|
||||
#include "link_dp_training.h"
|
||||
#include "link_dp_capability.h"
|
||||
#include "link/accessories/link_dp_trace.h"
|
||||
#include "dm_helpers.h"
|
||||
|
||||
#define DC_LOGGER_INIT(logger)
|
||||
|
||||
bool dc_link_check_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 < 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 (sink_status_changed ||
|
||||
!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) {
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
dc_link_set_psr_allow_active(link, &allow_active, true, false, NULL);
|
||||
allow_active = true;
|
||||
dc_link_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;
|
||||
}
|
||||
|
||||
void dc_link_dp_handle_link_loss(struct dc_link *link)
|
||||
{
|
||||
int i;
|
||||
struct pipe_ctx *pipe_ctx;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pipe_ctx == NULL || pipe_ctx->stream == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off &&
|
||||
pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe)
|
||||
core_link_disable_stream(pipe_ctx);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
|
||||
if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off
|
||||
&& pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe) {
|
||||
// Always use max settings here for DP 1.4a LL Compliance CTS
|
||||
if (link->is_automated) {
|
||||
pipe_ctx->link_config.dp_link_settings.lane_count =
|
||||
link->verified_link_cap.lane_count;
|
||||
pipe_ctx->link_config.dp_link_settings.link_rate =
|
||||
link->verified_link_cap.link_rate;
|
||||
pipe_ctx->link_config.dp_link_settings.link_spread =
|
||||
link->verified_link_cap.link_spread;
|
||||
}
|
||||
core_link_enable_stream(link->dc->current_state, pipe_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
sizeof(union hpd_irq_data));
|
||||
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];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*************************Short Pulse IRQ***************************/
|
||||
bool dc_link_dp_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
|
||||
*/
|
||||
|
||||
if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
|
||||
is_dp_branch_device(link))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dc_link_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;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
/* 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
|
||||
link->is_automated = 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 (!dc_link_dp_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;
|
||||
}
|
||||
|
||||
/* For now we only handle 'Downstream port status' case.
|
||||
* If we got sink count changed it means
|
||||
* Downstream port status changed,
|
||||
* then DM should call DC to do the detection.
|
||||
* NOTE: Do not handle link loss on eDP since it is internal link*/
|
||||
if ((link->connector_signal != SIGNAL_TYPE_EDP) &&
|
||||
dc_link_check_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
|
||||
dc_link_dp_handle_link_loss(link);
|
||||
|
||||
status = false;
|
||||
if (out_link_loss)
|
||||
*out_link_loss = true;
|
||||
|
||||
dp_trace_link_loss_increment(link);
|
||||
}
|
||||
|
||||
if (link->type == dc_connection_sst_branch &&
|
||||
hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
|
||||
!= link->dpcd_sink_count)
|
||||
status = true;
|
||||
|
||||
/* 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,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_IRQ_HANDLER_H__
|
||||
#define __DC_LINK_DP_IRQ_HANDLER_H__
|
||||
|
||||
#include "link.h"
|
||||
|
||||
enum dc_status dp_read_hpd_rx_irq_data(
|
||||
struct dc_link *link,
|
||||
union hpd_irq_data *irq_data);
|
||||
|
||||
#endif /* __DC_LINK_DP_IRQ_HANDLER_H__ */
|
||||
|
|
@ -240,7 +240,7 @@ enum dpcd_training_patterns
|
|||
return dpcd_tr_pattern;
|
||||
}
|
||||
|
||||
static uint8_t get_nibble_at_index(const uint8_t *buf,
|
||||
uint8_t dp_get_nibble_at_index(const uint8_t *buf,
|
||||
uint32_t index)
|
||||
{
|
||||
uint8_t nibble;
|
||||
|
|
@ -520,7 +520,7 @@ enum link_training_result dp_check_link_loss_status(
|
|||
/*
|
||||
* check lanes status
|
||||
*/
|
||||
lane_status.raw = get_nibble_at_index(&dpcd_buf[2], lane);
|
||||
lane_status.raw = dp_get_nibble_at_index(&dpcd_buf[2], lane);
|
||||
|
||||
if (!lane_status.bits.CHANNEL_EQ_DONE_0 ||
|
||||
!lane_status.bits.CR_DONE_0 ||
|
||||
|
|
@ -579,9 +579,9 @@ enum dc_status dp_get_lane_status_and_lane_adjust(
|
|||
lane++) {
|
||||
|
||||
ln_status[lane].raw =
|
||||
get_nibble_at_index(&dpcd_buf[0], lane);
|
||||
dp_get_nibble_at_index(&dpcd_buf[0], lane);
|
||||
ln_adjust[lane].raw =
|
||||
get_nibble_at_index(&dpcd_buf[lane_adjust_offset], lane);
|
||||
dp_get_nibble_at_index(&dpcd_buf[lane_adjust_offset], lane);
|
||||
}
|
||||
|
||||
ln_align->raw = dpcd_buf[2];
|
||||
|
|
|
|||
|
|
@ -176,4 +176,7 @@ void dp_log_training_result(
|
|||
|
||||
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);
|
||||
#endif /* __DC_LINK_DP_TRAINING_H__ */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user