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