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