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
dp_parse_link_loss_status(struct dc_link * link,union hpd_irq_data * hpd_irq_dpcd_data)46 bool 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
handle_hpd_irq_psr_sink(struct dc_link * link)122 static 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
handle_hpd_irq_replay_sink(struct dc_link * link)188 static 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 if (link->replay_settings.config.force_disable_desync_error_check)
233 return;
234
235 /* Acknowledge and clear configuration bits */
236 dm_helpers_dp_write_dpcd(
237 link->ctx,
238 link,
239 DP_SINK_PR_REPLAY_STATUS,
240 &replay_configuration.raw,
241 sizeof(replay_configuration.raw));
242
243 /* Update desync error counter */
244 link->replay_settings.replay_desync_error_fail_count++;
245
246 /* Acknowledge and clear error bits */
247 dm_helpers_dp_write_dpcd(
248 link->ctx,
249 link,
250 DP_PSR_ERROR_STATUS,/*DpcdAddress_REPLAY_Error_Status*/
251 &replay_error_status.raw,
252 sizeof(replay_error_status.raw));
253
254 /* Replay error, disable and re-enable Replay */
255 if (link->replay_settings.replay_allow_active) {
256 allow_active = false;
257 edp_set_replay_allow_active(link, &allow_active, true, false, NULL);
258 allow_active = true;
259 edp_set_replay_allow_active(link, &allow_active, true, false, NULL);
260 }
261 }
262 }
263
dp_handle_link_loss(struct dc_link * link)264 void dp_handle_link_loss(struct dc_link *link)
265 {
266 struct pipe_ctx *pipes[MAX_PIPES];
267 struct dc_state *state = link->dc->current_state;
268 uint8_t count;
269 int i;
270
271 link_get_master_pipes_with_dpms_on(link, state, &count, pipes);
272
273 for (i = 0; i < count; i++)
274 link_set_dpms_off(pipes[i]);
275
276 for (i = count - 1; i >= 0; i--) {
277 // Always use max settings here for DP 1.4a LL Compliance CTS
278 if (link->skip_fallback_on_link_loss) {
279 pipes[i]->link_config.dp_link_settings.lane_count =
280 link->verified_link_cap.lane_count;
281 pipes[i]->link_config.dp_link_settings.link_rate =
282 link->verified_link_cap.link_rate;
283 pipes[i]->link_config.dp_link_settings.link_spread =
284 link->verified_link_cap.link_spread;
285 }
286 link_set_dpms_on(link->dc->current_state, pipes[i]);
287 }
288 }
289
dp_handle_tunneling_irq(struct dc_link * link)290 static void dp_handle_tunneling_irq(struct dc_link *link)
291 {
292 enum dc_status retval;
293 uint8_t tunneling_status = 0;
294
295 retval = core_link_read_dpcd(
296 link, DP_TUNNELING_STATUS,
297 &tunneling_status,
298 sizeof(tunneling_status));
299
300 if (retval == DC_OK) {
301 DC_LOG_HW_HPD_IRQ("%s: Got DP tunneling status on link %d status=0x%x",
302 __func__, link->link_index, tunneling_status);
303
304 if (tunneling_status & DP_TUNNELING_BW_ALLOC_BITS_MASK)
305 link_dp_dpia_handle_bw_alloc_status(link, tunneling_status);
306 }
307
308 tunneling_status = DP_TUNNELING_IRQ;
309 core_link_write_dpcd(
310 link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0,
311 &tunneling_status, 1);
312 }
313
read_dpcd204h_on_irq_hpd(struct dc_link * link,union hpd_irq_data * irq_data)314 static void read_dpcd204h_on_irq_hpd(struct dc_link *link, union hpd_irq_data *irq_data)
315 {
316 enum dc_status retval;
317 union lane_align_status_updated dpcd_lane_status_updated = {0};
318
319 retval = core_link_read_dpcd(
320 link,
321 DP_LANE_ALIGN_STATUS_UPDATED,
322 &dpcd_lane_status_updated.raw,
323 sizeof(union lane_align_status_updated));
324
325 if (retval == DC_OK) {
326 irq_data->bytes.lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b =
327 dpcd_lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b;
328 irq_data->bytes.lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b =
329 dpcd_lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b;
330 }
331 }
332
dp_read_hpd_rx_irq_data(struct dc_link * link,union hpd_irq_data * irq_data)333 enum dc_status dp_read_hpd_rx_irq_data(
334 struct dc_link *link,
335 union hpd_irq_data *irq_data)
336 {
337 static enum dc_status retval;
338
339 /* The HW reads 16 bytes from 200h on HPD,
340 * but if we get an AUX_DEFER, the HW cannot retry
341 * and this causes the CTS tests 4.3.2.1 - 3.2.4 to
342 * fail, so we now explicitly read 6 bytes which is
343 * the req from the above mentioned test cases.
344 *
345 * For DP 1.4 we need to read those from 2002h range.
346 */
347 if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) {
348 retval = core_link_read_dpcd(
349 link,
350 DP_SINK_COUNT,
351 irq_data->raw,
352 DP_SINK_STATUS - DP_SINK_COUNT + 1);
353
354 if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) {
355 retval = core_link_read_dpcd(
356 link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0,
357 &irq_data->bytes.link_service_irq_esi0.raw, 1);
358 }
359 } else {
360 /* Read 14 bytes in a single read and then copy only the required fields.
361 * This is more efficient than doing it in two separate AUX reads. */
362
363 uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1] = {0};
364
365 retval = core_link_read_dpcd(
366 link,
367 DP_SINK_COUNT_ESI,
368 tmp,
369 sizeof(tmp));
370
371 if (retval != DC_OK)
372 return retval;
373
374 irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI];
375 irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI];
376 irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI];
377 irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI];
378 irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI];
379 irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI];
380 irq_data->bytes.link_service_irq_esi0.raw = tmp[DP_LINK_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI];
381
382 /*
383 * This display doesn't have correct values in DPCD200Eh.
384 * Read and check DPCD204h instead.
385 */
386 if (link->wa_flags.read_dpcd204h_on_irq_hpd)
387 read_dpcd204h_on_irq_hpd(link, irq_data);
388 }
389
390 return retval;
391 }
392
393 /*************************Short Pulse IRQ***************************/
dp_should_allow_hpd_rx_irq(const struct dc_link * link)394 bool dp_should_allow_hpd_rx_irq(const struct dc_link *link)
395 {
396 /*
397 * Don't handle RX IRQ unless one of following is met:
398 * 1) The link is established (cur_link_settings != unknown)
399 * 2) We know we're dealing with a branch device, SST or MST
400 */
401
402 if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
403 is_dp_branch_device(link))
404 return true;
405
406 return false;
407 }
408
dp_handle_hpd_rx_irq(struct dc_link * link,union hpd_irq_data * out_hpd_irq_dpcd_data,bool * out_link_loss,bool defer_handling,bool * has_left_work)409 bool dp_handle_hpd_rx_irq(struct dc_link *link,
410 union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss,
411 bool defer_handling, bool *has_left_work)
412 {
413 union hpd_irq_data hpd_irq_dpcd_data = {0};
414 union device_service_irq device_service_clear = {0};
415 enum dc_status result;
416 bool status = false;
417
418 if (out_link_loss)
419 *out_link_loss = false;
420
421 if (has_left_work)
422 *has_left_work = false;
423 /* For use cases related to down stream connection status change,
424 * PSR and device auto test, refer to function handle_sst_hpd_irq
425 * in DAL2.1*/
426
427 DC_LOG_HW_HPD_IRQ("%s: Got short pulse HPD on link %d\n",
428 __func__, link->link_index);
429
430
431 /* All the "handle_hpd_irq_xxx()" methods
432 * should be called only after
433 * dal_dpsst_ls_read_hpd_irq_data
434 * Order of calls is important too
435 */
436 result = dp_read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data);
437 if (out_hpd_irq_dpcd_data)
438 *out_hpd_irq_dpcd_data = hpd_irq_dpcd_data;
439
440 if (result != DC_OK) {
441 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain irq data\n",
442 __func__);
443 return false;
444 }
445
446 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
447 // Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC
448 if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
449 !link->dc->config.enable_dpia_pre_training)
450 link->skip_fallback_on_link_loss = true;
451
452 device_service_clear.bits.AUTOMATED_TEST = 1;
453 core_link_write_dpcd(
454 link,
455 DP_DEVICE_SERVICE_IRQ_VECTOR,
456 &device_service_clear.raw,
457 sizeof(device_service_clear.raw));
458 device_service_clear.raw = 0;
459 if (defer_handling && has_left_work)
460 *has_left_work = true;
461 else
462 dc_link_dp_handle_automated_test(link);
463 return false;
464 }
465
466 if (!dp_should_allow_hpd_rx_irq(link)) {
467 DC_LOG_HW_HPD_IRQ("%s: skipping HPD handling on %d\n",
468 __func__, link->link_index);
469 return false;
470 }
471
472 if (handle_hpd_irq_psr_sink(link))
473 /* PSR-related error was detected and handled */
474 return true;
475
476 handle_hpd_irq_replay_sink(link);
477
478 /* If PSR-related error handled, Main link may be off,
479 * so do not handle as a normal sink status change interrupt.
480 */
481
482 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) {
483 if (defer_handling && has_left_work)
484 *has_left_work = true;
485 return true;
486 }
487
488 /* check if we have MST msg and return since we poll for it */
489 if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
490 if (defer_handling && has_left_work)
491 *has_left_work = true;
492 return false;
493 }
494
495 /* For now we only handle 'Downstream port status' case.
496 * If we got sink count changed it means
497 * Downstream port status changed,
498 * then DM should call DC to do the detection.
499 * NOTE: Do not handle link loss on eDP since it is internal link
500 */
501 if ((link->connector_signal != SIGNAL_TYPE_EDP) &&
502 dp_parse_link_loss_status(
503 link,
504 &hpd_irq_dpcd_data)) {
505 /* Connectivity log: link loss */
506 CONN_DATA_LINK_LOSS(link,
507 hpd_irq_dpcd_data.raw,
508 sizeof(hpd_irq_dpcd_data),
509 "Status: ");
510
511 if (defer_handling && has_left_work)
512 *has_left_work = true;
513 else
514 dp_handle_link_loss(link);
515
516 status = false;
517 if (out_link_loss)
518 *out_link_loss = true;
519
520 dp_trace_link_loss_increment(link);
521 }
522
523 if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) {
524 if (hpd_irq_dpcd_data.bytes.link_service_irq_esi0.bits.DP_LINK_TUNNELING_IRQ)
525 dp_handle_tunneling_irq(link);
526 }
527
528 if (link->type == dc_connection_sst_branch &&
529 hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
530 != link->dpcd_sink_count)
531 status = true;
532
533 /* reasons for HPD RX:
534 * 1. Link Loss - ie Re-train the Link
535 * 2. MST sideband message
536 * 3. Automated Test - ie. Internal Commit
537 * 4. CP (copy protection) - (not interesting for DM???)
538 * 5. DRR
539 * 6. Downstream Port status changed
540 * -ie. Detect - this the only one
541 * which is interesting for DM because
542 * it must call dc_link_detect.
543 */
544 return status;
545 }
546