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
45bool 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 buf: &hpd_irq_dpcd_data->bytes.lane01_status.raw,
71 index: 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_settings: &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 data: &irq_reg_rx_power_state,
107 size: 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
121static 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 ctx: link->ctx,
130 link,
131 address: 368,/*DpcdAddress_PSR_Enable_Cfg*/
132 data: &psr_configuration.raw,
133 size: 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 ctx: link->ctx,
142 link,
143 address: 0x2006, /*DpcdAddress_PSR_Error_Status*/
144 data: (unsigned char *) dpcdbuf,
145 size: 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 ctx: link->ctx,
160 link,
161 address: 8198,/*DpcdAddress_PSR_Error_Status*/
162 data: &psr_error_status.raw,
163 size: 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: &allow_active, wait: true, force_static: false, NULL);
169 allow_active = true;
170 edp_set_psr_allow_active(link, allow_active: &allow_active, wait: true, force_static: 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
187static 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 ctx: link->ctx,
198 link,
199 DP_SINK_PR_REPLAY_STATUS,
200 data: &replay_configuration.raw,
201 size: sizeof(replay_configuration.raw));
202
203 dm_helpers_dp_read_dpcd(
204 ctx: link->ctx,
205 link,
206 DP_PSR_ERROR_STATUS,
207 data: &replay_error_status.raw,
208 size: 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 ctx: link->ctx,
231 link,
232 DP_SINK_PR_REPLAY_STATUS,
233 data: &replay_configuration.raw,
234 size: sizeof(replay_configuration.raw));
235
236 /* Acknowledge and clear error bits */
237 dm_helpers_dp_write_dpcd(
238 ctx: link->ctx,
239 link,
240 DP_PSR_ERROR_STATUS,/*DpcdAddress_REPLAY_Error_Status*/
241 data: &replay_error_status.raw,
242 size: 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(dc_link: link, enable: &allow_active, wait: true, force_static: false, NULL);
248 allow_active = true;
249 edp_set_replay_allow_active(dc_link: link, enable: &allow_active, wait: true, force_static: false, NULL);
250 }
251 }
252}
253
254void 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: &count, pipes);
262
263 for (i = 0; i < count; i++)
264 link_set_dpms_off(pipe_ctx: 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(state: link->dc->current_state, pipe_ctx: pipes[i]);
277 }
278}
279
280static 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 data: &dpcd_lane_status_updated.raw,
289 size: 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
299enum 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 data: irq_data->raw,
318 size: 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 data: tmp,
329 size: 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***************************/
353bool 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
368bool 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, irq_data: &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 data: &device_service_clear.raw,
415 size: 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: &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

source code of linux/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c