1 | /* |
2 | * Copyright 2023 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 "dc.h" |
27 | #include "dc_dmub_srv.h" |
28 | #include "dmub/dmub_srv.h" |
29 | #include "core_types.h" |
30 | #include "dmub_replay.h" |
31 | |
32 | #define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ |
33 | |
34 | #define MAX_PIPES 6 |
35 | |
36 | /* |
37 | * Get Replay state from firmware. |
38 | */ |
39 | static void dmub_replay_get_state(struct dmub_replay *dmub, enum replay_state *state, uint8_t panel_inst) |
40 | { |
41 | struct dmub_srv *srv = dmub->ctx->dmub_srv->dmub; |
42 | /* uint32_t raw_state = 0; */ |
43 | uint32_t retry_count = 0; |
44 | enum dmub_status status; |
45 | |
46 | do { |
47 | // Send gpint command and wait for ack |
48 | status = dmub_srv_send_gpint_command(dmub: srv, command_code: DMUB_GPINT__GET_REPLAY_STATE, param: panel_inst, timeout_us: 30); |
49 | |
50 | if (status == DMUB_STATUS_OK) { |
51 | // GPINT was executed, get response |
52 | dmub_srv_get_gpint_response(dmub: srv, response: (uint32_t *)state); |
53 | } else |
54 | // Return invalid state when GPINT times out |
55 | *state = REPLAY_STATE_INVALID; |
56 | } while (++retry_count <= 1000 && *state == REPLAY_STATE_INVALID); |
57 | |
58 | // Assert if max retry hit |
59 | if (retry_count >= 1000 && *state == REPLAY_STATE_INVALID) { |
60 | ASSERT(0); |
61 | /* To-do: Add retry fail log */ |
62 | } |
63 | } |
64 | |
65 | /* |
66 | * Enable/Disable Replay. |
67 | */ |
68 | static void dmub_replay_enable(struct dmub_replay *dmub, bool enable, bool wait, uint8_t panel_inst) |
69 | { |
70 | union dmub_rb_cmd cmd; |
71 | struct dc_context *dc = dmub->ctx; |
72 | uint32_t retry_count; |
73 | enum replay_state state = REPLAY_STATE_0; |
74 | |
75 | memset(&cmd, 0, sizeof(cmd)); |
76 | cmd.replay_enable.header.type = DMUB_CMD__REPLAY; |
77 | cmd.replay_enable.data.panel_inst = panel_inst; |
78 | |
79 | cmd.replay_enable.header.sub_type = DMUB_CMD__REPLAY_ENABLE; |
80 | if (enable) |
81 | cmd.replay_enable.data.enable = REPLAY_ENABLE; |
82 | else |
83 | cmd.replay_enable.data.enable = REPLAY_DISABLE; |
84 | |
85 | cmd.replay_enable.header.payload_bytes = sizeof(struct dmub_rb_cmd_replay_enable_data); |
86 | |
87 | dm_execute_dmub_cmd(ctx: dc, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
88 | |
89 | /* Below loops 1000 x 500us = 500 ms. |
90 | * Exit REPLAY may need to wait 1-2 frames to power up. Timeout after at |
91 | * least a few frames. Should never hit the max retry assert below. |
92 | */ |
93 | if (wait) { |
94 | for (retry_count = 0; retry_count <= 1000; retry_count++) { |
95 | dmub_replay_get_state(dmub, state: &state, panel_inst); |
96 | |
97 | if (enable) { |
98 | if (state != REPLAY_STATE_0) |
99 | break; |
100 | } else { |
101 | if (state == REPLAY_STATE_0) |
102 | break; |
103 | } |
104 | |
105 | fsleep(usecs: 500); |
106 | } |
107 | |
108 | /* assert if max retry hit */ |
109 | if (retry_count >= 1000) |
110 | ASSERT(0); |
111 | } |
112 | |
113 | } |
114 | |
115 | /* |
116 | * Set REPLAY power optimization flags. |
117 | */ |
118 | static void dmub_replay_set_power_opt(struct dmub_replay *dmub, unsigned int power_opt, uint8_t panel_inst) |
119 | { |
120 | union dmub_rb_cmd cmd; |
121 | struct dc_context *dc = dmub->ctx; |
122 | |
123 | memset(&cmd, 0, sizeof(cmd)); |
124 | cmd.replay_set_power_opt.header.type = DMUB_CMD__REPLAY; |
125 | cmd.replay_set_power_opt.header.sub_type = DMUB_CMD__SET_REPLAY_POWER_OPT; |
126 | cmd.replay_set_power_opt.header.payload_bytes = sizeof(struct dmub_cmd_replay_set_power_opt_data); |
127 | cmd.replay_set_power_opt.replay_set_power_opt_data.power_opt = power_opt; |
128 | cmd.replay_set_power_opt.replay_set_power_opt_data.panel_inst = panel_inst; |
129 | |
130 | dm_execute_dmub_cmd(ctx: dc, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
131 | } |
132 | |
133 | /* |
134 | * Setup Replay by programming phy registers and sending replay hw context values to firmware. |
135 | */ |
136 | static bool dmub_replay_copy_settings(struct dmub_replay *dmub, |
137 | struct dc_link *link, |
138 | struct replay_context *replay_context, |
139 | uint8_t panel_inst) |
140 | { |
141 | union dmub_rb_cmd cmd; |
142 | struct dc_context *dc = dmub->ctx; |
143 | struct dmub_cmd_replay_copy_settings_data *copy_settings_data |
144 | = &cmd.replay_copy_settings.replay_copy_settings_data; |
145 | struct pipe_ctx *pipe_ctx = NULL; |
146 | struct resource_context *res_ctx = &link->ctx->dc->current_state->res_ctx; |
147 | int i = 0; |
148 | |
149 | for (i = 0; i < MAX_PIPES; i++) { |
150 | if (res_ctx && |
151 | res_ctx->pipe_ctx[i].stream && |
152 | res_ctx->pipe_ctx[i].stream->link && |
153 | res_ctx->pipe_ctx[i].stream->link == link && |
154 | res_ctx->pipe_ctx[i].stream->link->connector_signal == SIGNAL_TYPE_EDP) { |
155 | pipe_ctx = &res_ctx->pipe_ctx[i]; |
156 | //TODO: refactor for multi edp support |
157 | break; |
158 | } |
159 | } |
160 | |
161 | if (!pipe_ctx) |
162 | return false; |
163 | |
164 | memset(&cmd, 0, sizeof(cmd)); |
165 | cmd.replay_copy_settings.header.type = DMUB_CMD__REPLAY; |
166 | cmd.replay_copy_settings.header.sub_type = DMUB_CMD__REPLAY_COPY_SETTINGS; |
167 | cmd.replay_copy_settings.header.payload_bytes = sizeof(struct dmub_cmd_replay_copy_settings_data); |
168 | |
169 | // HW insts |
170 | copy_settings_data->aux_inst = replay_context->aux_inst; |
171 | copy_settings_data->digbe_inst = replay_context->digbe_inst; |
172 | copy_settings_data->digfe_inst = replay_context->digfe_inst; |
173 | |
174 | if (pipe_ctx->plane_res.dpp) |
175 | copy_settings_data->dpp_inst = pipe_ctx->plane_res.dpp->inst; |
176 | else |
177 | copy_settings_data->dpp_inst = 0; |
178 | if (pipe_ctx->stream_res.tg) |
179 | copy_settings_data->otg_inst = pipe_ctx->stream_res.tg->inst; |
180 | else |
181 | copy_settings_data->otg_inst = 0; |
182 | |
183 | copy_settings_data->dpphy_inst = link->link_enc->transmitter; |
184 | |
185 | // Misc |
186 | copy_settings_data->line_time_in_ns = replay_context->line_time_in_ns; |
187 | copy_settings_data->panel_inst = panel_inst; |
188 | copy_settings_data->debug.u32All = link->replay_settings.config.debug_flags; |
189 | copy_settings_data->pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line; |
190 | copy_settings_data->max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line; |
191 | copy_settings_data->smu_optimizations_en = link->replay_settings.replay_smu_opt_enable; |
192 | copy_settings_data->replay_timing_sync_supported = link->replay_settings.config.replay_timing_sync_supported; |
193 | |
194 | copy_settings_data->flags.u32All = 0; |
195 | copy_settings_data->flags.bitfields.fec_enable_status = (link->fec_state == dc_link_fec_enabled); |
196 | copy_settings_data->flags.bitfields.dsc_enable_status = (pipe_ctx->stream->timing.flags.DSC == 1); |
197 | // WA for PSRSU+DSC on specific TCON, if DSC is enabled, force PSRSU as ffu mode(full frame update) |
198 | if (((link->dpcd_caps.fec_cap.bits.FEC_CAPABLE && |
199 | !link->dc->debug.disable_fec) && |
200 | (link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && |
201 | !link->panel_config.dsc.disable_dsc_edp && |
202 | link->dc->caps.edp_dsc_support)) && |
203 | link->dpcd_caps.sink_dev_id == DP_DEVICE_ID_38EC11 /*&& |
204 | (!memcmp(link->dpcd_caps.sink_dev_id_str, DP_SINK_DEVICE_STR_ID_1, |
205 | sizeof(DP_SINK_DEVICE_STR_ID_1)) || |
206 | !memcmp(link->dpcd_caps.sink_dev_id_str, DP_SINK_DEVICE_STR_ID_2, |
207 | sizeof(DP_SINK_DEVICE_STR_ID_2)))*/) |
208 | copy_settings_data->flags.bitfields.force_wakeup_by_tps3 = 1; |
209 | else |
210 | copy_settings_data->flags.bitfields.force_wakeup_by_tps3 = 0; |
211 | |
212 | |
213 | dm_execute_dmub_cmd(ctx: dc, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
214 | |
215 | return true; |
216 | } |
217 | |
218 | /* |
219 | * Set coasting vtotal. |
220 | */ |
221 | static void dmub_replay_set_coasting_vtotal(struct dmub_replay *dmub, |
222 | uint16_t coasting_vtotal, |
223 | uint8_t panel_inst) |
224 | { |
225 | union dmub_rb_cmd cmd; |
226 | struct dc_context *dc = dmub->ctx; |
227 | |
228 | memset(&cmd, 0, sizeof(cmd)); |
229 | cmd.replay_set_coasting_vtotal.header.type = DMUB_CMD__REPLAY; |
230 | cmd.replay_set_coasting_vtotal.header.sub_type = DMUB_CMD__REPLAY_SET_COASTING_VTOTAL; |
231 | cmd.replay_set_coasting_vtotal.header.payload_bytes = sizeof(struct dmub_cmd_replay_set_coasting_vtotal_data); |
232 | cmd.replay_set_coasting_vtotal.replay_set_coasting_vtotal_data.coasting_vtotal = coasting_vtotal; |
233 | |
234 | dm_execute_dmub_cmd(ctx: dc, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
235 | } |
236 | |
237 | /* |
238 | * Get Replay residency from firmware. |
239 | */ |
240 | static void dmub_replay_residency(struct dmub_replay *dmub, uint8_t panel_inst, |
241 | uint32_t *residency, const bool is_start, const bool is_alpm) |
242 | { |
243 | struct dmub_srv *srv = dmub->ctx->dmub_srv->dmub; |
244 | uint16_t param = (uint16_t)(panel_inst << 8); |
245 | |
246 | if (is_alpm) |
247 | param |= REPLAY_RESIDENCY_MODE_ALPM; |
248 | |
249 | if (is_start) |
250 | param |= REPLAY_RESIDENCY_ENABLE; |
251 | |
252 | // Send gpint command and wait for ack |
253 | dmub_srv_send_gpint_command(dmub: srv, command_code: DMUB_GPINT__REPLAY_RESIDENCY, param, timeout_us: 30); |
254 | |
255 | if (!is_start) |
256 | dmub_srv_get_gpint_response(dmub: srv, response: residency); |
257 | else |
258 | *residency = 0; |
259 | } |
260 | |
261 | /* |
262 | * Set REPLAY power optimization flags and coasting vtotal. |
263 | */ |
264 | static void dmub_replay_set_power_opt_and_coasting_vtotal(struct dmub_replay *dmub, |
265 | unsigned int power_opt, uint8_t panel_inst, uint16_t coasting_vtotal) |
266 | { |
267 | union dmub_rb_cmd cmd; |
268 | struct dc_context *dc = dmub->ctx; |
269 | |
270 | memset(&cmd, 0, sizeof(cmd)); |
271 | cmd.replay_set_power_opt_and_coasting_vtotal.header.type = DMUB_CMD__REPLAY; |
272 | cmd.replay_set_power_opt_and_coasting_vtotal.header.sub_type = |
273 | DMUB_CMD__REPLAY_SET_POWER_OPT_AND_COASTING_VTOTAL; |
274 | cmd.replay_set_power_opt_and_coasting_vtotal.header.payload_bytes = |
275 | sizeof(struct dmub_rb_cmd_replay_set_power_opt_and_coasting_vtotal); |
276 | cmd.replay_set_power_opt_and_coasting_vtotal.replay_set_power_opt_data.power_opt = power_opt; |
277 | cmd.replay_set_power_opt_and_coasting_vtotal.replay_set_power_opt_data.panel_inst = panel_inst; |
278 | cmd.replay_set_power_opt_and_coasting_vtotal.replay_set_coasting_vtotal_data.coasting_vtotal = coasting_vtotal; |
279 | |
280 | dc_wake_and_execute_dmub_cmd(ctx: dc, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
281 | } |
282 | |
283 | /* |
284 | * send Replay general cmd to DMUB. |
285 | */ |
286 | static void dmub_replay_send_cmd(struct dmub_replay *dmub, |
287 | enum replay_FW_Message_type msg, union dmub_replay_cmd_set *cmd_element) |
288 | { |
289 | union dmub_rb_cmd cmd; |
290 | struct dc_context *ctx = NULL; |
291 | |
292 | if (dmub == NULL || cmd_element == NULL) |
293 | return; |
294 | |
295 | ctx = dmub->ctx; |
296 | if (ctx != NULL) { |
297 | |
298 | if (msg != Replay_Msg_Not_Support) { |
299 | memset(&cmd, 0, sizeof(cmd)); |
300 | //Header |
301 | cmd.replay_set_timing_sync.header.type = DMUB_CMD__REPLAY; |
302 | } else |
303 | return; |
304 | } else |
305 | return; |
306 | |
307 | switch (msg) { |
308 | case Replay_Set_Timing_Sync_Supported: |
309 | //Header |
310 | cmd.replay_set_timing_sync.header.sub_type = |
311 | DMUB_CMD__REPLAY_SET_TIMING_SYNC_SUPPORTED; |
312 | cmd.replay_set_timing_sync.header.payload_bytes = |
313 | sizeof(struct dmub_rb_cmd_replay_set_timing_sync); |
314 | //Cmd Body |
315 | cmd.replay_set_timing_sync.replay_set_timing_sync_data.panel_inst = |
316 | cmd_element->sync_data.panel_inst; |
317 | cmd.replay_set_timing_sync.replay_set_timing_sync_data.timing_sync_supported = |
318 | cmd_element->sync_data.timing_sync_supported; |
319 | break; |
320 | case Replay_Set_Residency_Frameupdate_Timer: |
321 | //Header |
322 | cmd.replay_set_frameupdate_timer.header.sub_type = |
323 | DMUB_CMD__REPLAY_SET_RESIDENCY_FRAMEUPDATE_TIMER; |
324 | cmd.replay_set_frameupdate_timer.header.payload_bytes = |
325 | sizeof(struct dmub_rb_cmd_replay_set_frameupdate_timer); |
326 | //Cmd Body |
327 | cmd.replay_set_frameupdate_timer.data.panel_inst = |
328 | cmd_element->panel_inst; |
329 | cmd.replay_set_frameupdate_timer.data.enable = |
330 | cmd_element->timer_data.enable; |
331 | cmd.replay_set_frameupdate_timer.data.frameupdate_count = |
332 | cmd_element->timer_data.frameupdate_count; |
333 | break; |
334 | case Replay_Msg_Not_Support: |
335 | default: |
336 | return; |
337 | break; |
338 | } |
339 | |
340 | dc_wake_and_execute_dmub_cmd(ctx, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
341 | } |
342 | |
343 | static const struct dmub_replay_funcs replay_funcs = { |
344 | .replay_copy_settings = dmub_replay_copy_settings, |
345 | .replay_enable = dmub_replay_enable, |
346 | .replay_get_state = dmub_replay_get_state, |
347 | .replay_set_power_opt = dmub_replay_set_power_opt, |
348 | .replay_set_coasting_vtotal = dmub_replay_set_coasting_vtotal, |
349 | .replay_residency = dmub_replay_residency, |
350 | .replay_set_power_opt_and_coasting_vtotal = dmub_replay_set_power_opt_and_coasting_vtotal, |
351 | .replay_send_cmd = dmub_replay_send_cmd, |
352 | }; |
353 | |
354 | /* |
355 | * Construct Replay object. |
356 | */ |
357 | static void dmub_replay_construct(struct dmub_replay *replay, struct dc_context *ctx) |
358 | { |
359 | replay->ctx = ctx; |
360 | replay->funcs = &replay_funcs; |
361 | } |
362 | |
363 | /* |
364 | * Allocate and initialize Replay object. |
365 | */ |
366 | struct dmub_replay *dmub_replay_create(struct dc_context *ctx) |
367 | { |
368 | struct dmub_replay *replay = kzalloc(size: sizeof(struct dmub_replay), GFP_KERNEL); |
369 | |
370 | if (replay == NULL) { |
371 | BREAK_TO_DEBUGGER(); |
372 | return NULL; |
373 | } |
374 | |
375 | dmub_replay_construct(replay, ctx); |
376 | |
377 | return replay; |
378 | } |
379 | |
380 | /* |
381 | * Deallocate Replay object. |
382 | */ |
383 | void dmub_replay_destroy(struct dmub_replay **dmub) |
384 | { |
385 | kfree(objp: *dmub); |
386 | *dmub = NULL; |
387 | } |
388 | |