2 * Copyright 2019 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
27 #include "dc_dmub_srv.h"
28 #include "../dmub/dmub_srv.h"
29 #include "dm_helpers.h"
30 #include "dc_hw_types.h"
31 #include "core_types.h"
32 #include "../basics/conversion.h"
34 #define CTX dc_dmub_srv->ctx
35 #define DC_LOGGER CTX->logger
37 static void dc_dmub_srv_construct(struct dc_dmub_srv
*dc_srv
, struct dc
*dc
,
38 struct dmub_srv
*dmub
)
41 dc_srv
->ctx
= dc
->ctx
;
44 struct dc_dmub_srv
*dc_dmub_srv_create(struct dc
*dc
, struct dmub_srv
*dmub
)
46 struct dc_dmub_srv
*dc_srv
=
47 kzalloc(sizeof(struct dc_dmub_srv
), GFP_KERNEL
);
54 dc_dmub_srv_construct(dc_srv
, dc
, dmub
);
59 void dc_dmub_srv_destroy(struct dc_dmub_srv
**dmub_srv
)
67 void dc_dmub_srv_cmd_queue(struct dc_dmub_srv
*dc_dmub_srv
,
68 union dmub_rb_cmd
*cmd
)
70 struct dmub_srv
*dmub
= dc_dmub_srv
->dmub
;
71 struct dc_context
*dc_ctx
= dc_dmub_srv
->ctx
;
72 enum dmub_status status
;
74 status
= dmub_srv_cmd_queue(dmub
, cmd
);
75 if (status
== DMUB_STATUS_OK
)
78 if (status
!= DMUB_STATUS_QUEUE_FULL
)
81 /* Execute and wait for queue to become empty again. */
82 dc_dmub_srv_cmd_execute(dc_dmub_srv
);
83 dc_dmub_srv_wait_idle(dc_dmub_srv
);
85 /* Requeue the command. */
86 status
= dmub_srv_cmd_queue(dmub
, cmd
);
87 if (status
== DMUB_STATUS_OK
)
91 DC_ERROR("Error queuing DMUB command: status=%d\n", status
);
92 dc_dmub_srv_log_diagnostic_data(dc_dmub_srv
);
95 void dc_dmub_srv_cmd_execute(struct dc_dmub_srv
*dc_dmub_srv
)
97 struct dmub_srv
*dmub
= dc_dmub_srv
->dmub
;
98 struct dc_context
*dc_ctx
= dc_dmub_srv
->ctx
;
99 enum dmub_status status
;
101 status
= dmub_srv_cmd_execute(dmub
);
102 if (status
!= DMUB_STATUS_OK
) {
103 DC_ERROR("Error starting DMUB execution: status=%d\n", status
);
104 dc_dmub_srv_log_diagnostic_data(dc_dmub_srv
);
108 void dc_dmub_srv_wait_idle(struct dc_dmub_srv
*dc_dmub_srv
)
110 struct dmub_srv
*dmub
= dc_dmub_srv
->dmub
;
111 struct dc_context
*dc_ctx
= dc_dmub_srv
->ctx
;
112 enum dmub_status status
;
114 status
= dmub_srv_wait_for_idle(dmub
, 100000);
115 if (status
!= DMUB_STATUS_OK
) {
116 DC_ERROR("Error waiting for DMUB idle: status=%d\n", status
);
117 dc_dmub_srv_log_diagnostic_data(dc_dmub_srv
);
121 void dc_dmub_srv_clear_inbox0_ack(struct dc_dmub_srv
*dmub_srv
)
123 struct dmub_srv
*dmub
= dmub_srv
->dmub
;
124 struct dc_context
*dc_ctx
= dmub_srv
->ctx
;
125 enum dmub_status status
= DMUB_STATUS_OK
;
127 status
= dmub_srv_clear_inbox0_ack(dmub
);
128 if (status
!= DMUB_STATUS_OK
) {
129 DC_ERROR("Error clearing INBOX0 ack: status=%d\n", status
);
130 dc_dmub_srv_log_diagnostic_data(dmub_srv
);
134 void dc_dmub_srv_wait_for_inbox0_ack(struct dc_dmub_srv
*dmub_srv
)
136 struct dmub_srv
*dmub
= dmub_srv
->dmub
;
137 struct dc_context
*dc_ctx
= dmub_srv
->ctx
;
138 enum dmub_status status
= DMUB_STATUS_OK
;
140 status
= dmub_srv_wait_for_inbox0_ack(dmub
, 100000);
141 if (status
!= DMUB_STATUS_OK
) {
142 DC_ERROR("Error waiting for INBOX0 HW Lock Ack\n");
143 dc_dmub_srv_log_diagnostic_data(dmub_srv
);
147 void dc_dmub_srv_send_inbox0_cmd(struct dc_dmub_srv
*dmub_srv
,
148 union dmub_inbox0_data_register data
)
150 struct dmub_srv
*dmub
= dmub_srv
->dmub
;
151 struct dc_context
*dc_ctx
= dmub_srv
->ctx
;
152 enum dmub_status status
= DMUB_STATUS_OK
;
154 status
= dmub_srv_send_inbox0_cmd(dmub
, data
);
155 if (status
!= DMUB_STATUS_OK
) {
156 DC_ERROR("Error sending INBOX0 cmd\n");
157 dc_dmub_srv_log_diagnostic_data(dmub_srv
);
161 bool dc_dmub_srv_cmd_with_reply_data(struct dc_dmub_srv
*dc_dmub_srv
, union dmub_rb_cmd
*cmd
)
163 struct dmub_srv
*dmub
;
164 enum dmub_status status
;
166 if (!dc_dmub_srv
|| !dc_dmub_srv
->dmub
)
169 dmub
= dc_dmub_srv
->dmub
;
171 status
= dmub_srv_cmd_with_reply_data(dmub
, cmd
);
172 if (status
!= DMUB_STATUS_OK
) {
173 DC_LOG_DEBUG("No reply for DMUB command: status=%d\n", status
);
180 void dc_dmub_srv_wait_phy_init(struct dc_dmub_srv
*dc_dmub_srv
)
182 struct dmub_srv
*dmub
= dc_dmub_srv
->dmub
;
183 struct dc_context
*dc_ctx
= dc_dmub_srv
->ctx
;
184 enum dmub_status status
;
187 /* Wait up to a second for PHY init. */
188 status
= dmub_srv_wait_for_phy_init(dmub
, 1000000);
189 if (status
== DMUB_STATUS_OK
)
190 /* Initialization OK */
193 DC_ERROR("DMCUB PHY init failed: status=%d\n", status
);
196 if (status
!= DMUB_STATUS_TIMEOUT
)
198 * Server likely initialized or we don't have
199 * DMCUB HW support - this won't end.
203 /* Continue spinning so we don't hang the ASIC. */
207 bool dc_dmub_srv_notify_stream_mask(struct dc_dmub_srv
*dc_dmub_srv
,
208 unsigned int stream_mask
)
210 struct dmub_srv
*dmub
;
211 const uint32_t timeout
= 30;
213 if (!dc_dmub_srv
|| !dc_dmub_srv
->dmub
)
216 dmub
= dc_dmub_srv
->dmub
;
218 return dmub_srv_send_gpint_command(
219 dmub
, DMUB_GPINT__IDLE_OPT_NOTIFY_STREAM_MASK
,
220 stream_mask
, timeout
) == DMUB_STATUS_OK
;
223 bool dc_dmub_srv_is_restore_required(struct dc_dmub_srv
*dc_dmub_srv
)
225 struct dmub_srv
*dmub
;
226 struct dc_context
*dc_ctx
;
227 union dmub_fw_boot_status boot_status
;
228 enum dmub_status status
;
230 if (!dc_dmub_srv
|| !dc_dmub_srv
->dmub
)
233 dmub
= dc_dmub_srv
->dmub
;
234 dc_ctx
= dc_dmub_srv
->ctx
;
236 status
= dmub_srv_get_fw_boot_status(dmub
, &boot_status
);
237 if (status
!= DMUB_STATUS_OK
) {
238 DC_ERROR("Error querying DMUB boot status: error=%d\n", status
);
242 return boot_status
.bits
.restore_required
;
245 bool dc_dmub_srv_get_dmub_outbox0_msg(const struct dc
*dc
, struct dmcub_trace_buf_entry
*entry
)
247 struct dmub_srv
*dmub
= dc
->ctx
->dmub_srv
->dmub
;
248 return dmub_srv_get_outbox0_msg(dmub
, entry
);
251 void dc_dmub_trace_event_control(struct dc
*dc
, bool enable
)
253 dm_helpers_dmub_outbox_interrupt_control(dc
->ctx
, enable
);
256 void dc_dmub_srv_drr_update_cmd(struct dc
*dc
, uint32_t tg_inst
, uint32_t vtotal_min
, uint32_t vtotal_max
)
258 union dmub_rb_cmd cmd
= { 0 };
260 cmd
.drr_update
.header
.type
= DMUB_CMD__FW_ASSISTED_MCLK_SWITCH
;
261 cmd
.drr_update
.header
.sub_type
= DMUB_CMD__FAMS_DRR_UPDATE
;
262 cmd
.drr_update
.dmub_optc_state_req
.v_total_max
= vtotal_max
;
263 cmd
.drr_update
.dmub_optc_state_req
.v_total_min
= vtotal_min
;
264 cmd
.drr_update
.dmub_optc_state_req
.tg_inst
= tg_inst
;
266 cmd
.drr_update
.header
.payload_bytes
= sizeof(cmd
.drr_update
) - sizeof(cmd
.drr_update
.header
);
268 // Send the command to the DMCUB.
269 dc_dmub_srv_cmd_queue(dc
->ctx
->dmub_srv
, &cmd
);
270 dc_dmub_srv_cmd_execute(dc
->ctx
->dmub_srv
);
271 dc_dmub_srv_wait_idle(dc
->ctx
->dmub_srv
);
274 void dc_dmub_srv_set_drr_manual_trigger_cmd(struct dc
*dc
, uint32_t tg_inst
)
276 union dmub_rb_cmd cmd
= { 0 };
278 cmd
.drr_update
.header
.type
= DMUB_CMD__FW_ASSISTED_MCLK_SWITCH
;
279 cmd
.drr_update
.header
.sub_type
= DMUB_CMD__FAMS_SET_MANUAL_TRIGGER
;
280 cmd
.drr_update
.dmub_optc_state_req
.tg_inst
= tg_inst
;
282 cmd
.drr_update
.header
.payload_bytes
= sizeof(cmd
.drr_update
) - sizeof(cmd
.drr_update
.header
);
284 // Send the command to the DMCUB.
285 dc_dmub_srv_cmd_queue(dc
->ctx
->dmub_srv
, &cmd
);
286 dc_dmub_srv_cmd_execute(dc
->ctx
->dmub_srv
);
287 dc_dmub_srv_wait_idle(dc
->ctx
->dmub_srv
);
290 static uint8_t dc_dmub_srv_get_pipes_for_stream(struct dc
*dc
, struct dc_stream_state
*stream
)
295 for (i
= 0; i
< MAX_PIPES
; i
++) {
296 struct pipe_ctx
*pipe
= &dc
->current_state
->res_ctx
.pipe_ctx
[i
];
298 if (pipe
->stream
== stream
&& pipe
->stream_res
.tg
)
304 static int dc_dmub_srv_get_timing_generator_offset(struct dc
*dc
, struct dc_stream_state
*stream
)
309 for (i
= 0; i
< MAX_PIPES
; i
++) {
310 struct pipe_ctx
*pipe
= &dc
->current_state
->res_ctx
.pipe_ctx
[i
];
312 if (pipe
->stream
== stream
&& pipe
->stream_res
.tg
) {
313 tg_inst
= pipe
->stream_res
.tg
->inst
;
320 bool dc_dmub_srv_p_state_delegate(struct dc
*dc
, bool should_manage_pstate
, struct dc_state
*context
)
322 union dmub_rb_cmd cmd
= { 0 };
323 struct dmub_cmd_fw_assisted_mclk_switch_config
*config_data
= &cmd
.fw_assisted_mclk_switch
.config_data
;
325 int ramp_up_num_steps
= 1; // TODO: Ramp is currently disabled. Reenable it.
326 uint8_t visual_confirm_enabled
= dc
->debug
.visual_confirm
== VISUAL_CONFIRM_FAMS
;
332 cmd
.fw_assisted_mclk_switch
.header
.type
= DMUB_CMD__FW_ASSISTED_MCLK_SWITCH
;
333 cmd
.fw_assisted_mclk_switch
.header
.sub_type
= DMUB_CMD__FAMS_SETUP_FW_CTRL
;
334 cmd
.fw_assisted_mclk_switch
.config_data
.fams_enabled
= should_manage_pstate
;
335 cmd
.fw_assisted_mclk_switch
.config_data
.visual_confirm_enabled
= visual_confirm_enabled
;
337 for (i
= 0; context
&& i
< context
->stream_count
; i
++) {
338 struct dc_stream_state
*stream
= context
->streams
[i
];
339 uint8_t min_refresh_in_hz
= (stream
->timing
.min_refresh_in_uhz
+ 999999) / 1000000;
340 int tg_inst
= dc_dmub_srv_get_timing_generator_offset(dc
, stream
);
342 config_data
->pipe_data
[tg_inst
].pix_clk_100hz
= stream
->timing
.pix_clk_100hz
;
343 config_data
->pipe_data
[tg_inst
].min_refresh_in_hz
= min_refresh_in_hz
;
344 config_data
->pipe_data
[tg_inst
].max_ramp_step
= ramp_up_num_steps
;
345 config_data
->pipe_data
[tg_inst
].pipes
= dc_dmub_srv_get_pipes_for_stream(dc
, stream
);
348 cmd
.fw_assisted_mclk_switch
.header
.payload_bytes
=
349 sizeof(cmd
.fw_assisted_mclk_switch
) - sizeof(cmd
.fw_assisted_mclk_switch
.header
);
351 // Send the command to the DMCUB.
352 dc_dmub_srv_cmd_queue(dc
->ctx
->dmub_srv
, &cmd
);
353 dc_dmub_srv_cmd_execute(dc
->ctx
->dmub_srv
);
354 dc_dmub_srv_wait_idle(dc
->ctx
->dmub_srv
);
359 void dc_dmub_srv_query_caps_cmd(struct dmub_srv
*dmub
)
361 union dmub_rb_cmd cmd
= { 0 };
362 enum dmub_status status
;
368 memset(&cmd
, 0, sizeof(cmd
));
370 /* Prepare fw command */
371 cmd
.query_feature_caps
.header
.type
= DMUB_CMD__QUERY_FEATURE_CAPS
;
372 cmd
.query_feature_caps
.header
.sub_type
= 0;
373 cmd
.query_feature_caps
.header
.ret_status
= 1;
374 cmd
.query_feature_caps
.header
.payload_bytes
= sizeof(struct dmub_cmd_query_feature_caps_data
);
376 /* Send command to fw */
377 status
= dmub_srv_cmd_with_reply_data(dmub
, &cmd
);
379 ASSERT(status
== DMUB_STATUS_OK
);
381 /* If command was processed, copy feature caps to dmub srv */
382 if (status
== DMUB_STATUS_OK
&&
383 cmd
.query_feature_caps
.header
.ret_status
== 0) {
384 memcpy(&dmub
->feature_caps
,
385 &cmd
.query_feature_caps
.query_feature_caps_data
,
386 sizeof(struct dmub_feature_caps
));
390 #ifdef CONFIG_DRM_AMD_DC_DCN
392 * ***********************************************************************************************
393 * populate_subvp_cmd_drr_info: Helper to populate DRR pipe info for the DMCUB subvp command
395 * Populate the DMCUB SubVP command with DRR pipe info. All the information required for calculating
396 * the SubVP + DRR microschedule is populated here.
398 * High level algorithm:
399 * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe
400 * 2. Calculate the min and max vtotal which supports SubVP + DRR microschedule
401 * 3. Populate the drr_info with the min and max supported vtotal values
403 * @param [in] dc: current dc state
404 * @param [in] subvp_pipe: pipe_ctx for the SubVP pipe
405 * @param [in] vblank_pipe: pipe_ctx for the DRR pipe
406 * @param [in] pipe_data: Pipe data which stores the VBLANK/DRR info
410 * ***********************************************************************************************
412 static void populate_subvp_cmd_drr_info(struct dc
*dc
,
413 struct pipe_ctx
*subvp_pipe
,
414 struct pipe_ctx
*vblank_pipe
,
415 struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2
*pipe_data
)
417 struct dc_crtc_timing
*main_timing
= &subvp_pipe
->stream
->timing
;
418 struct dc_crtc_timing
*phantom_timing
= &subvp_pipe
->stream
->mall_stream_config
.paired_stream
->timing
;
419 struct dc_crtc_timing
*drr_timing
= &vblank_pipe
->stream
->timing
;
420 int16_t drr_frame_us
= 0;
421 int16_t min_drr_supported_us
= 0;
422 int16_t max_drr_supported_us
= 0;
423 int16_t max_drr_vblank_us
= 0;
424 int16_t max_drr_mallregion_us
= 0;
425 int16_t mall_region_us
= 0;
426 int16_t prefetch_us
= 0;
427 int16_t subvp_active_us
= 0;
428 int16_t drr_active_us
= 0;
429 int16_t min_vtotal_supported
= 0;
430 int16_t max_vtotal_supported
= 0;
432 pipe_data
->pipe_config
.vblank_data
.drr_info
.drr_in_use
= true;
433 pipe_data
->pipe_config
.vblank_data
.drr_info
.use_ramping
= false; // for now don't use ramping
434 pipe_data
->pipe_config
.vblank_data
.drr_info
.drr_window_size_ms
= 4; // hardcode 4ms DRR window for now
436 drr_frame_us
= div64_s64(drr_timing
->v_total
* drr_timing
->h_total
,
437 (int64_t)(drr_timing
->pix_clk_100hz
* 100) * 1000000);
438 // P-State allow width and FW delays already included phantom_timing->v_addressable
439 mall_region_us
= div64_s64(phantom_timing
->v_addressable
* phantom_timing
->h_total
,
440 (int64_t)(phantom_timing
->pix_clk_100hz
* 100) * 1000000);
441 min_drr_supported_us
= drr_frame_us
+ mall_region_us
+ SUBVP_DRR_MARGIN_US
;
442 min_vtotal_supported
= div64_s64(drr_timing
->pix_clk_100hz
* 100 *
443 (div64_s64((int64_t)min_drr_supported_us
, 1000000)),
444 (int64_t)drr_timing
->h_total
);
446 prefetch_us
= div64_s64((phantom_timing
->v_total
- phantom_timing
->v_front_porch
) * phantom_timing
->h_total
,
447 (int64_t)(phantom_timing
->pix_clk_100hz
* 100) * 1000000 +
448 dc
->caps
.subvp_prefetch_end_to_mall_start_us
);
449 subvp_active_us
= div64_s64(main_timing
->v_addressable
* main_timing
->h_total
,
450 (int64_t)(main_timing
->pix_clk_100hz
* 100) * 1000000);
451 drr_active_us
= div64_s64(drr_timing
->v_addressable
* drr_timing
->h_total
,
452 (int64_t)(drr_timing
->pix_clk_100hz
* 100) * 1000000);
453 max_drr_vblank_us
= div64_s64((int64_t)(subvp_active_us
- prefetch_us
- drr_active_us
), 2) + drr_active_us
;
454 max_drr_mallregion_us
= subvp_active_us
- prefetch_us
- mall_region_us
;
455 max_drr_supported_us
= max_drr_vblank_us
> max_drr_mallregion_us
? max_drr_vblank_us
: max_drr_mallregion_us
;
456 max_vtotal_supported
= div64_s64(drr_timing
->pix_clk_100hz
* 100 * (div64_s64((int64_t)max_drr_supported_us
, 1000000)),
457 (int64_t)drr_timing
->h_total
);
459 pipe_data
->pipe_config
.vblank_data
.drr_info
.min_vtotal_supported
= min_vtotal_supported
;
460 pipe_data
->pipe_config
.vblank_data
.drr_info
.max_vtotal_supported
= max_vtotal_supported
;
464 * ***********************************************************************************************
465 * populate_subvp_cmd_vblank_pipe_info: Helper to populate VBLANK pipe info for the DMUB subvp command
467 * Populate the DMCUB SubVP command with VBLANK pipe info. All the information required to calculate
468 * the microschedule for SubVP + VBLANK case is stored in the pipe_data (subvp_data and vblank_data).
469 * Also check if the VBLANK pipe is a DRR display -- if it is make a call to populate drr_info.
471 * @param [in] dc: current dc state
472 * @param [in] context: new dc state
473 * @param [in] cmd: DMUB cmd to be populated with SubVP info
474 * @param [in] vblank_pipe: pipe_ctx for the VBLANK pipe
475 * @param [in] cmd_pipe_index: index for the pipe array in DMCUB SubVP cmd
479 * ***********************************************************************************************
481 static void populate_subvp_cmd_vblank_pipe_info(struct dc
*dc
,
482 struct dc_state
*context
,
483 union dmub_rb_cmd
*cmd
,
484 struct pipe_ctx
*vblank_pipe
,
485 uint8_t cmd_pipe_index
)
488 struct pipe_ctx
*pipe
= NULL
;
489 struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2
*pipe_data
=
490 &cmd
->fw_assisted_mclk_switch_v2
.config_data
.pipe_data
[cmd_pipe_index
];
492 // Find the SubVP pipe
493 for (i
= 0; i
< dc
->res_pool
->pipe_count
; i
++) {
494 pipe
= &context
->res_ctx
.pipe_ctx
[i
];
496 // We check for master pipe, but it shouldn't matter since we only need
497 // the pipe for timing info (stream should be same for any pipe splits)
498 if (!pipe
->stream
|| !pipe
->plane_state
|| pipe
->top_pipe
|| pipe
->prev_odm_pipe
)
501 // Find the SubVP pipe
502 if (pipe
->stream
->mall_stream_config
.type
== SUBVP_MAIN
)
506 pipe_data
->mode
= VBLANK
;
507 pipe_data
->pipe_config
.vblank_data
.pix_clk_100hz
= vblank_pipe
->stream
->timing
.pix_clk_100hz
;
508 pipe_data
->pipe_config
.vblank_data
.vblank_start
= vblank_pipe
->stream
->timing
.v_total
-
509 vblank_pipe
->stream
->timing
.v_front_porch
;
510 pipe_data
->pipe_config
.vblank_data
.vtotal
= vblank_pipe
->stream
->timing
.v_total
;
511 pipe_data
->pipe_config
.vblank_data
.htotal
= vblank_pipe
->stream
->timing
.h_total
;
512 pipe_data
->pipe_config
.vblank_data
.vblank_pipe_index
= vblank_pipe
->pipe_idx
;
513 pipe_data
->pipe_config
.vblank_data
.vstartup_start
= vblank_pipe
->pipe_dlg_param
.vstartup_start
;
514 pipe_data
->pipe_config
.vblank_data
.vblank_end
=
515 vblank_pipe
->stream
->timing
.v_total
- vblank_pipe
->stream
->timing
.v_front_porch
- vblank_pipe
->stream
->timing
.v_addressable
;
517 if (vblank_pipe
->stream
->ignore_msa_timing_param
)
518 populate_subvp_cmd_drr_info(dc
, pipe
, vblank_pipe
, pipe_data
);
522 * ***********************************************************************************************
523 * update_subvp_prefetch_end_to_mall_start: Helper for SubVP + SubVP case
525 * For SubVP + SubVP, we use a single vertical interrupt to start the microschedule for both
526 * SubVP pipes. In order for this to work correctly, the MALL REGION of both SubVP pipes must
527 * start at the same time. This function lengthens the prefetch end to mall start delay of the
528 * SubVP pipe that has the shorter prefetch so that both MALL REGION's will start at the same time.
530 * @param [in] dc: current dc state
531 * @param [in] context: new dc state
532 * @param [in] cmd: DMUB cmd to be populated with SubVP info
533 * @param [in] subvp_pipes: Array of SubVP pipes (should always be length 2)
537 * ***********************************************************************************************
539 static void update_subvp_prefetch_end_to_mall_start(struct dc
*dc
,
540 struct dc_state
*context
,
541 union dmub_rb_cmd
*cmd
,
542 struct pipe_ctx
*subvp_pipes
[])
544 uint32_t subvp0_prefetch_us
= 0;
545 uint32_t subvp1_prefetch_us
= 0;
546 uint32_t prefetch_delta_us
= 0;
547 struct dc_crtc_timing
*phantom_timing0
= &subvp_pipes
[0]->stream
->mall_stream_config
.paired_stream
->timing
;
548 struct dc_crtc_timing
*phantom_timing1
= &subvp_pipes
[1]->stream
->mall_stream_config
.paired_stream
->timing
;
549 struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2
*pipe_data
= NULL
;
551 subvp0_prefetch_us
= div64_s64((phantom_timing0
->v_total
- phantom_timing0
->v_front_porch
) * phantom_timing0
->h_total
,
552 (int64_t)(phantom_timing0
->pix_clk_100hz
* 100) * 1000000 + dc
->caps
.subvp_prefetch_end_to_mall_start_us
);
553 subvp1_prefetch_us
= div64_s64((phantom_timing1
->v_total
- phantom_timing1
->v_front_porch
) * phantom_timing1
->h_total
,
554 (int64_t)(phantom_timing1
->pix_clk_100hz
* 100) * 1000000 + dc
->caps
.subvp_prefetch_end_to_mall_start_us
);
556 // Whichever SubVP PIPE has the smaller prefetch (including the prefetch end to mall start time)
557 // should increase it's prefetch time to match the other
558 if (subvp0_prefetch_us
> subvp1_prefetch_us
) {
559 pipe_data
= &cmd
->fw_assisted_mclk_switch_v2
.config_data
.pipe_data
[1];
560 prefetch_delta_us
= subvp0_prefetch_us
- subvp1_prefetch_us
;
561 pipe_data
->pipe_config
.subvp_data
.prefetch_to_mall_start_lines
=
562 div64_s64(((div64_s64((int64_t)(dc
->caps
.subvp_prefetch_end_to_mall_start_us
+ prefetch_delta_us
), 1000000)) *
563 (phantom_timing1
->pix_clk_100hz
* 100) + phantom_timing1
->h_total
- 1),
564 (int64_t)phantom_timing1
->h_total
);
565 } else if (subvp1_prefetch_us
> subvp0_prefetch_us
) {
566 pipe_data
= &cmd
->fw_assisted_mclk_switch_v2
.config_data
.pipe_data
[0];
567 prefetch_delta_us
= subvp1_prefetch_us
- subvp0_prefetch_us
;
568 pipe_data
->pipe_config
.subvp_data
.prefetch_to_mall_start_lines
=
569 div64_s64(((div64_s64((int64_t)(dc
->caps
.subvp_prefetch_end_to_mall_start_us
+ prefetch_delta_us
), 1000000)) *
570 (phantom_timing0
->pix_clk_100hz
* 100) + phantom_timing0
->h_total
- 1),
571 (int64_t)phantom_timing0
->h_total
);
576 * ***************************************************************************************
577 * setup_subvp_dmub_command: Helper to populate the SubVP pipe info for the DMUB subvp command
579 * Populate the DMCUB SubVP command with SubVP pipe info. All the information required to
580 * calculate the microschedule for the SubVP pipe is stored in the pipe_data of the DMCUB
583 * @param [in] dc: current dc state
584 * @param [in] context: new dc state
585 * @param [in] cmd: DMUB cmd to be populated with SubVP info
586 * @param [in] subvp_pipe: pipe_ctx for the SubVP pipe
587 * @param [in] cmd_pipe_index: index for the pipe array in DMCUB SubVP cmd
591 * ***************************************************************************************
593 static void populate_subvp_cmd_pipe_info(struct dc
*dc
,
594 struct dc_state
*context
,
595 union dmub_rb_cmd
*cmd
,
596 struct pipe_ctx
*subvp_pipe
,
597 uint8_t cmd_pipe_index
)
600 struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2
*pipe_data
=
601 &cmd
->fw_assisted_mclk_switch_v2
.config_data
.pipe_data
[cmd_pipe_index
];
602 struct dc_crtc_timing
*main_timing
= &subvp_pipe
->stream
->timing
;
603 struct dc_crtc_timing
*phantom_timing
= &subvp_pipe
->stream
->mall_stream_config
.paired_stream
->timing
;
604 uint32_t out_num
, out_den
;
606 pipe_data
->mode
= SUBVP
;
607 pipe_data
->pipe_config
.subvp_data
.pix_clk_100hz
= subvp_pipe
->stream
->timing
.pix_clk_100hz
;
608 pipe_data
->pipe_config
.subvp_data
.htotal
= subvp_pipe
->stream
->timing
.h_total
;
609 pipe_data
->pipe_config
.subvp_data
.vtotal
= subvp_pipe
->stream
->timing
.v_total
;
610 pipe_data
->pipe_config
.subvp_data
.main_vblank_start
=
611 main_timing
->v_total
- main_timing
->v_front_porch
;
612 pipe_data
->pipe_config
.subvp_data
.main_vblank_end
=
613 main_timing
->v_total
- main_timing
->v_front_porch
- main_timing
->v_addressable
;
614 pipe_data
->pipe_config
.subvp_data
.mall_region_lines
= phantom_timing
->v_addressable
;
615 pipe_data
->pipe_config
.subvp_data
.main_pipe_index
= subvp_pipe
->pipe_idx
;
616 pipe_data
->pipe_config
.subvp_data
.is_drr
= subvp_pipe
->stream
->ignore_msa_timing_param
;
618 /* Calculate the scaling factor from the src and dst height.
619 * e.g. If 3840x2160 being downscaled to 1920x1080, the scaling factor is 1/2.
620 * Reduce the fraction 1080/2160 = 1/2 for the "scaling factor"
622 reduce_fraction(subvp_pipe
->stream
->src
.height
, subvp_pipe
->stream
->dst
.height
, &out_num
, &out_den
);
623 // TODO: Uncomment below lines once DMCUB include headers are promoted
624 //pipe_data->pipe_config.subvp_data.scale_factor_numerator = out_num;
625 //pipe_data->pipe_config.subvp_data.scale_factor_denominator = out_den;
627 // Prefetch lines is equal to VACTIVE + BP + VSYNC
628 pipe_data
->pipe_config
.subvp_data
.prefetch_lines
=
629 phantom_timing
->v_total
- phantom_timing
->v_front_porch
;
632 pipe_data
->pipe_config
.subvp_data
.prefetch_to_mall_start_lines
=
633 div64_s64(((div64_s64((int64_t)dc
->caps
.subvp_prefetch_end_to_mall_start_us
, 1000000)) *
634 (phantom_timing
->pix_clk_100hz
* 100) + phantom_timing
->h_total
- 1),
635 (int64_t)phantom_timing
->h_total
);
636 pipe_data
->pipe_config
.subvp_data
.processing_delay_lines
=
637 div64_s64(((div64_s64((int64_t)dc
->caps
.subvp_fw_processing_delay_us
, 1000000)) *
638 (phantom_timing
->pix_clk_100hz
* 100) + phantom_timing
->h_total
- 1),
639 (int64_t)phantom_timing
->h_total
);
640 // Find phantom pipe index based on phantom stream
641 for (j
= 0; j
< dc
->res_pool
->pipe_count
; j
++) {
642 struct pipe_ctx
*phantom_pipe
= &context
->res_ctx
.pipe_ctx
[j
];
644 if (phantom_pipe
->stream
== subvp_pipe
->stream
->mall_stream_config
.paired_stream
) {
645 pipe_data
->pipe_config
.subvp_data
.phantom_pipe_index
= phantom_pipe
->pipe_idx
;
652 * ***************************************************************************************
653 * dc_dmub_setup_subvp_dmub_command: Populate the DMCUB SubVP command
655 * This function loops through each pipe and populates the DMUB
656 * SubVP CMD info based on the pipe (e.g. SubVP, VBLANK).
658 * @param [in] dc: current dc state
659 * @param [in] context: new dc state
660 * @param [in] cmd: DMUB cmd to be populated with SubVP info
664 * ***************************************************************************************
666 void dc_dmub_setup_subvp_dmub_command(struct dc
*dc
,
667 struct dc_state
*context
,
670 uint8_t cmd_pipe_index
= 0;
671 uint32_t i
, pipe_idx
;
672 uint8_t subvp_count
= 0;
673 union dmub_rb_cmd cmd
;
674 struct pipe_ctx
*subvp_pipes
[2];
675 uint32_t wm_val_refclk
= 0;
677 memset(&cmd
, 0, sizeof(cmd
));
678 // FW command for SUBVP
679 cmd
.fw_assisted_mclk_switch_v2
.header
.type
= DMUB_CMD__FW_ASSISTED_MCLK_SWITCH
;
680 cmd
.fw_assisted_mclk_switch_v2
.header
.sub_type
= DMUB_CMD__HANDLE_SUBVP_CMD
;
681 cmd
.fw_assisted_mclk_switch_v2
.header
.payload_bytes
=
682 sizeof(cmd
.fw_assisted_mclk_switch_v2
) - sizeof(cmd
.fw_assisted_mclk_switch_v2
.header
);
684 for (i
= 0; i
< dc
->res_pool
->pipe_count
; i
++) {
685 struct pipe_ctx
*pipe
= &context
->res_ctx
.pipe_ctx
[i
];
690 if (pipe
->plane_state
&& !pipe
->top_pipe
&&
691 pipe
->stream
->mall_stream_config
.type
== SUBVP_MAIN
)
692 subvp_pipes
[subvp_count
++] = pipe
;
696 // For each pipe that is a "main" SUBVP pipe, fill in pipe data for DMUB SUBVP cmd
697 for (i
= 0, pipe_idx
= 0; i
< dc
->res_pool
->pipe_count
; i
++) {
698 struct pipe_ctx
*pipe
= &context
->res_ctx
.pipe_ctx
[i
];
703 if (pipe
->plane_state
&& pipe
->stream
->mall_stream_config
.paired_stream
&&
704 pipe
->stream
->mall_stream_config
.type
== SUBVP_MAIN
) {
705 populate_subvp_cmd_pipe_info(dc
, context
, &cmd
, pipe
, cmd_pipe_index
++);
706 } else if (pipe
->plane_state
&& pipe
->stream
->mall_stream_config
.type
== SUBVP_NONE
) {
707 // Don't need to check for ActiveDRAMClockChangeMargin < 0, not valid in cases where
708 // we run through DML without calculating "natural" P-state support
709 populate_subvp_cmd_vblank_pipe_info(dc
, context
, &cmd
, pipe
, cmd_pipe_index
++);
714 if (subvp_count
== 2) {
715 update_subvp_prefetch_end_to_mall_start(dc
, context
, &cmd
, subvp_pipes
);
717 cmd
.fw_assisted_mclk_switch_v2
.config_data
.pstate_allow_width_us
= dc
->caps
.subvp_pstate_allow_width_us
;
718 cmd
.fw_assisted_mclk_switch_v2
.config_data
.vertical_int_margin_us
= dc
->caps
.subvp_vertical_int_margin_us
;
720 // Store the original watermark value for this SubVP config so we can lower it when the
721 // MCLK switch starts
722 wm_val_refclk
= context
->bw_ctx
.bw
.dcn
.watermarks
.a
.cstate_pstate
.pstate_change_ns
*
723 dc
->res_pool
->ref_clocks
.dchub_ref_clock_inKhz
/ 1000 / 1000;
725 cmd
.fw_assisted_mclk_switch_v2
.config_data
.watermark_a_cache
= wm_val_refclk
< 0xFFFF ? wm_val_refclk
: 0xFFFF;
727 dc_dmub_srv_cmd_queue(dc
->ctx
->dmub_srv
, &cmd
);
728 dc_dmub_srv_cmd_execute(dc
->ctx
->dmub_srv
);
729 dc_dmub_srv_wait_idle(dc
->ctx
->dmub_srv
);
733 bool dc_dmub_srv_get_diagnostic_data(struct dc_dmub_srv
*dc_dmub_srv
, struct dmub_diagnostic_data
*diag_data
)
735 if (!dc_dmub_srv
|| !dc_dmub_srv
->dmub
|| !diag_data
)
737 return dmub_srv_get_diagnostic_data(dc_dmub_srv
->dmub
, diag_data
);
740 void dc_dmub_srv_log_diagnostic_data(struct dc_dmub_srv
*dc_dmub_srv
)
742 struct dmub_diagnostic_data diag_data
= {0};
744 if (!dc_dmub_srv
|| !dc_dmub_srv
->dmub
) {
745 DC_LOG_ERROR("%s: invalid parameters.", __func__
);
749 if (!dc_dmub_srv_get_diagnostic_data(dc_dmub_srv
, &diag_data
)) {
750 DC_LOG_ERROR("%s: dc_dmub_srv_get_diagnostic_data failed.", __func__
);
756 " dmcub_version : %08x\n"
757 " scratch [0] : %08x\n"
758 " scratch [1] : %08x\n"
759 " scratch [2] : %08x\n"
760 " scratch [3] : %08x\n"
761 " scratch [4] : %08x\n"
762 " scratch [5] : %08x\n"
763 " scratch [6] : %08x\n"
764 " scratch [7] : %08x\n"
765 " scratch [8] : %08x\n"
766 " scratch [9] : %08x\n"
767 " scratch [10] : %08x\n"
768 " scratch [11] : %08x\n"
769 " scratch [12] : %08x\n"
770 " scratch [13] : %08x\n"
771 " scratch [14] : %08x\n"
772 " scratch [15] : %08x\n"
774 " unk_fault_addr : %08x\n"
775 " inst_fault_addr : %08x\n"
776 " data_fault_addr : %08x\n"
777 " inbox1_rptr : %08x\n"
778 " inbox1_wptr : %08x\n"
779 " inbox1_size : %08x\n"
780 " inbox0_rptr : %08x\n"
781 " inbox0_wptr : %08x\n"
782 " inbox0_size : %08x\n"
784 " is_soft_reset : %d\n"
785 " is_secure_reset : %d\n"
786 " is_traceport_en : %d\n"
789 diag_data
.dmcub_version
,
790 diag_data
.scratch
[0],
791 diag_data
.scratch
[1],
792 diag_data
.scratch
[2],
793 diag_data
.scratch
[3],
794 diag_data
.scratch
[4],
795 diag_data
.scratch
[5],
796 diag_data
.scratch
[6],
797 diag_data
.scratch
[7],
798 diag_data
.scratch
[8],
799 diag_data
.scratch
[9],
800 diag_data
.scratch
[10],
801 diag_data
.scratch
[11],
802 diag_data
.scratch
[12],
803 diag_data
.scratch
[13],
804 diag_data
.scratch
[14],
805 diag_data
.scratch
[15],
807 diag_data
.undefined_address_fault_addr
,
808 diag_data
.inst_fetch_fault_addr
,
809 diag_data
.data_write_fault_addr
,
810 diag_data
.inbox1_rptr
,
811 diag_data
.inbox1_wptr
,
812 diag_data
.inbox1_size
,
813 diag_data
.inbox0_rptr
,
814 diag_data
.inbox0_wptr
,
815 diag_data
.inbox0_size
,
816 diag_data
.is_dmcub_enabled
,
817 diag_data
.is_dmcub_soft_reset
,
818 diag_data
.is_dmcub_secure_reset
,
819 diag_data
.is_traceport_en
,
820 diag_data
.is_cw0_enabled
,
821 diag_data
.is_cw6_enabled
);