drm/amd/display: move dp irq handler functions from dc_link_dp to link_dp_irq_handler
authorWenjing Liu <wenjing.liu@amd.com>
Mon, 9 Jan 2023 19:38:35 +0000 (14:38 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 24 Jan 2023 18:26:25 +0000 (13:26 -0500)
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>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
drivers/gpu/drm/amd/display/dc/dc_link.h
drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h
drivers/gpu/drm/amd/display/dc/link/Makefile
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c [new file with mode: 0644]
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.h [new file with mode: 0644]
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c
drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.h

index 2e7ae42..20c719b 100644 (file)
@@ -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);
index d22d34c..21cc519 100644 (file)
 #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];
index 19d15b7..de0a1db 100644 (file)
@@ -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(
index b7bd272..10bef15 100644 (file)
@@ -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);
index 4198f66..3ecb72b 100644 (file)
@@ -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))
index ba53437..8a6fabe 100644 (file)
@@ -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)++;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
new file mode 100644 (file)
index 0000000..9a832a9
--- /dev/null
@@ -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;
+}
+
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.h
new file mode 100644 (file)
index 0000000..cac7acd
--- /dev/null
@@ -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__ */
index fefefd7..2a5f61f 100644 (file)
@@ -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];
index 376d370..a049486 100644 (file)
@@ -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__ */