xref: /linux/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c (revision d4a292c5f8e65d2784b703c67179f4f7d0c7846c)
1 /*
2  * Copyright 2025 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 #include "link_dp_panel_replay.h"
27 #include "link_edp_panel_control.h"
28 #include "link_dpcd.h"
29 #include "dm_helpers.h"
30 #include "dc/dc_dmub_srv.h"
31 #include "dce/dmub_replay.h"
32 
33 #define DC_LOGGER \
34 	link->ctx->logger
35 
36 #define DP_SINK_PR_ENABLE_AND_CONFIGURATION		0x37B
37 #define DP_SINK_ENABLE_FRAME_SKIPPING_MODE_SHIFT	(5)
38 
dp_pr_calc_num_static_frames(unsigned int vsync_rate_hz)39 static unsigned int dp_pr_calc_num_static_frames(unsigned int vsync_rate_hz)
40 {
41 	// at least 2 frames for static screen
42 	unsigned int num_frames = 2;
43 
44 	// get number of frames for at least 50ms
45 	if (vsync_rate_hz > 40)
46 		num_frames = (vsync_rate_hz + 10) / 20;
47 
48 	return num_frames;
49 }
50 
dp_pr_set_static_screen_param(struct dc_link * link)51 static void dp_pr_set_static_screen_param(struct dc_link *link)
52 {
53 	struct dc_static_screen_params params = {0};
54 	struct dc *dc = link->ctx->dc;
55 	// only support DP sst for now
56 	if (!dc_is_dp_sst_signal(link->connector_signal))
57 		return;
58 
59 	for (int i = 0; i < MAX_PIPES; i++) {
60 		if (dc->current_state->res_ctx.pipe_ctx[i].stream &&
61 			dc->current_state->res_ctx.pipe_ctx[i].stream->link == link) {
62 			struct dc_stream_state *stream = dc->current_state->res_ctx.pipe_ctx[i].stream;
63 			unsigned int vsync_rate_hz = div64_u64(div64_u64(
64 											(stream->timing.pix_clk_100hz * (u64)100),
65 											stream->timing.v_total),
66 											stream->timing.h_total);
67 
68 			params.triggers.cursor_update = true;
69 			params.triggers.overlay_update = true;
70 			params.triggers.surface_update = true;
71 			params.num_frames = dp_pr_calc_num_static_frames(vsync_rate_hz);
72 
73 			dc_stream_set_static_screen_params(dc, &stream, 1, &params);
74 			break;
75 		}
76 	}
77 }
78 
dp_setup_panel_replay(struct dc_link * link,const struct dc_stream_state * stream)79 static bool dp_setup_panel_replay(struct dc_link *link, const struct dc_stream_state *stream)
80 {
81 	/* To-do: Setup Replay */
82 	struct dc *dc;
83 	struct dmub_replay *replay;
84 	int i;
85 	unsigned int panel_inst;
86 	struct replay_context replay_context = { 0 };
87 	unsigned int lineTimeInNs = 0;
88 
89 	union panel_replay_enable_and_configuration_1 pr_config_1 = { 0 };
90 	union panel_replay_enable_and_configuration_2 pr_config_2 = { 0 };
91 
92 	union dpcd_alpm_configuration alpm_config;
93 	uint8_t data = 0;
94 
95 	replay_context.controllerId = CONTROLLER_ID_UNDEFINED;
96 
97 	if (!link)
98 		return false;
99 
100 	//Clear Panel Replay enable & config
101 	dm_helpers_dp_write_dpcd(link->ctx, link,
102 		DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1,
103 		(uint8_t *)&(pr_config_1.raw), sizeof(uint8_t));
104 
105 	dm_helpers_dp_write_dpcd(link->ctx, link,
106 		DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2,
107 		(uint8_t *)&(pr_config_2.raw), sizeof(uint8_t));
108 
109 	if (!(link->replay_settings.config.replay_supported))
110 		return false;
111 
112 	dc = link->ctx->dc;
113 
114 	//not sure should keep or not
115 	replay = dc->res_pool->replay;
116 
117 	if (!replay)
118 		return false;
119 
120 	if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
121 		return false;
122 
123 	replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel;
124 	replay_context.digbe_inst = link->link_enc->transmitter;
125 	replay_context.digfe_inst = link->link_enc->preferred_engine;
126 
127 	for (i = 0; i < MAX_PIPES; i++) {
128 		if (dc->current_state->res_ctx.pipe_ctx[i].stream
129 				== stream) {
130 			/* dmcu -1 for all controller id values,
131 			 * therefore +1 here
132 			 */
133 			replay_context.controllerId =
134 				dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg->inst + 1;
135 			break;
136 		}
137 	}
138 
139 	lineTimeInNs =
140 		((stream->timing.h_total * 1000000) /
141 			(stream->timing.pix_clk_100hz / 10)) + 1;
142 
143 	replay_context.line_time_in_ns = lineTimeInNs;
144 
145 	link->replay_settings.replay_feature_enabled = dp_pr_copy_settings(link, &replay_context);
146 
147 	if (link->replay_settings.replay_feature_enabled) {
148 		if (dc_is_embedded_signal(link->connector_signal)) {
149 			pr_config_1.bits.PANEL_REPLAY_ENABLE = 1;
150 			pr_config_1.bits.PANEL_REPLAY_CRC_ENABLE = 1;
151 			pr_config_1.bits.IRQ_HPD_ASSDP_MISSING = 1;
152 			pr_config_1.bits.IRQ_HPD_VSCSDP_UNCORRECTABLE_ERROR = 1;
153 			pr_config_1.bits.IRQ_HPD_RFB_ERROR = 1;
154 			pr_config_1.bits.IRQ_HPD_ACTIVE_FRAME_CRC_ERROR = 1;
155 			pr_config_1.bits.PANEL_REPLAY_SELECTIVE_UPDATE_ENABLE = 1;
156 			pr_config_1.bits.PANEL_REPLAY_EARLY_TRANSPORT_ENABLE = 1;
157 		} else {
158 			pr_config_1.bits.PANEL_REPLAY_ENABLE = 1;
159 		}
160 
161 		pr_config_2.bits.SINK_REFRESH_RATE_UNLOCK_GRANTED = 0;
162 
163 		if (link->dpcd_caps.vesa_replay_caps.bits.SU_Y_GRANULARITY_EXT_CAP_SUPPORTED)
164 			pr_config_2.bits.SU_Y_GRANULARITY_EXT_VALUE_ENABLED = 1;
165 
166 		pr_config_2.bits.SU_REGION_SCAN_LINE_CAPTURE_INDICATION = 0;
167 
168 		dm_helpers_dp_write_dpcd(link->ctx, link,
169 			DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1,
170 			(uint8_t *)&(pr_config_1.raw), sizeof(uint8_t));
171 
172 		dm_helpers_dp_write_dpcd(link->ctx, link,
173 			DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2,
174 			(uint8_t *)&(pr_config_2.raw), sizeof(uint8_t));
175 
176 		//ALPM Setup
177 		memset(&alpm_config, 0, sizeof(alpm_config));
178 		alpm_config.bits.ENABLE = link->replay_settings.config.alpm_mode != DC_ALPM_UNSUPPORTED ? 1 : 0;
179 
180 		if (link->replay_settings.config.alpm_mode == DC_ALPM_AUXLESS) {
181 			alpm_config.bits.ALPM_MODE_SEL = 1;
182 			alpm_config.bits.ACDS_PERIOD_DURATION = 1;
183 		}
184 
185 		dm_helpers_dp_write_dpcd(
186 			link->ctx,
187 			link,
188 			DP_RECEIVER_ALPM_CONFIG,
189 			&alpm_config.raw,
190 			sizeof(alpm_config.raw));
191 
192 		//Enable frame skipping
193 		if (link->replay_settings.config.frame_skip_supported)
194 			data = data | (1 << DP_SINK_ENABLE_FRAME_SKIPPING_MODE_SHIFT);
195 
196 		dm_helpers_dp_write_dpcd(link->ctx, link,
197 			DP_SINK_PR_ENABLE_AND_CONFIGURATION,
198 			(uint8_t *)&(data), sizeof(uint8_t));
199 	}
200 
201 	return true;
202 }
203 
204 
dp_pr_get_panel_inst(const struct dc * dc,const struct dc_link * link,unsigned int * inst_out)205 bool dp_pr_get_panel_inst(const struct dc *dc,
206 		const struct dc_link *link,
207 		unsigned int *inst_out)
208 {
209 	if (!dc || !link || !inst_out)
210 		return false;
211 
212 	if (dc->config.frame_update_cmd_version2 == false)
213 		return dc_get_edp_link_panel_inst(dc, link, inst_out);
214 
215 	if (!dc_is_dp_sst_signal(link->connector_signal)) /* only supoprt DP sst (eDP included) for now */
216 		return false;
217 
218 	for (unsigned int i = 0; i < MAX_PIPES; i++) {
219 		if (dc->current_state->res_ctx.pipe_ctx[i].stream &&
220 			dc->current_state->res_ctx.pipe_ctx[i].stream->link == link) {
221 			/* *inst_out is equal to otg number */
222 			if (dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg)
223 				*inst_out = dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg->inst;
224 			else
225 				*inst_out = 0;
226 
227 			return true;
228 		}
229 	}
230 
231 	return false;
232 }
233 
dp_setup_replay(struct dc_link * link,const struct dc_stream_state * stream)234 bool dp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream)
235 {
236 	if (!link)
237 		return false;
238 	if (link->replay_settings.config.replay_version == DC_VESA_PANEL_REPLAY)
239 		return dp_setup_panel_replay(link, stream);
240 	else if (link->replay_settings.config.replay_version == DC_FREESYNC_REPLAY)
241 		return edp_setup_freesync_replay(link, stream);
242 	else
243 		return false;
244 }
245 
dp_pr_enable(struct dc_link * link,bool enable)246 bool dp_pr_enable(struct dc_link *link, bool enable)
247 {
248 	struct dc *dc = link->ctx->dc;
249 	unsigned int panel_inst = 0;
250 	union dmub_rb_cmd cmd;
251 
252 	if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
253 		return false;
254 
255 	if (enable && !dc_is_embedded_signal(link->connector_signal))
256 		dp_pr_set_static_screen_param(link);
257 
258 	if (link->replay_settings.replay_allow_active != enable) {
259 		//for sending PR enable commands to DMUB
260 		memset(&cmd, 0, sizeof(cmd));
261 
262 		cmd.pr_enable.header.type = DMUB_CMD__PR;
263 		cmd.pr_enable.header.sub_type = DMUB_CMD__PR_ENABLE;
264 		cmd.pr_enable.header.payload_bytes = sizeof(struct dmub_cmd_pr_enable_data);
265 		cmd.pr_enable.data.panel_inst = panel_inst;
266 		cmd.pr_enable.data.enable = enable ? 1 : 0;
267 
268 		dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
269 
270 		link->replay_settings.replay_allow_active = enable;
271 	}
272 	return true;
273 }
274 
dp_pr_copy_settings(struct dc_link * link,struct replay_context * replay_context)275 bool dp_pr_copy_settings(struct dc_link *link, struct replay_context *replay_context)
276 {
277 	struct dc *dc = link->ctx->dc;
278 	unsigned int panel_inst = 0;
279 	union dmub_rb_cmd cmd;
280 	struct pipe_ctx *pipe_ctx = NULL;
281 
282 	if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
283 		return false;
284 
285 	for (unsigned int i = 0; i < MAX_PIPES; i++) {
286 		if (dc->current_state->res_ctx.pipe_ctx[i].stream &&
287 			dc->current_state->res_ctx.pipe_ctx[i].stream->link &&
288 			dc->current_state->res_ctx.pipe_ctx[i].stream->link == link &&
289 			dc_is_dp_sst_signal(dc->current_state->res_ctx.pipe_ctx[i].stream->link->connector_signal)) {
290 			pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
291 			/* todo: need update for MST */
292 			break;
293 		}
294 	}
295 
296 	if (!pipe_ctx)
297 		return false;
298 
299 	memset(&cmd, 0, sizeof(cmd));
300 	cmd.pr_copy_settings.header.type = DMUB_CMD__PR;
301 	cmd.pr_copy_settings.header.sub_type = DMUB_CMD__PR_COPY_SETTINGS;
302 	cmd.pr_copy_settings.header.payload_bytes = sizeof(struct dmub_cmd_pr_copy_settings_data);
303 	cmd.pr_copy_settings.data.panel_inst = panel_inst;
304 	// HW inst
305 	cmd.pr_copy_settings.data.aux_inst = replay_context->aux_inst;
306 	cmd.pr_copy_settings.data.digbe_inst = replay_context->digbe_inst;
307 	cmd.pr_copy_settings.data.digfe_inst = replay_context->digfe_inst;
308 	if (pipe_ctx->plane_res.dpp)
309 		cmd.pr_copy_settings.data.dpp_inst = pipe_ctx->plane_res.dpp->inst;
310 	else
311 		cmd.pr_copy_settings.data.dpp_inst = 0;
312 	if (pipe_ctx->stream_res.tg)
313 		cmd.pr_copy_settings.data.otg_inst = pipe_ctx->stream_res.tg->inst;
314 	else
315 		cmd.pr_copy_settings.data.otg_inst = 0;
316 
317 	cmd.pr_copy_settings.data.dpphy_inst = link->link_enc->transmitter;
318 
319 	cmd.pr_copy_settings.data.line_time_in_ns = replay_context->line_time_in_ns;
320 	cmd.pr_copy_settings.data.flags.bitfields.fec_enable_status = (link->fec_state == dc_link_fec_enabled);
321 	cmd.pr_copy_settings.data.flags.bitfields.dsc_enable_status = (pipe_ctx->stream->timing.flags.DSC == 1);
322 	cmd.pr_copy_settings.data.debug.u32All = link->replay_settings.config.debug_flags;
323 
324 	cmd.pr_copy_settings.data.su_granularity_needed = link->dpcd_caps.vesa_replay_caps.bits.PR_SU_GRANULARITY_NEEDED;
325 	cmd.pr_copy_settings.data.su_x_granularity = link->dpcd_caps.vesa_replay_su_info.pr_su_x_granularity;
326 	cmd.pr_copy_settings.data.su_y_granularity = link->dpcd_caps.vesa_replay_su_info.pr_su_y_granularity;
327 	cmd.pr_copy_settings.data.su_y_granularity_extended_caps =
328 		link->dpcd_caps.vesa_replay_su_info.pr_su_y_granularity_extended_caps;
329 
330 	if (pipe_ctx->stream->timing.dsc_cfg.num_slices_v > 0)
331 		cmd.pr_copy_settings.data.dsc_slice_height = (pipe_ctx->stream->timing.v_addressable +
332 			pipe_ctx->stream->timing.v_border_top + pipe_ctx->stream->timing.v_border_bottom) /
333 			pipe_ctx->stream->timing.dsc_cfg.num_slices_v;
334 
335 	if (dc_is_embedded_signal(link->connector_signal))
336 		cmd.pr_copy_settings.data.main_link_activity_option = OPTION_1C;
337 	else
338 		// For external DP, use option 1-A
339 		cmd.pr_copy_settings.data.main_link_activity_option = OPTION_1A;
340 
341 	dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
342 	return true;
343 }
344 
dp_pr_update_state(struct dc_link * link,struct dmub_cmd_pr_update_state_data * update_state_data)345 bool dp_pr_update_state(struct dc_link *link, struct dmub_cmd_pr_update_state_data *update_state_data)
346 {
347 	struct dc *dc = link->ctx->dc;
348 	unsigned int panel_inst = 0;
349 	union dmub_rb_cmd cmd;
350 
351 	if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
352 		return false;
353 
354 	memset(&cmd, 0, sizeof(cmd));
355 	memcpy(&cmd.pr_update_state.data, update_state_data, sizeof(struct dmub_cmd_pr_update_state_data));
356 
357 	cmd.pr_update_state.header.type = DMUB_CMD__PR;
358 	cmd.pr_update_state.header.sub_type = DMUB_CMD__PR_UPDATE_STATE;
359 	cmd.pr_update_state.header.payload_bytes = sizeof(struct dmub_cmd_pr_update_state_data);
360 	cmd.pr_update_state.data.panel_inst = panel_inst;
361 
362 	dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
363 	return true;
364 }
365 
dp_pr_set_general_cmd(struct dc_link * link,struct dmub_cmd_pr_general_cmd_data * general_cmd_data)366 bool dp_pr_set_general_cmd(struct dc_link *link, struct dmub_cmd_pr_general_cmd_data *general_cmd_data)
367 {
368 	struct dc *dc = link->ctx->dc;
369 	unsigned int panel_inst = 0;
370 	union dmub_rb_cmd cmd;
371 
372 	if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
373 		return false;
374 
375 	memset(&cmd, 0, sizeof(cmd));
376 	memcpy(&cmd.pr_general_cmd.data, general_cmd_data, sizeof(struct dmub_cmd_pr_general_cmd_data));
377 
378 	cmd.pr_general_cmd.header.type = DMUB_CMD__PR;
379 	cmd.pr_general_cmd.header.sub_type = DMUB_CMD__PR_GENERAL_CMD;
380 	cmd.pr_general_cmd.header.payload_bytes = sizeof(struct dmub_cmd_pr_general_cmd_data);
381 	cmd.pr_general_cmd.data.panel_inst = panel_inst;
382 
383 	dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
384 	return true;
385 }
386 
dp_pr_get_state(const struct dc_link * link,uint64_t * state)387 bool dp_pr_get_state(const struct dc_link *link, uint64_t *state)
388 {
389 	const struct dc *dc = link->ctx->dc;
390 	unsigned int panel_inst = 0;
391 	uint32_t retry_count = 0;
392 	uint32_t replay_state = 0;
393 
394 	if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
395 		return false;
396 
397 	do {
398 		// Send gpint command and wait for ack
399 		if (!dc_wake_and_execute_gpint(dc->ctx, DMUB_GPINT__GET_REPLAY_STATE, panel_inst,
400 					       &replay_state, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)) {
401 			// Return invalid state when GPINT times out
402 			replay_state = PR_STATE_INVALID;
403 		}
404 		/* Copy 32-bit result into 64-bit output */
405 		*state = replay_state;
406 	} while (++retry_count <= 1000 && *state == PR_STATE_INVALID);
407 
408 	// Assert if max retry hit
409 	if (retry_count >= 1000 && *state == PR_STATE_INVALID) {
410 		ASSERT(0);
411 		/* To-do: Add retry fail log */
412 	}
413 
414 	return true;
415 }
416