FILE
link_dp_irq_handler.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
1/* 2 * Copyright 2022 Advanced Micro Devices, Inc. 3 * 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: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 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. 21 * 22 * Authors: AMD 23 * 24 */ 25 26/* FILE POLICY AND INTENDED USAGE: 27 * This file implements DP HPD short pulse handling sequence according to DP 28 * specifications 29 * 30 */ 31 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_edp_panel_control.h" 37#include "link/accessories/link_dp_trace.h" 38#include "link/link_dpms.h" 39#include "dm_helpers.h" 40#include "link_dp_dpia_bw.h" 41 42#define DC_LOGGER \ 43 link->ctx->logger 44#define DC_LOGGER_INIT(logger) 45 46bool dp_parse_link_loss_status( 47 struct dc_link *link, 48 union hpd_irq_data *hpd_irq_dpcd_data) 49{ 50 uint8_t irq_reg_rx_power_state = 0; 51 enum dc_status dpcd_result = DC_ERROR_UNEXPECTED; 52 union lane_status lane_status; 53 uint32_t lane; 54 bool sink_status_changed; 55 bool return_code; 56 57 sink_status_changed = false; 58 return_code = false; 59 60 if (link->cur_link_settings.lane_count == 0) 61 return return_code; 62 63 /*1. Check that Link Status changed, before re-training.*/ 64 65 /*parse lane status*/ 66 for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) { 67 /* check status of lanes 0,1 68 * changed DpcdAddress_Lane01Status (0x202) 69 */ 70 lane_status.raw = dp_get_nibble_at_index( 71 &hpd_irq_dpcd_data->bytes.lane01_status.raw, 72 lane); 73 74 if (!lane_status.bits.CHANNEL_EQ_DONE_0 || 75 !lane_status.bits.CR_DONE_0 || 76 !lane_status.bits.SYMBOL_LOCKED_0) { 77 /* if one of the channel equalization, clock 78 * recovery or symbol lock is dropped 79 * consider it as (link has been 80 * dropped) dp sink status has changed 81 */ 82 sink_status_changed = true; 83 break; 84 } 85 } 86 87 /* Check interlane align.*/ 88 if (link_dp_get_encoding_format(&link->cur_link_settings) == DP_128b_132b_ENCODING && 89 (!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b || 90 !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b)) { 91 sink_status_changed = true; 92 } else if (!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { 93 sink_status_changed = true; 94 } 95 96 if (sink_status_changed) { 97 98 DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__); 99 100 return_code = true; 101 102 /*2. Check that we can handle interrupt: Not in FS DOS, 103 * Not in "Display Timeout" state, Link is trained. 104 */ 105 dpcd_result = core_link_read_dpcd(link, 106 DP_SET_POWER, 107 &irq_reg_rx_power_state, 108 sizeof(irq_reg_rx_power_state)); 109 110 if (dpcd_result != DC_OK) { 111 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n", 112 __func__); 113 } else { 114 if (irq_reg_rx_power_state != DP_SET_POWER_D0) 115 return_code = false; 116 } 117 } 118 119 return return_code; 120} 121 122static bool handle_hpd_irq_psr_sink(struct dc_link *link) 123{ 124 union dpcd_psr_configuration psr_configuration = {0}; 125 126 if (!link->psr_settings.psr_feature_enabled) 127 return false; 128 129 dm_helpers_dp_read_dpcd( 130 link->ctx, 131 link, 132 368,/*DpcdAddress_PSR_Enable_Cfg*/ 133 &psr_configuration.raw, 134 sizeof(psr_configuration.raw)); 135 136 if (psr_configuration.bits.ENABLE) { 137 unsigned char dpcdbuf[3] = {0}; 138 union psr_error_status psr_error_status; 139 union psr_sink_psr_status psr_sink_psr_status; 140 141 dm_helpers_dp_read_dpcd( 142 link->ctx, 143 link, 144 0x2006, /*DpcdAddress_PSR_Error_Status*/ 145 (unsigned char *) dpcdbuf, 146 sizeof(dpcdbuf)); 147 148 /*DPCD 2006h ERROR STATUS*/ 149 psr_error_status.raw = dpcdbuf[0]; 150 /*DPCD 2008h SINK PANEL SELF REFRESH STATUS*/ 151 psr_sink_psr_status.raw = dpcdbuf[2]; 152 153 if (psr_error_status.bits.LINK_CRC_ERROR || 154 psr_error_status.bits.RFB_STORAGE_ERROR || 155 psr_error_status.bits.VSC_SDP_ERROR) { 156 bool allow_active; 157 158 /* Acknowledge and clear error bits */ 159 dm_helpers_dp_write_dpcd( 160 link->ctx, 161 link, 162 8198,/*DpcdAddress_PSR_Error_Status*/ 163 &psr_error_status.raw, 164 sizeof(psr_error_status.raw)); 165 166 /* PSR error, disable and re-enable PSR */ 167 if (link->psr_settings.psr_allow_active) { 168 allow_active = false; 169 edp_set_psr_allow_active(link, &allow_active, true, false, NULL); 170 allow_active = true; 171 edp_set_psr_allow_active(link, &allow_active, true, false, NULL); 172 } 173 174 return true; 175 } else if (psr_sink_psr_status.bits.SINK_SELF_REFRESH_STATUS == 176 PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB){ 177 /* No error is detect, PSR is active. 178 * We should return with IRQ_HPD handled without 179 * checking for loss of sync since PSR would have 180 * powered down main link. 181 */ 182 return true; 183 } 184 } 185 return false; 186} 187 188static void handle_hpd_irq_replay_sink(struct dc_link *link) 189{ 190 union dpcd_replay_configuration replay_configuration = {0}; 191 /*AMD Replay version reuse DP_PSR_ERROR_STATUS for REPLAY_ERROR status.*/ 192 union psr_error_status replay_error_status = {0}; 193 bool ret = false; 194 int retries = 0; 195 196 if (!link->replay_settings.replay_feature_enabled) 197 return; 198 199 while (retries < 10) { 200 ret = dm_helpers_dp_read_dpcd( 201 link->ctx, 202 link, 203 DP_SINK_PR_REPLAY_STATUS, 204 &replay_configuration.raw, 205 sizeof(replay_configuration.raw)); 206 207 if (ret) 208 break; 209 210 retries++; 211 } 212 213 if (!ret) 214 DC_LOG_WARNING("[%s][%d] DPCD read addr.0x%x failed with %d retries\n", 215 __func__, __LINE__, 216 DP_SINK_PR_REPLAY_STATUS, retries); 217 218 dm_helpers_dp_read_dpcd( 219 link->ctx, 220 link, 221 DP_PSR_ERROR_STATUS, 222 &replay_error_status.raw, 223 sizeof(replay_error_status.raw)); 224 225 if (replay_error_status.bits.LINK_CRC_ERROR || 226 replay_configuration.bits.DESYNC_ERROR_STATUS || 227 replay_configuration.bits.STATE_TRANSITION_ERROR_STATUS) { 228 bool allow_active; 229 230 link->replay_settings.config.replay_error_status.raw |= replay_error_status.raw; 231 232 /* Increment desync error counter if a desync error is detected */ 233 if (replay_configuration.bits.DESYNC_ERROR_STATUS) 234 link->replay_settings.replay_desync_error_fail_count++; 235 236 if (link->replay_settings.config.force_disable_desync_error_check) 237 return; 238 239 /* Acknowledge and clear configuration bits */ 240 dm_helpers_dp_write_dpcd( 241 link->ctx, 242 link, 243 DP_SINK_PR_REPLAY_STATUS, 244 &replay_configuration.raw, 245 sizeof(replay_configuration.raw)); 246 247 /* Acknowledge and clear error bits */ 248 dm_helpers_dp_write_dpcd( 249 link->ctx, 250 link, 251 DP_PSR_ERROR_STATUS,/*DpcdAddress_REPLAY_Error_Status*/ 252 &replay_error_status.raw, 253 sizeof(replay_error_status.raw)); 254 255 /* Replay error, disable and re-enable Replay */ 256 if (link->replay_settings.replay_allow_active) { 257 allow_active = false; 258 edp_set_replay_allow_active(link, &allow_active, true, false, NULL); 259 allow_active = true; 260 edp_set_replay_allow_active(link, &allow_active, true, false, NULL); 261 } 262 } 263} 264 265void dp_handle_link_loss(struct dc_link *link) 266{ 267 struct pipe_ctx *pipes[MAX_PIPES]; 268 struct dc_state *state = link->dc->current_state; 269 uint8_t count; 270 int i; 271 272 link_get_master_pipes_with_dpms_on(link, state, &count, pipes); 273 274 for (i = 0; i < count; i++) 275 link_set_dpms_off(pipes[i]); 276 277 for (i = count - 1; i >= 0; i--) { 278 // Always use max settings here for DP 1.4a LL Compliance CTS 279 if (link->skip_fallback_on_link_loss) { 280 pipes[i]->link_config.dp_link_settings.lane_count = 281 link->verified_link_cap.lane_count; 282 pipes[i]->link_config.dp_link_settings.link_rate = 283 link->verified_link_cap.link_rate; 284 pipes[i]->link_config.dp_link_settings.link_spread = 285 link->verified_link_cap.link_spread; 286 } 287 link_set_dpms_on(link->dc->current_state, pipes[i]); 288 } 289} 290 291static void dp_handle_tunneling_irq(struct dc_link *link) 292{ 293 enum dc_status retval; 294 uint8_t tunneling_status = 0; 295 296 retval = core_link_read_dpcd( 297 link, DP_TUNNELING_STATUS, 298 &tunneling_status, 299 sizeof(tunneling_status)); 300 301 if (retval == DC_OK) { 302 DC_LOG_HW_HPD_IRQ("%s: Got DP tunneling status on link %d status=0x%x", 303 __func__, link->link_index, tunneling_status); 304 305 if (tunneling_status & DP_TUNNELING_BW_ALLOC_BITS_MASK) 306 link_dp_dpia_handle_bw_alloc_status(link, tunneling_status); 307 } 308 309 tunneling_status = DP_TUNNELING_IRQ; 310 core_link_write_dpcd( 311 link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, 312 &tunneling_status, 1); 313} 314 315static void read_dpcd204h_on_irq_hpd(struct dc_link *link, union hpd_irq_data *irq_data) 316{ 317 enum dc_status retval; 318 union lane_align_status_updated dpcd_lane_status_updated = {0}; 319 320 retval = core_link_read_dpcd( 321 link, 322 DP_LANE_ALIGN_STATUS_UPDATED, 323 &dpcd_lane_status_updated.raw, 324 sizeof(union lane_align_status_updated)); 325 326 if (retval == DC_OK) { 327 irq_data->bytes.lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b = 328 dpcd_lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b; 329 irq_data->bytes.lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b = 330 dpcd_lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b; 331 } 332} 333 334enum dc_status dp_read_hpd_rx_irq_data( 335 struct dc_link *link, 336 union hpd_irq_data *irq_data) 337{ 338 static enum dc_status retval; 339 340 /* The HW reads 16 bytes from 200h on HPD, 341 * but if we get an AUX_DEFER, the HW cannot retry 342 * and this causes the CTS tests 4.3.2.1 - 3.2.4 to 343 * fail, so we now explicitly read 6 bytes which is 344 * the req from the above mentioned test cases. 345 * 346 * For DP 1.4 we need to read those from 2002h range. 347 */ 348 if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) { 349 retval = core_link_read_dpcd( 350 link, 351 DP_SINK_COUNT, 352 irq_data->raw, 353 DP_SINK_STATUS - DP_SINK_COUNT + 1); 354 355 if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling) { 356 retval = core_link_read_dpcd( 357 link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, 358 &irq_data->bytes.link_service_irq_esi0.raw, 1); 359 } 360 } else { 361 /* Read 14 bytes in a single read and then copy only the required fields. 362 * This is more efficient than doing it in two separate AUX reads. */ 363 364 uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1] = {0}; 365 366 retval = core_link_read_dpcd( 367 link, 368 DP_SINK_COUNT_ESI, 369 tmp, 370 sizeof(tmp)); 371 372 if (retval != DC_OK) 373 return retval; 374 375 irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI]; 376 irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; 377 irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI]; 378 irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; 379 irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; 380 irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; 381 irq_data->bytes.link_service_irq_esi0.raw = tmp[DP_LINK_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; 382 383 /* 384 * This display doesn't have correct values in DPCD200Eh. 385 * Read and check DPCD204h instead. 386 */ 387 if (link->wa_flags.read_dpcd204h_on_irq_hpd) 388 read_dpcd204h_on_irq_hpd(link, irq_data); 389 } 390 391 return retval; 392} 393 394/*************************Short Pulse IRQ***************************/ 395bool dp_should_allow_hpd_rx_irq(const struct dc_link *link) 396{ 397 /* 398 * Don't handle RX IRQ unless one of following is met: 399 * 1) The link is established (cur_link_settings != unknown) 400 * 2) We know we're dealing with a branch device, SST or MST 401 * 3) The link is bw_alloc enabled. 402 */ 403 404 if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) || 405 is_dp_branch_device(link) || 406 link->dpia_bw_alloc_config.bw_alloc_enabled) 407 return true; 408 409 return false; 410} 411 412bool dp_handle_hpd_rx_irq(struct dc_link *link, 413 union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss, 414 bool defer_handling, bool *has_left_work) 415{ 416 union hpd_irq_data hpd_irq_dpcd_data = {0}; 417 union device_service_irq device_service_clear = {0}; 418 enum dc_status result; 419 bool status = false; 420 421 if (out_link_loss) 422 *out_link_loss = false; 423 424 if (has_left_work) 425 *has_left_work = false; 426 /* For use cases related to down stream connection status change, 427 * PSR and device auto test, refer to function handle_sst_hpd_irq 428 * in DAL2.1*/ 429 430 DC_LOG_HW_HPD_IRQ("%s: Got short pulse HPD on link %d\n", 431 __func__, link->link_index); 432 433 434 /* All the "handle_hpd_irq_xxx()" methods 435 * should be called only after 436 * dal_dpsst_ls_read_hpd_irq_data 437 * Order of calls is important too 438 */ 439 result = dp_read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data); 440 if (out_hpd_irq_dpcd_data) 441 *out_hpd_irq_dpcd_data = hpd_irq_dpcd_data; 442 443 if (result != DC_OK) { 444 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain irq data\n", 445 __func__); 446 return false; 447 } 448 449 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) { 450 // Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC 451 if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && 452 !link->dc->config.enable_dpia_pre_training) 453 link->skip_fallback_on_link_loss = true; 454 455 device_service_clear.bits.AUTOMATED_TEST = 1; 456 core_link_write_dpcd( 457 link, 458 DP_DEVICE_SERVICE_IRQ_VECTOR, 459 &device_service_clear.raw, 460 sizeof(device_service_clear.raw)); 461 device_service_clear.raw = 0; 462 if (defer_handling && has_left_work) 463 *has_left_work = true; 464 else 465 dc_link_dp_handle_automated_test(link); 466 return false; 467 } 468 469 if (!dp_should_allow_hpd_rx_irq(link)) { 470 DC_LOG_HW_HPD_IRQ("%s: skipping HPD handling on %d\n", 471 __func__, link->link_index); 472 return false; 473 } 474 475 if (handle_hpd_irq_psr_sink(link)) 476 /* PSR-related error was detected and handled */ 477 return true; 478 479 handle_hpd_irq_replay_sink(link); 480 481 /* If PSR-related error handled, Main link may be off, 482 * so do not handle as a normal sink status change interrupt. 483 */ 484 485 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) { 486 if (defer_handling && has_left_work) 487 *has_left_work = true; 488 return true; 489 } 490 491 /* check if we have MST msg and return since we poll for it */ 492 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) { 493 if (defer_handling && has_left_work) 494 *has_left_work = true; 495 return false; 496 } 497 498 /* For now we only handle 'Downstream port status' case. 499 * If we got sink count changed it means 500 * Downstream port status changed, 501 * then DM should call DC to do the detection. 502 * NOTE: Do not handle link loss on eDP since it is internal link 503 */ 504 if ((link->connector_signal != SIGNAL_TYPE_EDP) && 505 dp_parse_link_loss_status( 506 link, 507 &hpd_irq_dpcd_data)) { 508 /* Connectivity log: link loss */ 509 CONN_DATA_LINK_LOSS(link, 510 hpd_irq_dpcd_data.raw, 511 sizeof(hpd_irq_dpcd_data), 512 "Status: "); 513 514 if (defer_handling && has_left_work) 515 *has_left_work = true; 516 else 517 dp_handle_link_loss(link); 518 519 status = false; 520 if (out_link_loss) 521 *out_link_loss = true; 522 523 dp_trace_link_loss_increment(link); 524 } 525 526 if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling) { 527 if (hpd_irq_dpcd_data.bytes.link_service_irq_esi0.bits.DP_LINK_TUNNELING_IRQ) 528 dp_handle_tunneling_irq(link); 529 } 530 531 if (link->type == dc_connection_sst_branch && 532 hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT 533 != link->dpcd_sink_count) 534 status = true; 535 536 /* reasons for HPD RX: 537 * 1. Link Loss - ie Re-train the Link 538 * 2. MST sideband message 539 * 3. Automated Test - ie. Internal Commit 540 * 4. CP (copy protection) - (not interesting for DM???) 541 * 5. DRR 542 * 6. Downstream Port status changed 543 * -ie. Detect - this the only one 544 * which is interesting for DM because 545 * it must call dc_link_detect. 546 */ 547 return status; 548}
~/
Read-only file preview.