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 (sink_status_changed || 86 !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { 87 88 DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__); 89 90 return_code = true; 91 92 /*2. Check that we can handle interrupt: Not in FS DOS, 93 * Not in "Display Timeout" state, Link is trained. 94 */ 95 dpcd_result = core_link_read_dpcd(link, 96 DP_SET_POWER, 97 &irq_reg_rx_power_state, 98 sizeof(irq_reg_rx_power_state)); 99 100 if (dpcd_result != DC_OK) { 101 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n", 102 __func__); 103 } else { 104 if (irq_reg_rx_power_state != DP_SET_POWER_D0) 105 return_code = false; 106 } 107 } 108 109 return return_code; 110 } 111 112 static bool handle_hpd_irq_psr_sink(struct dc_link *link) 113 { 114 union dpcd_psr_configuration psr_configuration; 115 116 if (!link->psr_settings.psr_feature_enabled) 117 return false; 118 119 dm_helpers_dp_read_dpcd( 120 link->ctx, 121 link, 122 368,/*DpcdAddress_PSR_Enable_Cfg*/ 123 &psr_configuration.raw, 124 sizeof(psr_configuration.raw)); 125 126 if (psr_configuration.bits.ENABLE) { 127 unsigned char dpcdbuf[3] = {0}; 128 union psr_error_status psr_error_status; 129 union psr_sink_psr_status psr_sink_psr_status; 130 131 dm_helpers_dp_read_dpcd( 132 link->ctx, 133 link, 134 0x2006, /*DpcdAddress_PSR_Error_Status*/ 135 (unsigned char *) dpcdbuf, 136 sizeof(dpcdbuf)); 137 138 /*DPCD 2006h ERROR STATUS*/ 139 psr_error_status.raw = dpcdbuf[0]; 140 /*DPCD 2008h SINK PANEL SELF REFRESH STATUS*/ 141 psr_sink_psr_status.raw = dpcdbuf[2]; 142 143 if (psr_error_status.bits.LINK_CRC_ERROR || 144 psr_error_status.bits.RFB_STORAGE_ERROR || 145 psr_error_status.bits.VSC_SDP_ERROR) { 146 bool allow_active; 147 148 /* Acknowledge and clear error bits */ 149 dm_helpers_dp_write_dpcd( 150 link->ctx, 151 link, 152 8198,/*DpcdAddress_PSR_Error_Status*/ 153 &psr_error_status.raw, 154 sizeof(psr_error_status.raw)); 155 156 /* PSR error, disable and re-enable PSR */ 157 if (link->psr_settings.psr_allow_active) { 158 allow_active = false; 159 edp_set_psr_allow_active(link, &allow_active, true, false, NULL); 160 allow_active = true; 161 edp_set_psr_allow_active(link, &allow_active, true, false, NULL); 162 } 163 164 return true; 165 } else if (psr_sink_psr_status.bits.SINK_SELF_REFRESH_STATUS == 166 PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB){ 167 /* No error is detect, PSR is active. 168 * We should return with IRQ_HPD handled without 169 * checking for loss of sync since PSR would have 170 * powered down main link. 171 */ 172 return true; 173 } 174 } 175 return false; 176 } 177 178 void dp_handle_link_loss(struct dc_link *link) 179 { 180 struct pipe_ctx *pipes[MAX_PIPES]; 181 struct dc_state *state = link->dc->current_state; 182 uint8_t count; 183 int i; 184 185 link_get_master_pipes_with_dpms_on(link, state, &count, pipes); 186 187 for (i = 0; i < count; i++) 188 link_set_dpms_off(pipes[i]); 189 190 for (i = count - 1; i >= 0; i--) { 191 // Always use max settings here for DP 1.4a LL Compliance CTS 192 if (link->is_automated) { 193 pipes[i]->link_config.dp_link_settings.lane_count = 194 link->verified_link_cap.lane_count; 195 pipes[i]->link_config.dp_link_settings.link_rate = 196 link->verified_link_cap.link_rate; 197 pipes[i]->link_config.dp_link_settings.link_spread = 198 link->verified_link_cap.link_spread; 199 } 200 link_set_dpms_on(link->dc->current_state, pipes[i]); 201 } 202 } 203 204 enum dc_status dp_read_hpd_rx_irq_data( 205 struct dc_link *link, 206 union hpd_irq_data *irq_data) 207 { 208 static enum dc_status retval; 209 210 /* The HW reads 16 bytes from 200h on HPD, 211 * but if we get an AUX_DEFER, the HW cannot retry 212 * and this causes the CTS tests 4.3.2.1 - 3.2.4 to 213 * fail, so we now explicitly read 6 bytes which is 214 * the req from the above mentioned test cases. 215 * 216 * For DP 1.4 we need to read those from 2002h range. 217 */ 218 if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) 219 retval = core_link_read_dpcd( 220 link, 221 DP_SINK_COUNT, 222 irq_data->raw, 223 sizeof(union hpd_irq_data)); 224 else { 225 /* Read 14 bytes in a single read and then copy only the required fields. 226 * This is more efficient than doing it in two separate AUX reads. */ 227 228 uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1]; 229 230 retval = core_link_read_dpcd( 231 link, 232 DP_SINK_COUNT_ESI, 233 tmp, 234 sizeof(tmp)); 235 236 if (retval != DC_OK) 237 return retval; 238 239 irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI]; 240 irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; 241 irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI]; 242 irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; 243 irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; 244 irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; 245 } 246 247 return retval; 248 } 249 250 /*************************Short Pulse IRQ***************************/ 251 bool dp_should_allow_hpd_rx_irq(const struct dc_link *link) 252 { 253 /* 254 * Don't handle RX IRQ unless one of following is met: 255 * 1) The link is established (cur_link_settings != unknown) 256 * 2) We know we're dealing with a branch device, SST or MST 257 */ 258 259 if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) || 260 is_dp_branch_device(link)) 261 return true; 262 263 return false; 264 } 265 266 bool dp_handle_hpd_rx_irq(struct dc_link *link, 267 union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss, 268 bool defer_handling, bool *has_left_work) 269 { 270 union hpd_irq_data hpd_irq_dpcd_data = {0}; 271 union device_service_irq device_service_clear = {0}; 272 enum dc_status result; 273 bool status = false; 274 275 if (out_link_loss) 276 *out_link_loss = false; 277 278 if (has_left_work) 279 *has_left_work = false; 280 /* For use cases related to down stream connection status change, 281 * PSR and device auto test, refer to function handle_sst_hpd_irq 282 * in DAL2.1*/ 283 284 DC_LOG_HW_HPD_IRQ("%s: Got short pulse HPD on link %d\n", 285 __func__, link->link_index); 286 287 288 /* All the "handle_hpd_irq_xxx()" methods 289 * should be called only after 290 * dal_dpsst_ls_read_hpd_irq_data 291 * Order of calls is important too 292 */ 293 result = dp_read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data); 294 if (out_hpd_irq_dpcd_data) 295 *out_hpd_irq_dpcd_data = hpd_irq_dpcd_data; 296 297 if (result != DC_OK) { 298 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain irq data\n", 299 __func__); 300 return false; 301 } 302 303 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) { 304 // Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC 305 link->is_automated = true; 306 device_service_clear.bits.AUTOMATED_TEST = 1; 307 core_link_write_dpcd( 308 link, 309 DP_DEVICE_SERVICE_IRQ_VECTOR, 310 &device_service_clear.raw, 311 sizeof(device_service_clear.raw)); 312 device_service_clear.raw = 0; 313 if (defer_handling && has_left_work) 314 *has_left_work = true; 315 else 316 dc_link_dp_handle_automated_test(link); 317 return false; 318 } 319 320 if (!dp_should_allow_hpd_rx_irq(link)) { 321 DC_LOG_HW_HPD_IRQ("%s: skipping HPD handling on %d\n", 322 __func__, link->link_index); 323 return false; 324 } 325 326 if (handle_hpd_irq_psr_sink(link)) 327 /* PSR-related error was detected and handled */ 328 return true; 329 330 /* If PSR-related error handled, Main link may be off, 331 * so do not handle as a normal sink status change interrupt. 332 */ 333 334 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) { 335 if (defer_handling && has_left_work) 336 *has_left_work = true; 337 return true; 338 } 339 340 /* check if we have MST msg and return since we poll for it */ 341 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) { 342 if (defer_handling && has_left_work) 343 *has_left_work = true; 344 return false; 345 } 346 347 /* For now we only handle 'Downstream port status' case. 348 * If we got sink count changed it means 349 * Downstream port status changed, 350 * then DM should call DC to do the detection. 351 * NOTE: Do not handle link loss on eDP since it is internal link*/ 352 if ((link->connector_signal != SIGNAL_TYPE_EDP) && 353 dp_parse_link_loss_status( 354 link, 355 &hpd_irq_dpcd_data)) { 356 /* Connectivity log: link loss */ 357 CONN_DATA_LINK_LOSS(link, 358 hpd_irq_dpcd_data.raw, 359 sizeof(hpd_irq_dpcd_data), 360 "Status: "); 361 362 if (defer_handling && has_left_work) 363 *has_left_work = true; 364 else 365 dp_handle_link_loss(link); 366 367 status = false; 368 if (out_link_loss) 369 *out_link_loss = true; 370 371 dp_trace_link_loss_increment(link); 372 } 373 374 if (link->type == dc_connection_sst_branch && 375 hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT 376 != link->dpcd_sink_count) 377 status = true; 378 379 /* reasons for HPD RX: 380 * 1. Link Loss - ie Re-train the Link 381 * 2. MST sideband message 382 * 3. Automated Test - ie. Internal Commit 383 * 4. CP (copy protection) - (not interesting for DM???) 384 * 5. DRR 385 * 6. Downstream Port status changed 386 * -ie. Detect - this the only one 387 * which is interesting for DM because 388 * it must call dc_link_detect. 389 */ 390 return status; 391 } 392