Commit | Line | Data |
---|---|---|
3a1627b0 NK |
1 | /* |
2 | * Copyright 2019 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" | |
cdca3f21 | 28 | #include "../dmub/dmub_srv.h" |
70732504 | 29 | #include "dm_helpers.h" |
c2fbe663 FC |
30 | #include "dc_hw_types.h" |
31 | #include "core_types.h" | |
fbe43dcd | 32 | #include "../basics/conversion.h" |
b73353f7 | 33 | #include "cursor_reg_cache.h" |
3a1627b0 | 34 | |
ecdfc5c9 NK |
35 | #define CTX dc_dmub_srv->ctx |
36 | #define DC_LOGGER CTX->logger | |
37 | ||
3a1627b0 NK |
38 | static void dc_dmub_srv_construct(struct dc_dmub_srv *dc_srv, struct dc *dc, |
39 | struct dmub_srv *dmub) | |
40 | { | |
41 | dc_srv->dmub = dmub; | |
42 | dc_srv->ctx = dc->ctx; | |
43 | } | |
44 | ||
45 | struct dc_dmub_srv *dc_dmub_srv_create(struct dc *dc, struct dmub_srv *dmub) | |
46 | { | |
47 | struct dc_dmub_srv *dc_srv = | |
48 | kzalloc(sizeof(struct dc_dmub_srv), GFP_KERNEL); | |
49 | ||
50 | if (dc_srv == NULL) { | |
51 | BREAK_TO_DEBUGGER(); | |
52 | return NULL; | |
53 | } | |
54 | ||
55 | dc_dmub_srv_construct(dc_srv, dc, dmub); | |
56 | ||
57 | return dc_srv; | |
58 | } | |
59 | ||
60 | void dc_dmub_srv_destroy(struct dc_dmub_srv **dmub_srv) | |
61 | { | |
62 | if (*dmub_srv) { | |
63 | kfree(*dmub_srv); | |
64 | *dmub_srv = NULL; | |
65 | } | |
66 | } | |
67 | ||
3a1627b0 NK |
68 | void dc_dmub_srv_wait_idle(struct dc_dmub_srv *dc_dmub_srv) |
69 | { | |
70 | struct dmub_srv *dmub = dc_dmub_srv->dmub; | |
71 | struct dc_context *dc_ctx = dc_dmub_srv->ctx; | |
72 | enum dmub_status status; | |
73 | ||
74 | status = dmub_srv_wait_for_idle(dmub, 100000); | |
2631ac1a | 75 | if (status != DMUB_STATUS_OK) { |
3a1627b0 | 76 | DC_ERROR("Error waiting for DMUB idle: status=%d\n", status); |
2631ac1a AT |
77 | dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); |
78 | } | |
3a1627b0 NK |
79 | } |
80 | ||
d493a024 AL |
81 | void dc_dmub_srv_clear_inbox0_ack(struct dc_dmub_srv *dmub_srv) |
82 | { | |
83 | struct dmub_srv *dmub = dmub_srv->dmub; | |
84 | struct dc_context *dc_ctx = dmub_srv->ctx; | |
85 | enum dmub_status status = DMUB_STATUS_OK; | |
86 | ||
87 | status = dmub_srv_clear_inbox0_ack(dmub); | |
88 | if (status != DMUB_STATUS_OK) { | |
89 | DC_ERROR("Error clearing INBOX0 ack: status=%d\n", status); | |
90 | dc_dmub_srv_log_diagnostic_data(dmub_srv); | |
91 | } | |
92 | } | |
93 | ||
94 | void dc_dmub_srv_wait_for_inbox0_ack(struct dc_dmub_srv *dmub_srv) | |
95 | { | |
96 | struct dmub_srv *dmub = dmub_srv->dmub; | |
97 | struct dc_context *dc_ctx = dmub_srv->ctx; | |
98 | enum dmub_status status = DMUB_STATUS_OK; | |
99 | ||
100 | status = dmub_srv_wait_for_inbox0_ack(dmub, 100000); | |
101 | if (status != DMUB_STATUS_OK) { | |
102 | DC_ERROR("Error waiting for INBOX0 HW Lock Ack\n"); | |
103 | dc_dmub_srv_log_diagnostic_data(dmub_srv); | |
104 | } | |
105 | } | |
106 | ||
f2973d2a AL |
107 | void dc_dmub_srv_send_inbox0_cmd(struct dc_dmub_srv *dmub_srv, |
108 | union dmub_inbox0_data_register data) | |
109 | { | |
110 | struct dmub_srv *dmub = dmub_srv->dmub; | |
d493a024 AL |
111 | struct dc_context *dc_ctx = dmub_srv->ctx; |
112 | enum dmub_status status = DMUB_STATUS_OK; | |
113 | ||
114 | status = dmub_srv_send_inbox0_cmd(dmub, data); | |
115 | if (status != DMUB_STATUS_OK) { | |
116 | DC_ERROR("Error sending INBOX0 cmd\n"); | |
117 | dc_dmub_srv_log_diagnostic_data(dmub_srv); | |
118 | } | |
f2973d2a AL |
119 | } |
120 | ||
e97cc04f | 121 | bool dc_dmub_srv_cmd_run(struct dc_dmub_srv *dc_dmub_srv, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) |
ecdfc5c9 | 122 | { |
e97cc04f JP |
123 | return dc_dmub_srv_cmd_run_list(dc_dmub_srv, 1, cmd, wait_type); |
124 | } | |
125 | ||
126 | bool dc_dmub_srv_cmd_run_list(struct dc_dmub_srv *dc_dmub_srv, unsigned int count, union dmub_rb_cmd *cmd_list, enum dm_dmub_wait_type wait_type) | |
127 | { | |
44407010 | 128 | struct dc_context *dc_ctx; |
ecdfc5c9 NK |
129 | struct dmub_srv *dmub; |
130 | enum dmub_status status; | |
e97cc04f | 131 | int i; |
ecdfc5c9 NK |
132 | |
133 | if (!dc_dmub_srv || !dc_dmub_srv->dmub) | |
134 | return false; | |
135 | ||
44407010 | 136 | dc_ctx = dc_dmub_srv->ctx; |
ecdfc5c9 NK |
137 | dmub = dc_dmub_srv->dmub; |
138 | ||
e97cc04f JP |
139 | for (i = 0 ; i < count; i++) { |
140 | // Queue command | |
141 | status = dmub_srv_cmd_queue(dmub, &cmd_list[i]); | |
142 | ||
522b9a5d JP |
143 | if (status == DMUB_STATUS_QUEUE_FULL) { |
144 | /* Execute and wait for queue to become empty again. */ | |
145 | dmub_srv_cmd_execute(dmub); | |
146 | dmub_srv_wait_for_idle(dmub, 100000); | |
147 | ||
148 | /* Requeue the command. */ | |
149 | status = dmub_srv_cmd_queue(dmub, &cmd_list[i]); | |
150 | } | |
151 | ||
e97cc04f JP |
152 | if (status != DMUB_STATUS_OK) { |
153 | DC_ERROR("Error queueing DMUB command: status=%d\n", status); | |
154 | dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); | |
155 | return false; | |
156 | } | |
157 | } | |
158 | ||
159 | status = dmub_srv_cmd_execute(dmub); | |
ecdfc5c9 | 160 | if (status != DMUB_STATUS_OK) { |
e97cc04f JP |
161 | DC_ERROR("Error starting DMUB execution: status=%d\n", status); |
162 | dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); | |
ecdfc5c9 NK |
163 | return false; |
164 | } | |
165 | ||
e97cc04f JP |
166 | // Wait for DMUB to process command |
167 | if (wait_type != DM_DMUB_WAIT_TYPE_NO_WAIT) { | |
168 | status = dmub_srv_wait_for_idle(dmub, 100000); | |
169 | ||
170 | if (status != DMUB_STATUS_OK) { | |
171 | DC_LOG_DEBUG("No reply for DMUB command: status=%d\n", status); | |
172 | return false; | |
173 | } | |
174 | ||
175 | // Copy data back from ring buffer into command | |
176 | if (wait_type == DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) | |
177 | dmub_rb_get_return_data(&dmub->inbox1_rb, cmd_list); | |
178 | } | |
179 | ||
ecdfc5c9 NK |
180 | return true; |
181 | } | |
182 | ||
499e4b1c | 183 | bool dc_dmub_srv_optimized_init_done(struct dc_dmub_srv *dc_dmub_srv) |
3a1627b0 | 184 | { |
499e4b1c | 185 | struct dmub_srv *dmub; |
b94f1cc9 TR |
186 | struct dc_context *dc_ctx; |
187 | union dmub_fw_boot_status boot_status; | |
188 | enum dmub_status status; | |
a6e4da40 | 189 | |
499e4b1c EY |
190 | if (!dc_dmub_srv || !dc_dmub_srv->dmub) |
191 | return false; | |
a6e4da40 | 192 | |
499e4b1c | 193 | dmub = dc_dmub_srv->dmub; |
b94f1cc9 TR |
194 | dc_ctx = dc_dmub_srv->ctx; |
195 | ||
196 | status = dmub_srv_get_fw_boot_status(dmub, &boot_status); | |
197 | if (status != DMUB_STATUS_OK) { | |
198 | DC_ERROR("Error querying DMUB boot status: error=%d\n", status); | |
199 | return false; | |
200 | } | |
a6e4da40 | 201 | |
b94f1cc9 | 202 | return boot_status.bits.optimized_init_done; |
3a1627b0 | 203 | } |
0825d965 EY |
204 | |
205 | bool dc_dmub_srv_notify_stream_mask(struct dc_dmub_srv *dc_dmub_srv, | |
206 | unsigned int stream_mask) | |
207 | { | |
208 | struct dmub_srv *dmub; | |
209 | const uint32_t timeout = 30; | |
210 | ||
211 | if (!dc_dmub_srv || !dc_dmub_srv->dmub) | |
212 | return false; | |
213 | ||
214 | dmub = dc_dmub_srv->dmub; | |
215 | ||
216 | return dmub_srv_send_gpint_command( | |
217 | dmub, DMUB_GPINT__IDLE_OPT_NOTIFY_STREAM_MASK, | |
218 | stream_mask, timeout) == DMUB_STATUS_OK; | |
219 | } | |
8fe44c08 | 220 | |
b04cb192 NK |
221 | bool dc_dmub_srv_is_restore_required(struct dc_dmub_srv *dc_dmub_srv) |
222 | { | |
223 | struct dmub_srv *dmub; | |
224 | struct dc_context *dc_ctx; | |
225 | union dmub_fw_boot_status boot_status; | |
226 | enum dmub_status status; | |
227 | ||
228 | if (!dc_dmub_srv || !dc_dmub_srv->dmub) | |
229 | return false; | |
230 | ||
231 | dmub = dc_dmub_srv->dmub; | |
232 | dc_ctx = dc_dmub_srv->ctx; | |
233 | ||
234 | status = dmub_srv_get_fw_boot_status(dmub, &boot_status); | |
235 | if (status != DMUB_STATUS_OK) { | |
236 | DC_ERROR("Error querying DMUB boot status: error=%d\n", status); | |
237 | return false; | |
238 | } | |
239 | ||
240 | return boot_status.bits.restore_required; | |
241 | } | |
70732504 | 242 | |
6804287b | 243 | bool dc_dmub_srv_get_dmub_outbox0_msg(const struct dc *dc, struct dmcub_trace_buf_entry *entry) |
70732504 YS |
244 | { |
245 | struct dmub_srv *dmub = dc->ctx->dmub_srv->dmub; | |
6804287b | 246 | return dmub_srv_get_outbox0_msg(dmub, entry); |
70732504 YS |
247 | } |
248 | ||
249 | void dc_dmub_trace_event_control(struct dc *dc, bool enable) | |
250 | { | |
81927e28 | 251 | dm_helpers_dmub_outbox_interrupt_control(dc->ctx, enable); |
70732504 | 252 | } |
2631ac1a | 253 | |
00fa7f03 RS |
254 | void dc_dmub_srv_drr_update_cmd(struct dc *dc, uint32_t tg_inst, uint32_t vtotal_min, uint32_t vtotal_max) |
255 | { | |
256 | union dmub_rb_cmd cmd = { 0 }; | |
257 | ||
258 | cmd.drr_update.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; | |
259 | cmd.drr_update.header.sub_type = DMUB_CMD__FAMS_DRR_UPDATE; | |
260 | cmd.drr_update.dmub_optc_state_req.v_total_max = vtotal_max; | |
261 | cmd.drr_update.dmub_optc_state_req.v_total_min = vtotal_min; | |
262 | cmd.drr_update.dmub_optc_state_req.tg_inst = tg_inst; | |
263 | ||
264 | cmd.drr_update.header.payload_bytes = sizeof(cmd.drr_update) - sizeof(cmd.drr_update.header); | |
265 | ||
266 | // Send the command to the DMCUB. | |
e97cc04f | 267 | dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); |
00fa7f03 RS |
268 | } |
269 | ||
319568d7 AL |
270 | void dc_dmub_srv_set_drr_manual_trigger_cmd(struct dc *dc, uint32_t tg_inst) |
271 | { | |
272 | union dmub_rb_cmd cmd = { 0 }; | |
273 | ||
274 | cmd.drr_update.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; | |
9f5171ce | 275 | cmd.drr_update.header.sub_type = DMUB_CMD__FAMS_SET_MANUAL_TRIGGER; |
319568d7 AL |
276 | cmd.drr_update.dmub_optc_state_req.tg_inst = tg_inst; |
277 | ||
278 | cmd.drr_update.header.payload_bytes = sizeof(cmd.drr_update) - sizeof(cmd.drr_update.header); | |
279 | ||
280 | // Send the command to the DMCUB. | |
e97cc04f | 281 | dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); |
319568d7 AL |
282 | } |
283 | ||
84900aee | 284 | static uint8_t dc_dmub_srv_get_pipes_for_stream(struct dc *dc, struct dc_stream_state *stream) |
00fa7f03 RS |
285 | { |
286 | uint8_t pipes = 0; | |
287 | int i = 0; | |
288 | ||
289 | for (i = 0; i < MAX_PIPES; i++) { | |
290 | struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i]; | |
291 | ||
292 | if (pipe->stream == stream && pipe->stream_res.tg) | |
293 | pipes = i; | |
294 | } | |
295 | return pipes; | |
296 | } | |
297 | ||
4ed79308 AL |
298 | static void dc_dmub_srv_populate_fams_pipe_info(struct dc *dc, struct dc_state *context, |
299 | struct pipe_ctx *head_pipe, | |
300 | struct dmub_cmd_fw_assisted_mclk_switch_pipe_data *fams_pipe_data) | |
00fa7f03 | 301 | { |
4ed79308 | 302 | int j; |
b0d58d11 | 303 | int pipe_idx = 0; |
00fa7f03 | 304 | |
b0d58d11 | 305 | fams_pipe_data->pipe_index[pipe_idx++] = head_pipe->plane_res.hubp->inst; |
4ed79308 AL |
306 | for (j = 0; j < dc->res_pool->pipe_count; j++) { |
307 | struct pipe_ctx *split_pipe = &context->res_ctx.pipe_ctx[j]; | |
00fa7f03 | 308 | |
4ed79308 | 309 | if (split_pipe->stream == head_pipe->stream && (split_pipe->top_pipe || split_pipe->prev_odm_pipe)) { |
b0d58d11 | 310 | fams_pipe_data->pipe_index[pipe_idx++] = split_pipe->plane_res.hubp->inst; |
00fa7f03 RS |
311 | } |
312 | } | |
b0d58d11 | 313 | fams_pipe_data->pipe_count = pipe_idx; |
00fa7f03 RS |
314 | } |
315 | ||
316 | bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, struct dc_state *context) | |
317 | { | |
318 | union dmub_rb_cmd cmd = { 0 }; | |
319 | struct dmub_cmd_fw_assisted_mclk_switch_config *config_data = &cmd.fw_assisted_mclk_switch.config_data; | |
4ed79308 | 320 | int i = 0, k = 0; |
00fa7f03 | 321 | int ramp_up_num_steps = 1; // TODO: Ramp is currently disabled. Reenable it. |
45a92f45 | 322 | uint8_t visual_confirm_enabled; |
0289e0ed | 323 | int pipe_idx = 0; |
00fa7f03 RS |
324 | |
325 | if (dc == NULL) | |
326 | return false; | |
327 | ||
45a92f45 | 328 | visual_confirm_enabled = dc->debug.visual_confirm == VISUAL_CONFIRM_FAMS; |
329 | ||
00fa7f03 RS |
330 | // Format command. |
331 | cmd.fw_assisted_mclk_switch.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; | |
332 | cmd.fw_assisted_mclk_switch.header.sub_type = DMUB_CMD__FAMS_SETUP_FW_CTRL; | |
333 | cmd.fw_assisted_mclk_switch.config_data.fams_enabled = should_manage_pstate; | |
334 | cmd.fw_assisted_mclk_switch.config_data.visual_confirm_enabled = visual_confirm_enabled; | |
335 | ||
0289e0ed AL |
336 | if (should_manage_pstate) { |
337 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
338 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
339 | ||
340 | if (!pipe->stream) | |
341 | continue; | |
342 | ||
343 | /* If FAMS is being used to support P-State and there is a stream | |
344 | * that does not use FAMS, we are in an FPO + VActive scenario. | |
345 | * Assign vactive stretch margin in this case. | |
346 | */ | |
347 | if (!pipe->stream->fpo_in_use) { | |
348 | cmd.fw_assisted_mclk_switch.config_data.vactive_stretch_margin_us = dc->debug.fpo_vactive_margin_us; | |
349 | break; | |
350 | } | |
351 | pipe_idx++; | |
352 | } | |
353 | } | |
354 | ||
4ed79308 AL |
355 | for (i = 0, k = 0; context && i < dc->res_pool->pipe_count; i++) { |
356 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
00fa7f03 | 357 | |
4ed79308 AL |
358 | if (!pipe->top_pipe && !pipe->prev_odm_pipe && pipe->stream && pipe->stream->fpo_in_use) { |
359 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
360 | uint8_t min_refresh_in_hz = (pipe->stream->timing.min_refresh_in_uhz + 999999) / 1000000; | |
00fa7f03 | 361 | |
4ed79308 AL |
362 | config_data->pipe_data[k].pix_clk_100hz = pipe->stream->timing.pix_clk_100hz; |
363 | config_data->pipe_data[k].min_refresh_in_hz = min_refresh_in_hz; | |
364 | config_data->pipe_data[k].max_ramp_step = ramp_up_num_steps; | |
365 | config_data->pipe_data[k].pipes = dc_dmub_srv_get_pipes_for_stream(dc, pipe->stream); | |
366 | dc_dmub_srv_populate_fams_pipe_info(dc, context, pipe, &config_data->pipe_data[k]); | |
367 | k++; | |
368 | } | |
369 | } | |
00fa7f03 RS |
370 | cmd.fw_assisted_mclk_switch.header.payload_bytes = |
371 | sizeof(cmd.fw_assisted_mclk_switch) - sizeof(cmd.fw_assisted_mclk_switch.header); | |
372 | ||
373 | // Send the command to the DMCUB. | |
e97cc04f | 374 | dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); |
00fa7f03 RS |
375 | |
376 | return true; | |
377 | } | |
378 | ||
e97cc04f | 379 | void dc_dmub_srv_query_caps_cmd(struct dc_dmub_srv *dc_dmub_srv) |
ac2e555e AP |
380 | { |
381 | union dmub_rb_cmd cmd = { 0 }; | |
ac2e555e AP |
382 | |
383 | memset(&cmd, 0, sizeof(cmd)); | |
384 | ||
385 | /* Prepare fw command */ | |
386 | cmd.query_feature_caps.header.type = DMUB_CMD__QUERY_FEATURE_CAPS; | |
387 | cmd.query_feature_caps.header.sub_type = 0; | |
388 | cmd.query_feature_caps.header.ret_status = 1; | |
389 | cmd.query_feature_caps.header.payload_bytes = sizeof(struct dmub_cmd_query_feature_caps_data); | |
390 | ||
ac2e555e | 391 | /* If command was processed, copy feature caps to dmub srv */ |
e97cc04f | 392 | if (dm_execute_dmub_cmd(dc_dmub_srv->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && |
ac2e555e | 393 | cmd.query_feature_caps.header.ret_status == 0) { |
e97cc04f | 394 | memcpy(&dc_dmub_srv->dmub->feature_caps, |
ac2e555e AP |
395 | &cmd.query_feature_caps.query_feature_caps_data, |
396 | sizeof(struct dmub_feature_caps)); | |
397 | } | |
398 | } | |
399 | ||
b09c1fff LHM |
400 | void dc_dmub_srv_get_visual_confirm_color_cmd(struct dc *dc, struct pipe_ctx *pipe_ctx) |
401 | { | |
402 | union dmub_rb_cmd cmd = { 0 }; | |
b09c1fff LHM |
403 | unsigned int panel_inst = 0; |
404 | ||
405 | dc_get_edp_link_panel_inst(dc, pipe_ctx->stream->link, &panel_inst); | |
406 | ||
407 | memset(&cmd, 0, sizeof(cmd)); | |
408 | ||
409 | // Prepare fw command | |
410 | cmd.visual_confirm_color.header.type = DMUB_CMD__GET_VISUAL_CONFIRM_COLOR; | |
411 | cmd.visual_confirm_color.header.sub_type = 0; | |
412 | cmd.visual_confirm_color.header.ret_status = 1; | |
413 | cmd.visual_confirm_color.header.payload_bytes = sizeof(struct dmub_cmd_visual_confirm_color_data); | |
414 | cmd.visual_confirm_color.visual_confirm_color_data.visual_confirm_color.panel_inst = panel_inst; | |
415 | ||
b09c1fff | 416 | // If command was processed, copy feature caps to dmub srv |
e97cc04f | 417 | if (dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && |
b09c1fff LHM |
418 | cmd.visual_confirm_color.header.ret_status == 0) { |
419 | memcpy(&dc->ctx->dmub_srv->dmub->visual_confirm_color, | |
420 | &cmd.visual_confirm_color.visual_confirm_color_data, | |
421 | sizeof(struct dmub_visual_confirm_color)); | |
422 | } | |
423 | } | |
424 | ||
85f4bc0c | 425 | /** |
6be153dc | 426 | * populate_subvp_cmd_drr_info - Helper to populate DRR pipe info for the DMCUB subvp command |
85f4bc0c | 427 | * |
6be153dc RS |
428 | * @dc: [in] current dc state |
429 | * @subvp_pipe: [in] pipe_ctx for the SubVP pipe | |
430 | * @vblank_pipe: [in] pipe_ctx for the DRR pipe | |
431 | * @pipe_data: [in] Pipe data which stores the VBLANK/DRR info | |
432 | * | |
433 | * Populate the DMCUB SubVP command with DRR pipe info. All the information | |
434 | * required for calculating the SubVP + DRR microschedule is populated here. | |
85f4bc0c AL |
435 | * |
436 | * High level algorithm: | |
437 | * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe | |
438 | * 2. Calculate the min and max vtotal which supports SubVP + DRR microschedule | |
439 | * 3. Populate the drr_info with the min and max supported vtotal values | |
85f4bc0c AL |
440 | */ |
441 | static void populate_subvp_cmd_drr_info(struct dc *dc, | |
442 | struct pipe_ctx *subvp_pipe, | |
443 | struct pipe_ctx *vblank_pipe, | |
444 | struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data) | |
445 | { | |
446 | struct dc_crtc_timing *main_timing = &subvp_pipe->stream->timing; | |
447 | struct dc_crtc_timing *phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing; | |
448 | struct dc_crtc_timing *drr_timing = &vblank_pipe->stream->timing; | |
410e7474 AL |
449 | uint16_t drr_frame_us = 0; |
450 | uint16_t min_drr_supported_us = 0; | |
451 | uint16_t max_drr_supported_us = 0; | |
452 | uint16_t max_drr_vblank_us = 0; | |
453 | uint16_t max_drr_mallregion_us = 0; | |
454 | uint16_t mall_region_us = 0; | |
455 | uint16_t prefetch_us = 0; | |
456 | uint16_t subvp_active_us = 0; | |
457 | uint16_t drr_active_us = 0; | |
458 | uint16_t min_vtotal_supported = 0; | |
459 | uint16_t max_vtotal_supported = 0; | |
85f4bc0c AL |
460 | |
461 | pipe_data->pipe_config.vblank_data.drr_info.drr_in_use = true; | |
462 | pipe_data->pipe_config.vblank_data.drr_info.use_ramping = false; // for now don't use ramping | |
463 | pipe_data->pipe_config.vblank_data.drr_info.drr_window_size_ms = 4; // hardcode 4ms DRR window for now | |
464 | ||
410e7474 AL |
465 | drr_frame_us = div64_u64(((uint64_t)drr_timing->v_total * drr_timing->h_total * 1000000), |
466 | (((uint64_t)drr_timing->pix_clk_100hz * 100))); | |
85f4bc0c | 467 | // P-State allow width and FW delays already included phantom_timing->v_addressable |
410e7474 AL |
468 | mall_region_us = div64_u64(((uint64_t)phantom_timing->v_addressable * phantom_timing->h_total * 1000000), |
469 | (((uint64_t)phantom_timing->pix_clk_100hz * 100))); | |
85f4bc0c | 470 | min_drr_supported_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US; |
410e7474 AL |
471 | min_vtotal_supported = div64_u64(((uint64_t)drr_timing->pix_clk_100hz * 100 * min_drr_supported_us), |
472 | (((uint64_t)drr_timing->h_total * 1000000))); | |
473 | ||
474 | prefetch_us = div64_u64(((uint64_t)(phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total * 1000000), | |
475 | (((uint64_t)phantom_timing->pix_clk_100hz * 100) + dc->caps.subvp_prefetch_end_to_mall_start_us)); | |
476 | subvp_active_us = div64_u64(((uint64_t)main_timing->v_addressable * main_timing->h_total * 1000000), | |
477 | (((uint64_t)main_timing->pix_clk_100hz * 100))); | |
478 | drr_active_us = div64_u64(((uint64_t)drr_timing->v_addressable * drr_timing->h_total * 1000000), | |
479 | (((uint64_t)drr_timing->pix_clk_100hz * 100))); | |
964d6416 AL |
480 | max_drr_vblank_us = div64_u64((subvp_active_us - prefetch_us - |
481 | dc->caps.subvp_fw_processing_delay_us - drr_active_us), 2) + drr_active_us; | |
482 | max_drr_mallregion_us = subvp_active_us - prefetch_us - mall_region_us - dc->caps.subvp_fw_processing_delay_us; | |
85f4bc0c | 483 | max_drr_supported_us = max_drr_vblank_us > max_drr_mallregion_us ? max_drr_vblank_us : max_drr_mallregion_us; |
410e7474 AL |
484 | max_vtotal_supported = div64_u64(((uint64_t)drr_timing->pix_clk_100hz * 100 * max_drr_supported_us), |
485 | (((uint64_t)drr_timing->h_total * 1000000))); | |
85f4bc0c | 486 | |
964d6416 AL |
487 | /* When calculating the max vtotal supported for SubVP + DRR cases, add |
488 | * margin due to possible rounding errors (being off by 1 line in the | |
489 | * FW calculation can incorrectly push the P-State switch to wait 1 frame | |
490 | * longer). | |
491 | */ | |
492 | max_vtotal_supported = max_vtotal_supported - dc->caps.subvp_drr_max_vblank_margin_us; | |
493 | ||
85f4bc0c AL |
494 | pipe_data->pipe_config.vblank_data.drr_info.min_vtotal_supported = min_vtotal_supported; |
495 | pipe_data->pipe_config.vblank_data.drr_info.max_vtotal_supported = max_vtotal_supported; | |
ae7169a9 | 496 | pipe_data->pipe_config.vblank_data.drr_info.drr_vblank_start_margin = dc->caps.subvp_drr_vblank_start_margin_us; |
85f4bc0c AL |
497 | } |
498 | ||
499 | /** | |
6be153dc | 500 | * populate_subvp_cmd_vblank_pipe_info - Helper to populate VBLANK pipe info for the DMUB subvp command |
85f4bc0c | 501 | * |
6be153dc RS |
502 | * @dc: [in] current dc state |
503 | * @context: [in] new dc state | |
504 | * @cmd: [in] DMUB cmd to be populated with SubVP info | |
505 | * @vblank_pipe: [in] pipe_ctx for the VBLANK pipe | |
506 | * @cmd_pipe_index: [in] index for the pipe array in DMCUB SubVP cmd | |
85f4bc0c | 507 | * |
6be153dc RS |
508 | * Populate the DMCUB SubVP command with VBLANK pipe info. All the information |
509 | * required to calculate the microschedule for SubVP + VBLANK case is stored in | |
510 | * the pipe_data (subvp_data and vblank_data). Also check if the VBLANK pipe | |
511 | * is a DRR display -- if it is make a call to populate drr_info. | |
85f4bc0c AL |
512 | */ |
513 | static void populate_subvp_cmd_vblank_pipe_info(struct dc *dc, | |
514 | struct dc_state *context, | |
515 | union dmub_rb_cmd *cmd, | |
516 | struct pipe_ctx *vblank_pipe, | |
517 | uint8_t cmd_pipe_index) | |
518 | { | |
519 | uint32_t i; | |
520 | struct pipe_ctx *pipe = NULL; | |
521 | struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data = | |
522 | &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[cmd_pipe_index]; | |
523 | ||
524 | // Find the SubVP pipe | |
525 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
526 | pipe = &context->res_ctx.pipe_ctx[i]; | |
527 | ||
528 | // We check for master pipe, but it shouldn't matter since we only need | |
529 | // the pipe for timing info (stream should be same for any pipe splits) | |
530 | if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe) | |
531 | continue; | |
532 | ||
533 | // Find the SubVP pipe | |
534 | if (pipe->stream->mall_stream_config.type == SUBVP_MAIN) | |
535 | break; | |
536 | } | |
537 | ||
538 | pipe_data->mode = VBLANK; | |
539 | pipe_data->pipe_config.vblank_data.pix_clk_100hz = vblank_pipe->stream->timing.pix_clk_100hz; | |
540 | pipe_data->pipe_config.vblank_data.vblank_start = vblank_pipe->stream->timing.v_total - | |
541 | vblank_pipe->stream->timing.v_front_porch; | |
542 | pipe_data->pipe_config.vblank_data.vtotal = vblank_pipe->stream->timing.v_total; | |
543 | pipe_data->pipe_config.vblank_data.htotal = vblank_pipe->stream->timing.h_total; | |
544 | pipe_data->pipe_config.vblank_data.vblank_pipe_index = vblank_pipe->pipe_idx; | |
545 | pipe_data->pipe_config.vblank_data.vstartup_start = vblank_pipe->pipe_dlg_param.vstartup_start; | |
546 | pipe_data->pipe_config.vblank_data.vblank_end = | |
547 | vblank_pipe->stream->timing.v_total - vblank_pipe->stream->timing.v_front_porch - vblank_pipe->stream->timing.v_addressable; | |
548 | ||
549 | if (vblank_pipe->stream->ignore_msa_timing_param) | |
550 | populate_subvp_cmd_drr_info(dc, pipe, vblank_pipe, pipe_data); | |
551 | } | |
552 | ||
553 | /** | |
6be153dc | 554 | * update_subvp_prefetch_end_to_mall_start - Helper for SubVP + SubVP case |
85f4bc0c | 555 | * |
6be153dc RS |
556 | * @dc: [in] current dc state |
557 | * @context: [in] new dc state | |
558 | * @cmd: [in] DMUB cmd to be populated with SubVP info | |
559 | * @subvp_pipes: [in] Array of SubVP pipes (should always be length 2) | |
85f4bc0c | 560 | * |
6be153dc RS |
561 | * For SubVP + SubVP, we use a single vertical interrupt to start the |
562 | * microschedule for both SubVP pipes. In order for this to work correctly, the | |
563 | * MALL REGION of both SubVP pipes must start at the same time. This function | |
564 | * lengthens the prefetch end to mall start delay of the SubVP pipe that has | |
565 | * the shorter prefetch so that both MALL REGION's will start at the same time. | |
85f4bc0c AL |
566 | */ |
567 | static void update_subvp_prefetch_end_to_mall_start(struct dc *dc, | |
568 | struct dc_state *context, | |
569 | union dmub_rb_cmd *cmd, | |
570 | struct pipe_ctx *subvp_pipes[]) | |
571 | { | |
572 | uint32_t subvp0_prefetch_us = 0; | |
573 | uint32_t subvp1_prefetch_us = 0; | |
574 | uint32_t prefetch_delta_us = 0; | |
575 | struct dc_crtc_timing *phantom_timing0 = &subvp_pipes[0]->stream->mall_stream_config.paired_stream->timing; | |
576 | struct dc_crtc_timing *phantom_timing1 = &subvp_pipes[1]->stream->mall_stream_config.paired_stream->timing; | |
577 | struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data = NULL; | |
578 | ||
410e7474 AL |
579 | subvp0_prefetch_us = div64_u64(((uint64_t)(phantom_timing0->v_total - phantom_timing0->v_front_porch) * |
580 | (uint64_t)phantom_timing0->h_total * 1000000), | |
581 | (((uint64_t)phantom_timing0->pix_clk_100hz * 100) + dc->caps.subvp_prefetch_end_to_mall_start_us)); | |
582 | subvp1_prefetch_us = div64_u64(((uint64_t)(phantom_timing1->v_total - phantom_timing1->v_front_porch) * | |
583 | (uint64_t)phantom_timing1->h_total * 1000000), | |
584 | (((uint64_t)phantom_timing1->pix_clk_100hz * 100) + dc->caps.subvp_prefetch_end_to_mall_start_us)); | |
85f4bc0c AL |
585 | |
586 | // Whichever SubVP PIPE has the smaller prefetch (including the prefetch end to mall start time) | |
587 | // should increase it's prefetch time to match the other | |
588 | if (subvp0_prefetch_us > subvp1_prefetch_us) { | |
589 | pipe_data = &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[1]; | |
590 | prefetch_delta_us = subvp0_prefetch_us - subvp1_prefetch_us; | |
591 | pipe_data->pipe_config.subvp_data.prefetch_to_mall_start_lines = | |
410e7474 AL |
592 | div64_u64(((uint64_t)(dc->caps.subvp_prefetch_end_to_mall_start_us + prefetch_delta_us) * |
593 | ((uint64_t)phantom_timing1->pix_clk_100hz * 100) + ((uint64_t)phantom_timing1->h_total * 1000000 - 1)), | |
594 | ((uint64_t)phantom_timing1->h_total * 1000000)); | |
595 | ||
85f4bc0c AL |
596 | } else if (subvp1_prefetch_us > subvp0_prefetch_us) { |
597 | pipe_data = &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[0]; | |
598 | prefetch_delta_us = subvp1_prefetch_us - subvp0_prefetch_us; | |
599 | pipe_data->pipe_config.subvp_data.prefetch_to_mall_start_lines = | |
410e7474 AL |
600 | div64_u64(((uint64_t)(dc->caps.subvp_prefetch_end_to_mall_start_us + prefetch_delta_us) * |
601 | ((uint64_t)phantom_timing0->pix_clk_100hz * 100) + ((uint64_t)phantom_timing0->h_total * 1000000 - 1)), | |
602 | ((uint64_t)phantom_timing0->h_total * 1000000)); | |
85f4bc0c AL |
603 | } |
604 | } | |
605 | ||
606 | /** | |
0645b7a6 | 607 | * populate_subvp_cmd_pipe_info - Helper to populate the SubVP pipe info for the DMUB subvp command |
85f4bc0c | 608 | * |
6be153dc RS |
609 | * @dc: [in] current dc state |
610 | * @context: [in] new dc state | |
611 | * @cmd: [in] DMUB cmd to be populated with SubVP info | |
612 | * @subvp_pipe: [in] pipe_ctx for the SubVP pipe | |
613 | * @cmd_pipe_index: [in] index for the pipe array in DMCUB SubVP cmd | |
85f4bc0c | 614 | * |
6be153dc RS |
615 | * Populate the DMCUB SubVP command with SubVP pipe info. All the information |
616 | * required to calculate the microschedule for the SubVP pipe is stored in the | |
617 | * pipe_data of the DMCUB SubVP command. | |
85f4bc0c AL |
618 | */ |
619 | static void populate_subvp_cmd_pipe_info(struct dc *dc, | |
620 | struct dc_state *context, | |
621 | union dmub_rb_cmd *cmd, | |
622 | struct pipe_ctx *subvp_pipe, | |
623 | uint8_t cmd_pipe_index) | |
624 | { | |
625 | uint32_t j; | |
626 | struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data = | |
627 | &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[cmd_pipe_index]; | |
628 | struct dc_crtc_timing *main_timing = &subvp_pipe->stream->timing; | |
629 | struct dc_crtc_timing *phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing; | |
7857825b | 630 | uint32_t out_num_stream, out_den_stream, out_num_plane, out_den_plane, out_num, out_den; |
85f4bc0c AL |
631 | |
632 | pipe_data->mode = SUBVP; | |
633 | pipe_data->pipe_config.subvp_data.pix_clk_100hz = subvp_pipe->stream->timing.pix_clk_100hz; | |
634 | pipe_data->pipe_config.subvp_data.htotal = subvp_pipe->stream->timing.h_total; | |
635 | pipe_data->pipe_config.subvp_data.vtotal = subvp_pipe->stream->timing.v_total; | |
636 | pipe_data->pipe_config.subvp_data.main_vblank_start = | |
637 | main_timing->v_total - main_timing->v_front_porch; | |
638 | pipe_data->pipe_config.subvp_data.main_vblank_end = | |
639 | main_timing->v_total - main_timing->v_front_porch - main_timing->v_addressable; | |
640 | pipe_data->pipe_config.subvp_data.mall_region_lines = phantom_timing->v_addressable; | |
7da2bcda | 641 | pipe_data->pipe_config.subvp_data.main_pipe_index = subvp_pipe->stream_res.tg->inst; |
9f5171ce | 642 | pipe_data->pipe_config.subvp_data.is_drr = subvp_pipe->stream->ignore_msa_timing_param; |
85f4bc0c | 643 | |
fbe43dcd AL |
644 | /* Calculate the scaling factor from the src and dst height. |
645 | * e.g. If 3840x2160 being downscaled to 1920x1080, the scaling factor is 1/2. | |
646 | * Reduce the fraction 1080/2160 = 1/2 for the "scaling factor" | |
7857825b AL |
647 | * |
648 | * Make sure to combine stream and plane scaling together. | |
fbe43dcd | 649 | */ |
7857825b AL |
650 | reduce_fraction(subvp_pipe->stream->src.height, subvp_pipe->stream->dst.height, |
651 | &out_num_stream, &out_den_stream); | |
652 | reduce_fraction(subvp_pipe->plane_state->src_rect.height, subvp_pipe->plane_state->dst_rect.height, | |
653 | &out_num_plane, &out_den_plane); | |
654 | reduce_fraction(out_num_stream * out_num_plane, out_den_stream * out_den_plane, &out_num, &out_den); | |
74f4e84d AL |
655 | pipe_data->pipe_config.subvp_data.scale_factor_numerator = out_num; |
656 | pipe_data->pipe_config.subvp_data.scale_factor_denominator = out_den; | |
fbe43dcd | 657 | |
85f4bc0c AL |
658 | // Prefetch lines is equal to VACTIVE + BP + VSYNC |
659 | pipe_data->pipe_config.subvp_data.prefetch_lines = | |
660 | phantom_timing->v_total - phantom_timing->v_front_porch; | |
661 | ||
662 | // Round up | |
663 | pipe_data->pipe_config.subvp_data.prefetch_to_mall_start_lines = | |
410e7474 AL |
664 | div64_u64(((uint64_t)dc->caps.subvp_prefetch_end_to_mall_start_us * ((uint64_t)phantom_timing->pix_clk_100hz * 100) + |
665 | ((uint64_t)phantom_timing->h_total * 1000000 - 1)), ((uint64_t)phantom_timing->h_total * 1000000)); | |
85f4bc0c | 666 | pipe_data->pipe_config.subvp_data.processing_delay_lines = |
410e7474 AL |
667 | div64_u64(((uint64_t)(dc->caps.subvp_fw_processing_delay_us) * ((uint64_t)phantom_timing->pix_clk_100hz * 100) + |
668 | ((uint64_t)phantom_timing->h_total * 1000000 - 1)), ((uint64_t)phantom_timing->h_total * 1000000)); | |
b0d6de32 | 669 | |
b0d6de32 AL |
670 | if (subvp_pipe->bottom_pipe) { |
671 | pipe_data->pipe_config.subvp_data.main_split_pipe_index = subvp_pipe->bottom_pipe->pipe_idx; | |
672 | } else if (subvp_pipe->next_odm_pipe) { | |
673 | pipe_data->pipe_config.subvp_data.main_split_pipe_index = subvp_pipe->next_odm_pipe->pipe_idx; | |
674 | } else { | |
675 | pipe_data->pipe_config.subvp_data.main_split_pipe_index = 0; | |
676 | } | |
b0d6de32 | 677 | |
85f4bc0c AL |
678 | // Find phantom pipe index based on phantom stream |
679 | for (j = 0; j < dc->res_pool->pipe_count; j++) { | |
680 | struct pipe_ctx *phantom_pipe = &context->res_ctx.pipe_ctx[j]; | |
681 | ||
682 | if (phantom_pipe->stream == subvp_pipe->stream->mall_stream_config.paired_stream) { | |
7da2bcda | 683 | pipe_data->pipe_config.subvp_data.phantom_pipe_index = phantom_pipe->stream_res.tg->inst; |
b0d6de32 | 684 | if (phantom_pipe->bottom_pipe) { |
7da2bcda | 685 | pipe_data->pipe_config.subvp_data.phantom_split_pipe_index = phantom_pipe->bottom_pipe->plane_res.hubp->inst; |
b0d6de32 | 686 | } else if (phantom_pipe->next_odm_pipe) { |
7da2bcda | 687 | pipe_data->pipe_config.subvp_data.phantom_split_pipe_index = phantom_pipe->next_odm_pipe->plane_res.hubp->inst; |
b0d6de32 AL |
688 | } else { |
689 | pipe_data->pipe_config.subvp_data.phantom_split_pipe_index = 0; | |
690 | } | |
85f4bc0c AL |
691 | break; |
692 | } | |
693 | } | |
694 | } | |
695 | ||
696 | /** | |
6be153dc | 697 | * dc_dmub_setup_subvp_dmub_command - Populate the DMCUB SubVP command |
85f4bc0c | 698 | * |
6be153dc RS |
699 | * @dc: [in] current dc state |
700 | * @context: [in] new dc state | |
01543dcf | 701 | * @enable: [in] if true enables the pipes population |
85f4bc0c | 702 | * |
6be153dc RS |
703 | * This function loops through each pipe and populates the DMUB SubVP CMD info |
704 | * based on the pipe (e.g. SubVP, VBLANK). | |
85f4bc0c AL |
705 | */ |
706 | void dc_dmub_setup_subvp_dmub_command(struct dc *dc, | |
707 | struct dc_state *context, | |
708 | bool enable) | |
709 | { | |
710 | uint8_t cmd_pipe_index = 0; | |
711 | uint32_t i, pipe_idx; | |
712 | uint8_t subvp_count = 0; | |
713 | union dmub_rb_cmd cmd; | |
714 | struct pipe_ctx *subvp_pipes[2]; | |
715 | uint32_t wm_val_refclk = 0; | |
716 | ||
717 | memset(&cmd, 0, sizeof(cmd)); | |
718 | // FW command for SUBVP | |
719 | cmd.fw_assisted_mclk_switch_v2.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; | |
720 | cmd.fw_assisted_mclk_switch_v2.header.sub_type = DMUB_CMD__HANDLE_SUBVP_CMD; | |
721 | cmd.fw_assisted_mclk_switch_v2.header.payload_bytes = | |
722 | sizeof(cmd.fw_assisted_mclk_switch_v2) - sizeof(cmd.fw_assisted_mclk_switch_v2.header); | |
723 | ||
724 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
725 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
726 | ||
727 | if (!pipe->stream) | |
728 | continue; | |
729 | ||
b0d6de32 AL |
730 | /* For SubVP pipe count, only count the top most (ODM / MPC) pipe |
731 | */ | |
732 | if (pipe->plane_state && !pipe->top_pipe && !pipe->prev_odm_pipe && | |
85f4bc0c AL |
733 | pipe->stream->mall_stream_config.type == SUBVP_MAIN) |
734 | subvp_pipes[subvp_count++] = pipe; | |
735 | } | |
736 | ||
737 | if (enable) { | |
738 | // For each pipe that is a "main" SUBVP pipe, fill in pipe data for DMUB SUBVP cmd | |
739 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
740 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
741 | ||
742 | if (!pipe->stream) | |
743 | continue; | |
744 | ||
b0d6de32 AL |
745 | /* When populating subvp cmd info, only pass in the top most (ODM / MPC) pipe. |
746 | * Any ODM or MPC splits being used in SubVP will be handled internally in | |
747 | * populate_subvp_cmd_pipe_info | |
748 | */ | |
85f4bc0c | 749 | if (pipe->plane_state && pipe->stream->mall_stream_config.paired_stream && |
b0d6de32 | 750 | !pipe->top_pipe && !pipe->prev_odm_pipe && |
85f4bc0c AL |
751 | pipe->stream->mall_stream_config.type == SUBVP_MAIN) { |
752 | populate_subvp_cmd_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++); | |
9bb10b7a AG |
753 | } else if (pipe->plane_state && pipe->stream->mall_stream_config.type == SUBVP_NONE && |
754 | !pipe->top_pipe && !pipe->prev_odm_pipe) { | |
85f4bc0c AL |
755 | // Don't need to check for ActiveDRAMClockChangeMargin < 0, not valid in cases where |
756 | // we run through DML without calculating "natural" P-state support | |
757 | populate_subvp_cmd_vblank_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++); | |
758 | ||
759 | } | |
760 | pipe_idx++; | |
761 | } | |
762 | if (subvp_count == 2) { | |
763 | update_subvp_prefetch_end_to_mall_start(dc, context, &cmd, subvp_pipes); | |
764 | } | |
765 | cmd.fw_assisted_mclk_switch_v2.config_data.pstate_allow_width_us = dc->caps.subvp_pstate_allow_width_us; | |
766 | cmd.fw_assisted_mclk_switch_v2.config_data.vertical_int_margin_us = dc->caps.subvp_vertical_int_margin_us; | |
767 | ||
768 | // Store the original watermark value for this SubVP config so we can lower it when the | |
769 | // MCLK switch starts | |
770 | wm_val_refclk = context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns * | |
97997023 | 771 | (dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000) / 1000; |
85f4bc0c AL |
772 | |
773 | cmd.fw_assisted_mclk_switch_v2.config_data.watermark_a_cache = wm_val_refclk < 0xFFFF ? wm_val_refclk : 0xFFFF; | |
774 | } | |
e97cc04f JP |
775 | |
776 | dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); | |
85f4bc0c AL |
777 | } |
778 | ||
2631ac1a AT |
779 | bool dc_dmub_srv_get_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv, struct dmub_diagnostic_data *diag_data) |
780 | { | |
781 | if (!dc_dmub_srv || !dc_dmub_srv->dmub || !diag_data) | |
782 | return false; | |
783 | return dmub_srv_get_diagnostic_data(dc_dmub_srv->dmub, diag_data); | |
784 | } | |
785 | ||
786 | void dc_dmub_srv_log_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv) | |
787 | { | |
788 | struct dmub_diagnostic_data diag_data = {0}; | |
789 | ||
790 | if (!dc_dmub_srv || !dc_dmub_srv->dmub) { | |
791 | DC_LOG_ERROR("%s: invalid parameters.", __func__); | |
792 | return; | |
793 | } | |
794 | ||
795 | if (!dc_dmub_srv_get_diagnostic_data(dc_dmub_srv, &diag_data)) { | |
796 | DC_LOG_ERROR("%s: dc_dmub_srv_get_diagnostic_data failed.", __func__); | |
797 | return; | |
798 | } | |
799 | ||
800 | DC_LOG_DEBUG( | |
801 | "DMCUB STATE\n" | |
802 | " dmcub_version : %08x\n" | |
803 | " scratch [0] : %08x\n" | |
804 | " scratch [1] : %08x\n" | |
805 | " scratch [2] : %08x\n" | |
806 | " scratch [3] : %08x\n" | |
807 | " scratch [4] : %08x\n" | |
808 | " scratch [5] : %08x\n" | |
809 | " scratch [6] : %08x\n" | |
810 | " scratch [7] : %08x\n" | |
811 | " scratch [8] : %08x\n" | |
812 | " scratch [9] : %08x\n" | |
813 | " scratch [10] : %08x\n" | |
814 | " scratch [11] : %08x\n" | |
815 | " scratch [12] : %08x\n" | |
816 | " scratch [13] : %08x\n" | |
817 | " scratch [14] : %08x\n" | |
818 | " scratch [15] : %08x\n" | |
819 | " pc : %08x\n" | |
820 | " unk_fault_addr : %08x\n" | |
821 | " inst_fault_addr : %08x\n" | |
822 | " data_fault_addr : %08x\n" | |
823 | " inbox1_rptr : %08x\n" | |
824 | " inbox1_wptr : %08x\n" | |
825 | " inbox1_size : %08x\n" | |
826 | " inbox0_rptr : %08x\n" | |
827 | " inbox0_wptr : %08x\n" | |
828 | " inbox0_size : %08x\n" | |
829 | " is_enabled : %d\n" | |
830 | " is_soft_reset : %d\n" | |
831 | " is_secure_reset : %d\n" | |
832 | " is_traceport_en : %d\n" | |
833 | " is_cw0_en : %d\n" | |
834 | " is_cw6_en : %d\n", | |
835 | diag_data.dmcub_version, | |
836 | diag_data.scratch[0], | |
837 | diag_data.scratch[1], | |
838 | diag_data.scratch[2], | |
839 | diag_data.scratch[3], | |
840 | diag_data.scratch[4], | |
841 | diag_data.scratch[5], | |
842 | diag_data.scratch[6], | |
843 | diag_data.scratch[7], | |
844 | diag_data.scratch[8], | |
845 | diag_data.scratch[9], | |
846 | diag_data.scratch[10], | |
847 | diag_data.scratch[11], | |
848 | diag_data.scratch[12], | |
849 | diag_data.scratch[13], | |
850 | diag_data.scratch[14], | |
851 | diag_data.scratch[15], | |
852 | diag_data.pc, | |
853 | diag_data.undefined_address_fault_addr, | |
854 | diag_data.inst_fetch_fault_addr, | |
855 | diag_data.data_write_fault_addr, | |
856 | diag_data.inbox1_rptr, | |
857 | diag_data.inbox1_wptr, | |
858 | diag_data.inbox1_size, | |
859 | diag_data.inbox0_rptr, | |
860 | diag_data.inbox0_wptr, | |
861 | diag_data.inbox0_size, | |
862 | diag_data.is_dmcub_enabled, | |
863 | diag_data.is_dmcub_soft_reset, | |
864 | diag_data.is_dmcub_secure_reset, | |
865 | diag_data.is_traceport_en, | |
866 | diag_data.is_cw0_enabled, | |
867 | diag_data.is_cw6_enabled); | |
868 | } | |
b73353f7 | 869 | |
f7085cbf MT |
870 | static bool dc_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx) |
871 | { | |
872 | struct pipe_ctx *test_pipe, *split_pipe; | |
873 | const struct scaler_data *scl_data = &pipe_ctx->plane_res.scl_data; | |
874 | struct rect r1 = scl_data->recout, r2, r2_half; | |
875 | int r1_r = r1.x + r1.width, r1_b = r1.y + r1.height, r2_r, r2_b; | |
876 | int cur_layer = pipe_ctx->plane_state->layer_index; | |
877 | ||
878 | /** | |
879 | * Disable the cursor if there's another pipe above this with a | |
880 | * plane that contains this pipe's viewport to prevent double cursor | |
881 | * and incorrect scaling artifacts. | |
882 | */ | |
883 | for (test_pipe = pipe_ctx->top_pipe; test_pipe; | |
884 | test_pipe = test_pipe->top_pipe) { | |
885 | // Skip invisible layer and pipe-split plane on same layer | |
886 | if (!test_pipe->plane_state->visible || test_pipe->plane_state->layer_index == cur_layer) | |
887 | continue; | |
888 | ||
889 | r2 = test_pipe->plane_res.scl_data.recout; | |
890 | r2_r = r2.x + r2.width; | |
891 | r2_b = r2.y + r2.height; | |
892 | split_pipe = test_pipe; | |
893 | ||
894 | /** | |
895 | * There is another half plane on same layer because of | |
896 | * pipe-split, merge together per same height. | |
897 | */ | |
898 | for (split_pipe = pipe_ctx->top_pipe; split_pipe; | |
899 | split_pipe = split_pipe->top_pipe) | |
900 | if (split_pipe->plane_state->layer_index == test_pipe->plane_state->layer_index) { | |
901 | r2_half = split_pipe->plane_res.scl_data.recout; | |
902 | r2.x = (r2_half.x < r2.x) ? r2_half.x : r2.x; | |
903 | r2.width = r2.width + r2_half.width; | |
904 | r2_r = r2.x + r2.width; | |
905 | break; | |
906 | } | |
907 | ||
908 | if (r1.x >= r2.x && r1.y >= r2.y && r1_r <= r2_r && r1_b <= r2_b) | |
909 | return true; | |
910 | } | |
911 | ||
912 | return false; | |
913 | } | |
914 | ||
b73353f7 MT |
915 | static bool dc_dmub_should_update_cursor_data(struct pipe_ctx *pipe_ctx) |
916 | { | |
917 | if (pipe_ctx->plane_state != NULL) { | |
918 | if (pipe_ctx->plane_state->address.type == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE) | |
919 | return false; | |
f7085cbf MT |
920 | |
921 | if (dc_can_pipe_disable_cursor(pipe_ctx)) | |
922 | return false; | |
b73353f7 MT |
923 | } |
924 | ||
925 | if ((pipe_ctx->stream->link->psr_settings.psr_version == DC_PSR_VERSION_SU_1 || | |
926 | pipe_ctx->stream->link->psr_settings.psr_version == DC_PSR_VERSION_1) && | |
927 | pipe_ctx->stream->ctx->dce_version >= DCN_VERSION_3_1) | |
928 | return true; | |
929 | ||
930 | return false; | |
931 | } | |
932 | ||
933 | static void dc_build_cursor_update_payload0( | |
934 | struct pipe_ctx *pipe_ctx, uint8_t p_idx, | |
935 | struct dmub_cmd_update_cursor_payload0 *payload) | |
936 | { | |
937 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
938 | unsigned int panel_inst = 0; | |
939 | ||
940 | if (!dc_get_edp_link_panel_inst(hubp->ctx->dc, | |
941 | pipe_ctx->stream->link, &panel_inst)) | |
942 | return; | |
943 | ||
944 | /* Payload: Cursor Rect is built from position & attribute | |
945 | * x & y are obtained from postion | |
946 | */ | |
947 | payload->cursor_rect.x = hubp->cur_rect.x; | |
948 | payload->cursor_rect.y = hubp->cur_rect.y; | |
949 | /* w & h are obtained from attribute */ | |
950 | payload->cursor_rect.width = hubp->cur_rect.w; | |
951 | payload->cursor_rect.height = hubp->cur_rect.h; | |
952 | ||
953 | payload->enable = hubp->pos.cur_ctl.bits.cur_enable; | |
954 | payload->pipe_idx = p_idx; | |
955 | payload->cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1; | |
956 | payload->panel_inst = panel_inst; | |
957 | } | |
958 | ||
b73353f7 MT |
959 | static void dc_build_cursor_position_update_payload0( |
960 | struct dmub_cmd_update_cursor_payload0 *pl, const uint8_t p_idx, | |
961 | const struct hubp *hubp, const struct dpp *dpp) | |
962 | { | |
963 | /* Hubp */ | |
964 | pl->position_cfg.pHubp.cur_ctl.raw = hubp->pos.cur_ctl.raw; | |
965 | pl->position_cfg.pHubp.position.raw = hubp->pos.position.raw; | |
966 | pl->position_cfg.pHubp.hot_spot.raw = hubp->pos.hot_spot.raw; | |
967 | pl->position_cfg.pHubp.dst_offset.raw = hubp->pos.dst_offset.raw; | |
968 | ||
969 | /* dpp */ | |
970 | pl->position_cfg.pDpp.cur0_ctl.raw = dpp->pos.cur0_ctl.raw; | |
971 | pl->position_cfg.pipe_idx = p_idx; | |
972 | } | |
973 | ||
974 | static void dc_build_cursor_attribute_update_payload1( | |
975 | struct dmub_cursor_attributes_cfg *pl_A, const uint8_t p_idx, | |
976 | const struct hubp *hubp, const struct dpp *dpp) | |
977 | { | |
978 | /* Hubp */ | |
979 | pl_A->aHubp.SURFACE_ADDR_HIGH = hubp->att.SURFACE_ADDR_HIGH; | |
980 | pl_A->aHubp.SURFACE_ADDR = hubp->att.SURFACE_ADDR; | |
981 | pl_A->aHubp.cur_ctl.raw = hubp->att.cur_ctl.raw; | |
982 | pl_A->aHubp.size.raw = hubp->att.size.raw; | |
983 | pl_A->aHubp.settings.raw = hubp->att.settings.raw; | |
984 | ||
985 | /* dpp */ | |
986 | pl_A->aDpp.cur0_ctl.raw = dpp->att.cur0_ctl.raw; | |
987 | } | |
988 | ||
989 | /** | |
6be153dc | 990 | * dc_send_update_cursor_info_to_dmu - Populate the DMCUB Cursor update info command |
b73353f7 | 991 | * |
6be153dc RS |
992 | * @pCtx: [in] pipe context |
993 | * @pipe_idx: [in] pipe index | |
b73353f7 | 994 | * |
6be153dc RS |
995 | * This function would store the cursor related information and pass it into |
996 | * dmub | |
b73353f7 | 997 | */ |
b73353f7 MT |
998 | void dc_send_update_cursor_info_to_dmu( |
999 | struct pipe_ctx *pCtx, uint8_t pipe_idx) | |
1000 | { | |
e97cc04f JP |
1001 | union dmub_rb_cmd cmd[2]; |
1002 | union dmub_cmd_update_cursor_info_data *update_cursor_info_0 = | |
1003 | &cmd[0].update_cursor_info.update_cursor_info_data; | |
1004 | ||
1005 | memset(cmd, 0, sizeof(cmd)); | |
b73353f7 MT |
1006 | |
1007 | if (!dc_dmub_should_update_cursor_data(pCtx)) | |
1008 | return; | |
1009 | /* | |
1010 | * Since we use multi_cmd_pending for dmub command, the 2nd command is | |
1011 | * only assigned to store cursor attributes info. | |
1012 | * 1st command can view as 2 parts, 1st is for PSR/Replay data, the other | |
1013 | * is to store cursor position info. | |
1014 | * | |
1015 | * Command heaer type must be the same type if using multi_cmd_pending. | |
1016 | * Besides, while process 2nd command in DMU, the sub type is useless. | |
1017 | * So it's meanless to pass the sub type header with different type. | |
1018 | */ | |
1019 | ||
1020 | { | |
1021 | /* Build Payload#0 Header */ | |
e97cc04f JP |
1022 | cmd[0].update_cursor_info.header.type = DMUB_CMD__UPDATE_CURSOR_INFO; |
1023 | cmd[0].update_cursor_info.header.payload_bytes = | |
1024 | sizeof(cmd[0].update_cursor_info.update_cursor_info_data); | |
1025 | cmd[0].update_cursor_info.header.multi_cmd_pending = 1; //To combine multi dmu cmd, 1st cmd | |
b73353f7 MT |
1026 | |
1027 | /* Prepare Payload */ | |
e97cc04f | 1028 | dc_build_cursor_update_payload0(pCtx, pipe_idx, &update_cursor_info_0->payload0); |
b73353f7 | 1029 | |
e97cc04f | 1030 | dc_build_cursor_position_update_payload0(&update_cursor_info_0->payload0, pipe_idx, |
b73353f7 | 1031 | pCtx->plane_res.hubp, pCtx->plane_res.dpp); |
e97cc04f | 1032 | } |
b73353f7 MT |
1033 | { |
1034 | /* Build Payload#1 Header */ | |
e97cc04f JP |
1035 | cmd[1].update_cursor_info.header.type = DMUB_CMD__UPDATE_CURSOR_INFO; |
1036 | cmd[1].update_cursor_info.header.payload_bytes = sizeof(struct cursor_attributes_cfg); | |
1037 | cmd[1].update_cursor_info.header.multi_cmd_pending = 0; //Indicate it's the last command. | |
b73353f7 MT |
1038 | |
1039 | dc_build_cursor_attribute_update_payload1( | |
e97cc04f | 1040 | &cmd[1].update_cursor_info.update_cursor_info_data.payload1.attribute_cfg, |
b73353f7 MT |
1041 | pipe_idx, pCtx->plane_res.hubp, pCtx->plane_res.dpp); |
1042 | ||
1043 | /* Combine 2nd cmds update_curosr_info to DMU */ | |
e97cc04f | 1044 | dm_execute_dmub_cmd_list(pCtx->stream->ctx, 2, cmd, DM_DMUB_WAIT_TYPE_WAIT); |
b73353f7 MT |
1045 | } |
1046 | } |