2 * Copyright 2022 Advanced Micro Devices, Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
26 /* FILE POLICY AND INTENDED USAGE:
27 * This file implements DP HPD short pulse handling sequence according to DP
32 #include "link_dp_irq_handler.h"
33 #include "link_dpcd.h"
34 #include "link_dp_training.h"
35 #include "link_dp_capability.h"
36 #include "link/accessories/link_dp_trace.h"
37 #include "dm_helpers.h"
39 #define DC_LOGGER_INIT(logger)
41 bool dc_link_check_link_loss_status(
43 union hpd_irq_data *hpd_irq_dpcd_data)
45 uint8_t irq_reg_rx_power_state = 0;
46 enum dc_status dpcd_result = DC_ERROR_UNEXPECTED;
47 union lane_status lane_status;
49 bool sink_status_changed;
52 sink_status_changed = false;
55 if (link->cur_link_settings.lane_count == 0)
58 /*1. Check that Link Status changed, before re-training.*/
61 for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) {
62 /* check status of lanes 0,1
63 * changed DpcdAddress_Lane01Status (0x202)
65 lane_status.raw = dp_get_nibble_at_index(
66 &hpd_irq_dpcd_data->bytes.lane01_status.raw,
69 if (!lane_status.bits.CHANNEL_EQ_DONE_0 ||
70 !lane_status.bits.CR_DONE_0 ||
71 !lane_status.bits.SYMBOL_LOCKED_0) {
72 /* if one of the channel equalization, clock
73 * recovery or symbol lock is dropped
74 * consider it as (link has been
75 * dropped) dp sink status has changed
77 sink_status_changed = true;
82 /* Check interlane align.*/
83 if (sink_status_changed ||
84 !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) {
86 DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__);
90 /*2. Check that we can handle interrupt: Not in FS DOS,
91 * Not in "Display Timeout" state, Link is trained.
93 dpcd_result = core_link_read_dpcd(link,
95 &irq_reg_rx_power_state,
96 sizeof(irq_reg_rx_power_state));
98 if (dpcd_result != DC_OK) {
99 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n",
102 if (irq_reg_rx_power_state != DP_SET_POWER_D0)
110 static bool handle_hpd_irq_psr_sink(struct dc_link *link)
112 union dpcd_psr_configuration psr_configuration;
114 if (!link->psr_settings.psr_feature_enabled)
117 dm_helpers_dp_read_dpcd(
120 368,/*DpcdAddress_PSR_Enable_Cfg*/
121 &psr_configuration.raw,
122 sizeof(psr_configuration.raw));
124 if (psr_configuration.bits.ENABLE) {
125 unsigned char dpcdbuf[3] = {0};
126 union psr_error_status psr_error_status;
127 union psr_sink_psr_status psr_sink_psr_status;
129 dm_helpers_dp_read_dpcd(
132 0x2006, /*DpcdAddress_PSR_Error_Status*/
133 (unsigned char *) dpcdbuf,
136 /*DPCD 2006h ERROR STATUS*/
137 psr_error_status.raw = dpcdbuf[0];
138 /*DPCD 2008h SINK PANEL SELF REFRESH STATUS*/
139 psr_sink_psr_status.raw = dpcdbuf[2];
141 if (psr_error_status.bits.LINK_CRC_ERROR ||
142 psr_error_status.bits.RFB_STORAGE_ERROR ||
143 psr_error_status.bits.VSC_SDP_ERROR) {
146 /* Acknowledge and clear error bits */
147 dm_helpers_dp_write_dpcd(
150 8198,/*DpcdAddress_PSR_Error_Status*/
151 &psr_error_status.raw,
152 sizeof(psr_error_status.raw));
154 /* PSR error, disable and re-enable PSR */
155 if (link->psr_settings.psr_allow_active) {
156 allow_active = false;
157 dc_link_set_psr_allow_active(link, &allow_active, true, false, NULL);
159 dc_link_set_psr_allow_active(link, &allow_active, true, false, NULL);
163 } else if (psr_sink_psr_status.bits.SINK_SELF_REFRESH_STATUS ==
164 PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB){
165 /* No error is detect, PSR is active.
166 * We should return with IRQ_HPD handled without
167 * checking for loss of sync since PSR would have
168 * powered down main link.
176 void dc_link_dp_handle_link_loss(struct dc_link *link)
179 struct pipe_ctx *pipe_ctx;
181 for (i = 0; i < MAX_PIPES; i++) {
182 pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
183 if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
187 if (pipe_ctx == NULL || pipe_ctx->stream == NULL)
190 for (i = 0; i < MAX_PIPES; i++) {
191 pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
192 if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off &&
193 pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe)
194 link_set_dpms_off(pipe_ctx);
197 for (i = 0; i < MAX_PIPES; i++) {
198 pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
199 if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off
200 && pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe) {
201 // Always use max settings here for DP 1.4a LL Compliance CTS
202 if (link->is_automated) {
203 pipe_ctx->link_config.dp_link_settings.lane_count =
204 link->verified_link_cap.lane_count;
205 pipe_ctx->link_config.dp_link_settings.link_rate =
206 link->verified_link_cap.link_rate;
207 pipe_ctx->link_config.dp_link_settings.link_spread =
208 link->verified_link_cap.link_spread;
210 link_set_dpms_on(link->dc->current_state, pipe_ctx);
215 enum dc_status dc_link_dp_read_hpd_rx_irq_data(
216 struct dc_link *link,
217 union hpd_irq_data *irq_data)
219 static enum dc_status retval;
221 /* The HW reads 16 bytes from 200h on HPD,
222 * but if we get an AUX_DEFER, the HW cannot retry
223 * and this causes the CTS tests 4.3.2.1 - 3.2.4 to
224 * fail, so we now explicitly read 6 bytes which is
225 * the req from the above mentioned test cases.
227 * For DP 1.4 we need to read those from 2002h range.
229 if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14)
230 retval = core_link_read_dpcd(
234 sizeof(union hpd_irq_data));
236 /* Read 14 bytes in a single read and then copy only the required fields.
237 * This is more efficient than doing it in two separate AUX reads. */
239 uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1];
241 retval = core_link_read_dpcd(
250 irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI];
251 irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI];
252 irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI];
253 irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI];
254 irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI];
255 irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI];
261 /*************************Short Pulse IRQ***************************/
262 bool dc_link_dp_allow_hpd_rx_irq(const struct dc_link *link)
265 * Don't handle RX IRQ unless one of following is met:
266 * 1) The link is established (cur_link_settings != unknown)
267 * 2) We know we're dealing with a branch device, SST or MST
270 if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
271 is_dp_branch_device(link))
277 bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss,
278 bool defer_handling, bool *has_left_work)
280 union hpd_irq_data hpd_irq_dpcd_data = {0};
281 union device_service_irq device_service_clear = {0};
282 enum dc_status result;
286 *out_link_loss = false;
289 *has_left_work = false;
290 /* For use cases related to down stream connection status change,
291 * PSR and device auto test, refer to function handle_sst_hpd_irq
294 DC_LOG_HW_HPD_IRQ("%s: Got short pulse HPD on link %d\n",
295 __func__, link->link_index);
298 /* All the "handle_hpd_irq_xxx()" methods
299 * should be called only after
300 * dal_dpsst_ls_read_hpd_irq_data
301 * Order of calls is important too
303 result = dc_link_dp_read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data);
304 if (out_hpd_irq_dpcd_data)
305 *out_hpd_irq_dpcd_data = hpd_irq_dpcd_data;
307 if (result != DC_OK) {
308 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain irq data\n",
313 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
314 // Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC
315 link->is_automated = true;
316 device_service_clear.bits.AUTOMATED_TEST = 1;
317 core_link_write_dpcd(
319 DP_DEVICE_SERVICE_IRQ_VECTOR,
320 &device_service_clear.raw,
321 sizeof(device_service_clear.raw));
322 device_service_clear.raw = 0;
323 if (defer_handling && has_left_work)
324 *has_left_work = true;
326 dc_link_dp_handle_automated_test(link);
330 if (!dc_link_dp_allow_hpd_rx_irq(link)) {
331 DC_LOG_HW_HPD_IRQ("%s: skipping HPD handling on %d\n",
332 __func__, link->link_index);
336 if (handle_hpd_irq_psr_sink(link))
337 /* PSR-related error was detected and handled */
340 /* If PSR-related error handled, Main link may be off,
341 * so do not handle as a normal sink status change interrupt.
344 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) {
345 if (defer_handling && has_left_work)
346 *has_left_work = true;
350 /* check if we have MST msg and return since we poll for it */
351 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
352 if (defer_handling && has_left_work)
353 *has_left_work = true;
357 /* For now we only handle 'Downstream port status' case.
358 * If we got sink count changed it means
359 * Downstream port status changed,
360 * then DM should call DC to do the detection.
361 * NOTE: Do not handle link loss on eDP since it is internal link*/
362 if ((link->connector_signal != SIGNAL_TYPE_EDP) &&
363 dc_link_check_link_loss_status(
365 &hpd_irq_dpcd_data)) {
366 /* Connectivity log: link loss */
367 CONN_DATA_LINK_LOSS(link,
368 hpd_irq_dpcd_data.raw,
369 sizeof(hpd_irq_dpcd_data),
372 if (defer_handling && has_left_work)
373 *has_left_work = true;
375 dc_link_dp_handle_link_loss(link);
379 *out_link_loss = true;
381 dp_trace_link_loss_increment(link);
384 if (link->type == dc_connection_sst_branch &&
385 hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
386 != link->dpcd_sink_count)
389 /* reasons for HPD RX:
390 * 1. Link Loss - ie Re-train the Link
391 * 2. MST sideband message
392 * 3. Automated Test - ie. Internal Commit
393 * 4. CP (copy protection) - (not interesting for DM???)
395 * 6. Downstream Port status changed
396 * -ie. Detect - this the only one
397 * which is interesting for DM because
398 * it must call dc_link_detect.