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
dp_parse_link_loss_status(struct dc_link * link,union hpd_irq_data * hpd_irq_dpcd_data)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
handle_hpd_irq_psr_sink(struct dc_link * link)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
handle_hpd_irq_replay_sink(struct dc_link * link)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
dp_handle_link_loss(struct dc_link * link)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
read_dpcd204h_on_irq_hpd(struct dc_link * link,union hpd_irq_data * irq_data)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
dp_read_hpd_rx_irq_data(struct dc_link * link,union hpd_irq_data * irq_data)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***************************/
dp_should_allow_hpd_rx_irq(const struct dc_link * link)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
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)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