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