File Index
Files and directories
Sign in
FILE

link_dp_irq_handler.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c C
  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
 46bool 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
122static 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
188static 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
265void 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
291static 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
315static 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
334enum 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***************************/
395bool 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	 * 3) The link is bw_alloc enabled.
402	 */
403
404	if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
405		is_dp_branch_device(link) ||
406		link->dpia_bw_alloc_config.bw_alloc_enabled)
407		return true;
408
409	return false;
410}
411
412bool dp_handle_hpd_rx_irq(struct dc_link *link,
413		union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss,
414		bool defer_handling, bool *has_left_work)
415{
416	union hpd_irq_data hpd_irq_dpcd_data = {0};
417	union device_service_irq device_service_clear = {0};
418	enum dc_status result;
419	bool status = false;
420
421	if (out_link_loss)
422		*out_link_loss = false;
423
424	if (has_left_work)
425		*has_left_work = false;
426	/* For use cases related to down stream connection status change,
427	 * PSR and device auto test, refer to function handle_sst_hpd_irq
428	 * in DAL2.1*/
429
430	DC_LOG_HW_HPD_IRQ("%s: Got short pulse HPD on link %d\n",
431		__func__, link->link_index);
432
433
434	 /* All the "handle_hpd_irq_xxx()" methods
435		 * should be called only after
436		 * dal_dpsst_ls_read_hpd_irq_data
437		 * Order of calls is important too
438		 */
439	result = dp_read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data);
440	if (out_hpd_irq_dpcd_data)
441		*out_hpd_irq_dpcd_data = hpd_irq_dpcd_data;
442
443	if (result != DC_OK) {
444		DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain irq data\n",
445			__func__);
446		return false;
447	}
448
449	if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
450		// Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC
451		if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
452				!link->dc->config.enable_dpia_pre_training)
453			link->skip_fallback_on_link_loss = true;
454
455		device_service_clear.bits.AUTOMATED_TEST = 1;
456		core_link_write_dpcd(
457			link,
458			DP_DEVICE_SERVICE_IRQ_VECTOR,
459			&device_service_clear.raw,
460			sizeof(device_service_clear.raw));
461		device_service_clear.raw = 0;
462		if (defer_handling && has_left_work)
463			*has_left_work = true;
464		else
465			dc_link_dp_handle_automated_test(link);
466		return false;
467	}
468
469	if (!dp_should_allow_hpd_rx_irq(link)) {
470		DC_LOG_HW_HPD_IRQ("%s: skipping HPD handling on %d\n",
471			__func__, link->link_index);
472		return false;
473	}
474
475	if (handle_hpd_irq_psr_sink(link))
476		/* PSR-related error was detected and handled */
477		return true;
478
479	handle_hpd_irq_replay_sink(link);
480
481	/* If PSR-related error handled, Main link may be off,
482	 * so do not handle as a normal sink status change interrupt.
483	 */
484
485	if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) {
486		if (defer_handling && has_left_work)
487			*has_left_work = true;
488		return true;
489	}
490
491	/* check if we have MST msg and return since we poll for it */
492	if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
493		if (defer_handling && has_left_work)
494			*has_left_work = true;
495		return false;
496	}
497
498	/* For now we only handle 'Downstream port status' case.
499	 * If we got sink count changed it means
500	 * Downstream port status changed,
501	 * then DM should call DC to do the detection.
502	 * NOTE: Do not handle link loss on eDP since it is internal link
503	 */
504	if ((link->connector_signal != SIGNAL_TYPE_EDP) &&
505			dp_parse_link_loss_status(
506					link,
507					&hpd_irq_dpcd_data)) {
508		/* Connectivity log: link loss */
509		CONN_DATA_LINK_LOSS(link,
510					hpd_irq_dpcd_data.raw,
511					sizeof(hpd_irq_dpcd_data),
512					"Status: ");
513
514		if (defer_handling && has_left_work)
515			*has_left_work = true;
516		else
517			dp_handle_link_loss(link);
518
519		status = false;
520		if (out_link_loss)
521			*out_link_loss = true;
522
523		dp_trace_link_loss_increment(link);
524	}
525
526	if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling) {
527		if (hpd_irq_dpcd_data.bytes.link_service_irq_esi0.bits.DP_LINK_TUNNELING_IRQ)
528			dp_handle_tunneling_irq(link);
529	}
530
531	if (link->type == dc_connection_sst_branch &&
532		hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
533			!= link->dpcd_sink_count)
534		status = true;
535
536	/* reasons for HPD RX:
537	 * 1. Link Loss - ie Re-train the Link
538	 * 2. MST sideband message
539	 * 3. Automated Test - ie. Internal Commit
540	 * 4. CP (copy protection) - (not interesting for DM???)
541	 * 5. DRR
542	 * 6. Downstream Port status changed
543	 * -ie. Detect - this the only one
544	 * which is interesting for DM because
545	 * it must call dc_link_detect.
546	 */
547	return status;
548}
~/ Read-only file preview.