Merge tag 'amd-drm-next-6.1-2022-09-08' of https://gitlab.freedesktop.org/agd5f/linux...
[linux-2.6-block.git] / drivers / gpu / drm / amd / display / dc / dc_dmub_srv.c
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"
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"
33
34 #define CTX dc_dmub_srv->ctx
35 #define DC_LOGGER CTX->logger
36
37 static void dc_dmub_srv_construct(struct dc_dmub_srv *dc_srv, struct dc *dc,
38                                   struct dmub_srv *dmub)
39 {
40         dc_srv->dmub = dmub;
41         dc_srv->ctx = dc->ctx;
42 }
43
44 struct dc_dmub_srv *dc_dmub_srv_create(struct dc *dc, struct dmub_srv *dmub)
45 {
46         struct dc_dmub_srv *dc_srv =
47                 kzalloc(sizeof(struct dc_dmub_srv), GFP_KERNEL);
48
49         if (dc_srv == NULL) {
50                 BREAK_TO_DEBUGGER();
51                 return NULL;
52         }
53
54         dc_dmub_srv_construct(dc_srv, dc, dmub);
55
56         return dc_srv;
57 }
58
59 void dc_dmub_srv_destroy(struct dc_dmub_srv **dmub_srv)
60 {
61         if (*dmub_srv) {
62                 kfree(*dmub_srv);
63                 *dmub_srv = NULL;
64         }
65 }
66
67 void dc_dmub_srv_cmd_queue(struct dc_dmub_srv *dc_dmub_srv,
68                            union dmub_rb_cmd *cmd)
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_cmd_queue(dmub, cmd);
75         if (status == DMUB_STATUS_OK)
76                 return;
77
78         if (status != DMUB_STATUS_QUEUE_FULL)
79                 goto error;
80
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);
84
85         /* Requeue the command. */
86         status = dmub_srv_cmd_queue(dmub, cmd);
87         if (status == DMUB_STATUS_OK)
88                 return;
89
90 error:
91         DC_ERROR("Error queuing DMUB command: status=%d\n", status);
92         dc_dmub_srv_log_diagnostic_data(dc_dmub_srv);
93 }
94
95 void dc_dmub_srv_cmd_execute(struct dc_dmub_srv *dc_dmub_srv)
96 {
97         struct dmub_srv *dmub = dc_dmub_srv->dmub;
98         struct dc_context *dc_ctx = dc_dmub_srv->ctx;
99         enum dmub_status status;
100
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);
105         }
106 }
107
108 void dc_dmub_srv_wait_idle(struct dc_dmub_srv *dc_dmub_srv)
109 {
110         struct dmub_srv *dmub = dc_dmub_srv->dmub;
111         struct dc_context *dc_ctx = dc_dmub_srv->ctx;
112         enum dmub_status status;
113
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);
118         }
119 }
120
121 void dc_dmub_srv_clear_inbox0_ack(struct dc_dmub_srv *dmub_srv)
122 {
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;
126
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);
131         }
132 }
133
134 void dc_dmub_srv_wait_for_inbox0_ack(struct dc_dmub_srv *dmub_srv)
135 {
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;
139
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);
144         }
145 }
146
147 void dc_dmub_srv_send_inbox0_cmd(struct dc_dmub_srv *dmub_srv,
148                 union dmub_inbox0_data_register data)
149 {
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;
153
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);
158         }
159 }
160
161 bool dc_dmub_srv_cmd_with_reply_data(struct dc_dmub_srv *dc_dmub_srv, union dmub_rb_cmd *cmd)
162 {
163         struct dmub_srv *dmub;
164         enum dmub_status status;
165
166         if (!dc_dmub_srv || !dc_dmub_srv->dmub)
167                 return false;
168
169         dmub = dc_dmub_srv->dmub;
170
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);
174                 return false;
175         }
176
177         return true;
178 }
179
180 void dc_dmub_srv_wait_phy_init(struct dc_dmub_srv *dc_dmub_srv)
181 {
182         struct dmub_srv *dmub = dc_dmub_srv->dmub;
183         struct dc_context *dc_ctx = dc_dmub_srv->ctx;
184         enum dmub_status status;
185
186         for (;;) {
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 */
191                         break;
192
193                 DC_ERROR("DMCUB PHY init failed: status=%d\n", status);
194                 ASSERT(0);
195
196                 if (status != DMUB_STATUS_TIMEOUT)
197                         /*
198                          * Server likely initialized or we don't have
199                          * DMCUB HW support - this won't end.
200                          */
201                         break;
202
203                 /* Continue spinning so we don't hang the ASIC. */
204         }
205 }
206
207 bool dc_dmub_srv_notify_stream_mask(struct dc_dmub_srv *dc_dmub_srv,
208                                     unsigned int stream_mask)
209 {
210         struct dmub_srv *dmub;
211         const uint32_t timeout = 30;
212
213         if (!dc_dmub_srv || !dc_dmub_srv->dmub)
214                 return false;
215
216         dmub = dc_dmub_srv->dmub;
217
218         return dmub_srv_send_gpint_command(
219                        dmub, DMUB_GPINT__IDLE_OPT_NOTIFY_STREAM_MASK,
220                        stream_mask, timeout) == DMUB_STATUS_OK;
221 }
222
223 bool dc_dmub_srv_is_restore_required(struct dc_dmub_srv *dc_dmub_srv)
224 {
225         struct dmub_srv *dmub;
226         struct dc_context *dc_ctx;
227         union dmub_fw_boot_status boot_status;
228         enum dmub_status status;
229
230         if (!dc_dmub_srv || !dc_dmub_srv->dmub)
231                 return false;
232
233         dmub = dc_dmub_srv->dmub;
234         dc_ctx = dc_dmub_srv->ctx;
235
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);
239                 return false;
240         }
241
242         return boot_status.bits.restore_required;
243 }
244
245 bool dc_dmub_srv_get_dmub_outbox0_msg(const struct dc *dc, struct dmcub_trace_buf_entry *entry)
246 {
247         struct dmub_srv *dmub = dc->ctx->dmub_srv->dmub;
248         return dmub_srv_get_outbox0_msg(dmub, entry);
249 }
250
251 void dc_dmub_trace_event_control(struct dc *dc, bool enable)
252 {
253         dm_helpers_dmub_outbox_interrupt_control(dc->ctx, enable);
254 }
255
256 void dc_dmub_srv_drr_update_cmd(struct dc *dc, uint32_t tg_inst, uint32_t vtotal_min, uint32_t vtotal_max)
257 {
258         union dmub_rb_cmd cmd = { 0 };
259
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;
265
266         cmd.drr_update.header.payload_bytes = sizeof(cmd.drr_update) - sizeof(cmd.drr_update.header);
267
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);
272 }
273
274 void dc_dmub_srv_set_drr_manual_trigger_cmd(struct dc *dc, uint32_t tg_inst)
275 {
276         union dmub_rb_cmd cmd = { 0 };
277
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;
281
282         cmd.drr_update.header.payload_bytes = sizeof(cmd.drr_update) - sizeof(cmd.drr_update.header);
283
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);
288 }
289
290 static uint8_t dc_dmub_srv_get_pipes_for_stream(struct dc *dc, struct dc_stream_state *stream)
291 {
292         uint8_t pipes = 0;
293         int i = 0;
294
295         for (i = 0; i < MAX_PIPES; i++) {
296                 struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];
297
298                 if (pipe->stream == stream && pipe->stream_res.tg)
299                         pipes = i;
300         }
301         return pipes;
302 }
303
304 static int dc_dmub_srv_get_timing_generator_offset(struct dc *dc, struct dc_stream_state *stream)
305 {
306         int  tg_inst = 0;
307         int i = 0;
308
309         for (i = 0; i < MAX_PIPES; i++) {
310                 struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];
311
312                 if (pipe->stream == stream && pipe->stream_res.tg) {
313                         tg_inst = pipe->stream_res.tg->inst;
314                         break;
315                 }
316         }
317         return tg_inst;
318 }
319
320 bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, struct dc_state *context)
321 {
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;
324         int i = 0;
325         int ramp_up_num_steps = 1; // TODO: Ramp is currently disabled. Reenable it.
326         uint8_t visual_confirm_enabled;
327
328         if (dc == NULL)
329                 return false;
330
331         visual_confirm_enabled = dc->debug.visual_confirm == VISUAL_CONFIRM_FAMS;
332
333         // Format command.
334         cmd.fw_assisted_mclk_switch.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH;
335         cmd.fw_assisted_mclk_switch.header.sub_type = DMUB_CMD__FAMS_SETUP_FW_CTRL;
336         cmd.fw_assisted_mclk_switch.config_data.fams_enabled = should_manage_pstate;
337         cmd.fw_assisted_mclk_switch.config_data.visual_confirm_enabled = visual_confirm_enabled;
338
339         for (i = 0; context && i < context->stream_count; i++) {
340                 struct dc_stream_state *stream = context->streams[i];
341                 uint8_t min_refresh_in_hz = (stream->timing.min_refresh_in_uhz + 999999) / 1000000;
342                 int  tg_inst = dc_dmub_srv_get_timing_generator_offset(dc, stream);
343
344                 config_data->pipe_data[tg_inst].pix_clk_100hz = stream->timing.pix_clk_100hz;
345                 config_data->pipe_data[tg_inst].min_refresh_in_hz = min_refresh_in_hz;
346                 config_data->pipe_data[tg_inst].max_ramp_step = ramp_up_num_steps;
347                 config_data->pipe_data[tg_inst].pipes = dc_dmub_srv_get_pipes_for_stream(dc, stream);
348         }
349
350         cmd.fw_assisted_mclk_switch.header.payload_bytes =
351                 sizeof(cmd.fw_assisted_mclk_switch) - sizeof(cmd.fw_assisted_mclk_switch.header);
352
353         // Send the command to the DMCUB.
354         dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
355         dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
356         dc_dmub_srv_wait_idle(dc->ctx->dmub_srv);
357
358         return true;
359 }
360
361 void dc_dmub_srv_query_caps_cmd(struct dmub_srv *dmub)
362 {
363         union dmub_rb_cmd cmd = { 0 };
364         enum dmub_status status;
365
366         if (!dmub) {
367                 return;
368         }
369
370         memset(&cmd, 0, sizeof(cmd));
371
372         /* Prepare fw command */
373         cmd.query_feature_caps.header.type = DMUB_CMD__QUERY_FEATURE_CAPS;
374         cmd.query_feature_caps.header.sub_type = 0;
375         cmd.query_feature_caps.header.ret_status = 1;
376         cmd.query_feature_caps.header.payload_bytes = sizeof(struct dmub_cmd_query_feature_caps_data);
377
378         /* Send command to fw */
379         status = dmub_srv_cmd_with_reply_data(dmub, &cmd);
380
381         ASSERT(status == DMUB_STATUS_OK);
382
383         /* If command was processed, copy feature caps to dmub srv */
384         if (status == DMUB_STATUS_OK &&
385             cmd.query_feature_caps.header.ret_status == 0) {
386                 memcpy(&dmub->feature_caps,
387                        &cmd.query_feature_caps.query_feature_caps_data,
388                        sizeof(struct dmub_feature_caps));
389         }
390 }
391
392 void dc_dmub_srv_get_visual_confirm_color_cmd(struct dc *dc, struct pipe_ctx *pipe_ctx)
393 {
394         union dmub_rb_cmd cmd = { 0 };
395         enum dmub_status status;
396         unsigned int panel_inst = 0;
397
398         dc_get_edp_link_panel_inst(dc, pipe_ctx->stream->link, &panel_inst);
399
400         memset(&cmd, 0, sizeof(cmd));
401
402         // Prepare fw command
403         cmd.visual_confirm_color.header.type = DMUB_CMD__GET_VISUAL_CONFIRM_COLOR;
404         cmd.visual_confirm_color.header.sub_type = 0;
405         cmd.visual_confirm_color.header.ret_status = 1;
406         cmd.visual_confirm_color.header.payload_bytes = sizeof(struct dmub_cmd_visual_confirm_color_data);
407         cmd.visual_confirm_color.visual_confirm_color_data.visual_confirm_color.panel_inst = panel_inst;
408
409         // Send command to fw
410         status = dmub_srv_cmd_with_reply_data(dc->ctx->dmub_srv->dmub, &cmd);
411
412         ASSERT(status == DMUB_STATUS_OK);
413
414         // If command was processed, copy feature caps to dmub srv
415         if (status == DMUB_STATUS_OK &&
416                 cmd.visual_confirm_color.header.ret_status == 0) {
417                 memcpy(&dc->ctx->dmub_srv->dmub->visual_confirm_color,
418                         &cmd.visual_confirm_color.visual_confirm_color_data,
419                         sizeof(struct dmub_visual_confirm_color));
420         }
421 }
422
423 #ifdef CONFIG_DRM_AMD_DC_DCN
424 /**
425  * ***********************************************************************************************
426  * populate_subvp_cmd_drr_info: Helper to populate DRR pipe info for the DMCUB subvp command
427  *
428  * Populate the DMCUB SubVP command with DRR pipe info. All the information required for calculating
429  * the SubVP + DRR microschedule is populated here.
430  *
431  * High level algorithm:
432  * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe
433  * 2. Calculate the min and max vtotal which supports SubVP + DRR microschedule
434  * 3. Populate the drr_info with the min and max supported vtotal values
435  *
436  * @param [in] dc: current dc state
437  * @param [in] subvp_pipe: pipe_ctx for the SubVP pipe
438  * @param [in] vblank_pipe: pipe_ctx for the DRR pipe
439  * @param [in] pipe_data: Pipe data which stores the VBLANK/DRR info
440  *
441  * @return: void
442  *
443  * ***********************************************************************************************
444  */
445 static void populate_subvp_cmd_drr_info(struct dc *dc,
446                 struct pipe_ctx *subvp_pipe,
447                 struct pipe_ctx *vblank_pipe,
448                 struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data)
449 {
450         struct dc_crtc_timing *main_timing = &subvp_pipe->stream->timing;
451         struct dc_crtc_timing *phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing;
452         struct dc_crtc_timing *drr_timing = &vblank_pipe->stream->timing;
453         int16_t drr_frame_us = 0;
454         int16_t min_drr_supported_us = 0;
455         int16_t max_drr_supported_us = 0;
456         int16_t max_drr_vblank_us = 0;
457         int16_t max_drr_mallregion_us = 0;
458         int16_t mall_region_us = 0;
459         int16_t prefetch_us = 0;
460         int16_t subvp_active_us = 0;
461         int16_t drr_active_us = 0;
462         int16_t min_vtotal_supported = 0;
463         int16_t max_vtotal_supported = 0;
464
465         pipe_data->pipe_config.vblank_data.drr_info.drr_in_use = true;
466         pipe_data->pipe_config.vblank_data.drr_info.use_ramping = false; // for now don't use ramping
467         pipe_data->pipe_config.vblank_data.drr_info.drr_window_size_ms = 4; // hardcode 4ms DRR window for now
468
469         drr_frame_us = div64_s64(drr_timing->v_total * drr_timing->h_total,
470                                  (int64_t)(drr_timing->pix_clk_100hz * 100) * 1000000);
471         // P-State allow width and FW delays already included phantom_timing->v_addressable
472         mall_region_us = div64_s64(phantom_timing->v_addressable * phantom_timing->h_total,
473                                    (int64_t)(phantom_timing->pix_clk_100hz * 100) * 1000000);
474         min_drr_supported_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US;
475         min_vtotal_supported = div64_s64(drr_timing->pix_clk_100hz * 100 *
476                                          (div64_s64((int64_t)min_drr_supported_us, 1000000)),
477                                          (int64_t)drr_timing->h_total);
478
479         prefetch_us = div64_s64((phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total,
480                                 (int64_t)(phantom_timing->pix_clk_100hz * 100) * 1000000 +
481                                 dc->caps.subvp_prefetch_end_to_mall_start_us);
482         subvp_active_us = div64_s64(main_timing->v_addressable * main_timing->h_total,
483                                     (int64_t)(main_timing->pix_clk_100hz * 100) * 1000000);
484         drr_active_us = div64_s64(drr_timing->v_addressable * drr_timing->h_total,
485                                   (int64_t)(drr_timing->pix_clk_100hz * 100) * 1000000);
486         max_drr_vblank_us = div64_s64((int64_t)(subvp_active_us - prefetch_us - drr_active_us), 2) + drr_active_us;
487         max_drr_mallregion_us = subvp_active_us - prefetch_us - mall_region_us;
488         max_drr_supported_us = max_drr_vblank_us > max_drr_mallregion_us ? max_drr_vblank_us : max_drr_mallregion_us;
489         max_vtotal_supported = div64_s64(drr_timing->pix_clk_100hz * 100 * (div64_s64((int64_t)max_drr_supported_us, 1000000)),
490                                          (int64_t)drr_timing->h_total);
491
492         pipe_data->pipe_config.vblank_data.drr_info.min_vtotal_supported = min_vtotal_supported;
493         pipe_data->pipe_config.vblank_data.drr_info.max_vtotal_supported = max_vtotal_supported;
494 }
495
496 /**
497  * ***********************************************************************************************
498  * populate_subvp_cmd_vblank_pipe_info: Helper to populate VBLANK pipe info for the DMUB subvp command
499  *
500  * Populate the DMCUB SubVP command with VBLANK pipe info. All the information required to calculate
501  * the microschedule for SubVP + VBLANK case is stored in the pipe_data (subvp_data and vblank_data).
502  * Also check if the VBLANK pipe is a DRR display -- if it is make a call to populate drr_info.
503  *
504  * @param [in] dc: current dc state
505  * @param [in] context: new dc state
506  * @param [in] cmd: DMUB cmd to be populated with SubVP info
507  * @param [in] vblank_pipe: pipe_ctx for the VBLANK pipe
508  * @param [in] cmd_pipe_index: index for the pipe array in DMCUB SubVP cmd
509  *
510  * @return: void
511  *
512  * ***********************************************************************************************
513  */
514 static void populate_subvp_cmd_vblank_pipe_info(struct dc *dc,
515                 struct dc_state *context,
516                 union dmub_rb_cmd *cmd,
517                 struct pipe_ctx *vblank_pipe,
518                 uint8_t cmd_pipe_index)
519 {
520         uint32_t i;
521         struct pipe_ctx *pipe = NULL;
522         struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data =
523                         &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[cmd_pipe_index];
524
525         // Find the SubVP pipe
526         for (i = 0; i < dc->res_pool->pipe_count; i++) {
527                 pipe = &context->res_ctx.pipe_ctx[i];
528
529                 // We check for master pipe, but it shouldn't matter since we only need
530                 // the pipe for timing info (stream should be same for any pipe splits)
531                 if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)
532                         continue;
533
534                 // Find the SubVP pipe
535                 if (pipe->stream->mall_stream_config.type == SUBVP_MAIN)
536                         break;
537         }
538
539         pipe_data->mode = VBLANK;
540         pipe_data->pipe_config.vblank_data.pix_clk_100hz = vblank_pipe->stream->timing.pix_clk_100hz;
541         pipe_data->pipe_config.vblank_data.vblank_start = vblank_pipe->stream->timing.v_total -
542                                                         vblank_pipe->stream->timing.v_front_porch;
543         pipe_data->pipe_config.vblank_data.vtotal = vblank_pipe->stream->timing.v_total;
544         pipe_data->pipe_config.vblank_data.htotal = vblank_pipe->stream->timing.h_total;
545         pipe_data->pipe_config.vblank_data.vblank_pipe_index = vblank_pipe->pipe_idx;
546         pipe_data->pipe_config.vblank_data.vstartup_start = vblank_pipe->pipe_dlg_param.vstartup_start;
547         pipe_data->pipe_config.vblank_data.vblank_end =
548                         vblank_pipe->stream->timing.v_total - vblank_pipe->stream->timing.v_front_porch - vblank_pipe->stream->timing.v_addressable;
549
550         if (vblank_pipe->stream->ignore_msa_timing_param)
551                 populate_subvp_cmd_drr_info(dc, pipe, vblank_pipe, pipe_data);
552 }
553
554 /**
555  * ***********************************************************************************************
556  * update_subvp_prefetch_end_to_mall_start: Helper for SubVP + SubVP case
557  *
558  * For SubVP + SubVP, we use a single vertical interrupt to start the microschedule for both
559  * SubVP pipes. In order for this to work correctly, the MALL REGION of both SubVP pipes must
560  * start at the same time. This function lengthens the prefetch end to mall start delay of the
561  * SubVP pipe that has the shorter prefetch so that both MALL REGION's will start at the same time.
562  *
563  * @param [in] dc: current dc state
564  * @param [in] context: new dc state
565  * @param [in] cmd: DMUB cmd to be populated with SubVP info
566  * @param [in] subvp_pipes: Array of SubVP pipes (should always be length 2)
567  *
568  * @return: void
569  *
570  * ***********************************************************************************************
571  */
572 static void update_subvp_prefetch_end_to_mall_start(struct dc *dc,
573                 struct dc_state *context,
574                 union dmub_rb_cmd *cmd,
575                 struct pipe_ctx *subvp_pipes[])
576 {
577         uint32_t subvp0_prefetch_us = 0;
578         uint32_t subvp1_prefetch_us = 0;
579         uint32_t prefetch_delta_us = 0;
580         struct dc_crtc_timing *phantom_timing0 = &subvp_pipes[0]->stream->mall_stream_config.paired_stream->timing;
581         struct dc_crtc_timing *phantom_timing1 = &subvp_pipes[1]->stream->mall_stream_config.paired_stream->timing;
582         struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data = NULL;
583
584         subvp0_prefetch_us = div64_s64((phantom_timing0->v_total - phantom_timing0->v_front_porch) * phantom_timing0->h_total,
585                                        (int64_t)(phantom_timing0->pix_clk_100hz * 100) * 1000000 + dc->caps.subvp_prefetch_end_to_mall_start_us);
586         subvp1_prefetch_us = div64_s64((phantom_timing1->v_total - phantom_timing1->v_front_porch) * phantom_timing1->h_total,
587                                        (int64_t)(phantom_timing1->pix_clk_100hz * 100) * 1000000 + dc->caps.subvp_prefetch_end_to_mall_start_us);
588
589         // Whichever SubVP PIPE has the smaller prefetch (including the prefetch end to mall start time)
590         // should increase it's prefetch time to match the other
591         if (subvp0_prefetch_us > subvp1_prefetch_us) {
592                 pipe_data = &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[1];
593                 prefetch_delta_us = subvp0_prefetch_us - subvp1_prefetch_us;
594                 pipe_data->pipe_config.subvp_data.prefetch_to_mall_start_lines =
595                         div64_s64(((div64_s64((int64_t)(dc->caps.subvp_prefetch_end_to_mall_start_us + prefetch_delta_us), 1000000)) *
596                                    (phantom_timing1->pix_clk_100hz * 100) + phantom_timing1->h_total - 1),
597                                   (int64_t)phantom_timing1->h_total);
598         } else if (subvp1_prefetch_us >  subvp0_prefetch_us) {
599                 pipe_data = &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[0];
600                 prefetch_delta_us = subvp1_prefetch_us - subvp0_prefetch_us;
601                 pipe_data->pipe_config.subvp_data.prefetch_to_mall_start_lines =
602                         div64_s64(((div64_s64((int64_t)(dc->caps.subvp_prefetch_end_to_mall_start_us + prefetch_delta_us), 1000000)) *
603                                    (phantom_timing0->pix_clk_100hz * 100) + phantom_timing0->h_total - 1),
604                                   (int64_t)phantom_timing0->h_total);
605         }
606 }
607
608 /**
609  * ***************************************************************************************
610  * setup_subvp_dmub_command: Helper to populate the SubVP pipe info for the DMUB subvp command
611  *
612  * Populate the DMCUB SubVP command with SubVP pipe info. All the information required to
613  * calculate the microschedule for the SubVP pipe is stored in the pipe_data of the DMCUB
614  * SubVP command.
615  *
616  * @param [in] dc: current dc state
617  * @param [in] context: new dc state
618  * @param [in] cmd: DMUB cmd to be populated with SubVP info
619  * @param [in] subvp_pipe: pipe_ctx for the SubVP pipe
620  * @param [in] cmd_pipe_index: index for the pipe array in DMCUB SubVP cmd
621  *
622  * @return: void
623  *
624  * ***************************************************************************************
625  */
626 static void populate_subvp_cmd_pipe_info(struct dc *dc,
627                 struct dc_state *context,
628                 union dmub_rb_cmd *cmd,
629                 struct pipe_ctx *subvp_pipe,
630                 uint8_t cmd_pipe_index)
631 {
632         uint32_t j;
633         struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data =
634                         &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[cmd_pipe_index];
635         struct dc_crtc_timing *main_timing = &subvp_pipe->stream->timing;
636         struct dc_crtc_timing *phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing;
637         uint32_t out_num_stream, out_den_stream, out_num_plane, out_den_plane, out_num, out_den;
638
639         pipe_data->mode = SUBVP;
640         pipe_data->pipe_config.subvp_data.pix_clk_100hz = subvp_pipe->stream->timing.pix_clk_100hz;
641         pipe_data->pipe_config.subvp_data.htotal = subvp_pipe->stream->timing.h_total;
642         pipe_data->pipe_config.subvp_data.vtotal = subvp_pipe->stream->timing.v_total;
643         pipe_data->pipe_config.subvp_data.main_vblank_start =
644                         main_timing->v_total - main_timing->v_front_porch;
645         pipe_data->pipe_config.subvp_data.main_vblank_end =
646                         main_timing->v_total - main_timing->v_front_porch - main_timing->v_addressable;
647         pipe_data->pipe_config.subvp_data.mall_region_lines = phantom_timing->v_addressable;
648         pipe_data->pipe_config.subvp_data.main_pipe_index = subvp_pipe->pipe_idx;
649         pipe_data->pipe_config.subvp_data.is_drr = subvp_pipe->stream->ignore_msa_timing_param;
650
651         /* Calculate the scaling factor from the src and dst height.
652          * e.g. If 3840x2160 being downscaled to 1920x1080, the scaling factor is 1/2.
653          * Reduce the fraction 1080/2160 = 1/2 for the "scaling factor"
654          *
655          * Make sure to combine stream and plane scaling together.
656          */
657         reduce_fraction(subvp_pipe->stream->src.height, subvp_pipe->stream->dst.height,
658                         &out_num_stream, &out_den_stream);
659         reduce_fraction(subvp_pipe->plane_state->src_rect.height, subvp_pipe->plane_state->dst_rect.height,
660                         &out_num_plane, &out_den_plane);
661         reduce_fraction(out_num_stream * out_num_plane, out_den_stream * out_den_plane, &out_num, &out_den);
662         pipe_data->pipe_config.subvp_data.scale_factor_numerator = out_num;
663         pipe_data->pipe_config.subvp_data.scale_factor_denominator = out_den;
664
665         // Prefetch lines is equal to VACTIVE + BP + VSYNC
666         pipe_data->pipe_config.subvp_data.prefetch_lines =
667                         phantom_timing->v_total - phantom_timing->v_front_porch;
668
669         // Round up
670         pipe_data->pipe_config.subvp_data.prefetch_to_mall_start_lines =
671                 div64_s64(((div64_s64((int64_t)dc->caps.subvp_prefetch_end_to_mall_start_us, 1000000)) *
672                            (phantom_timing->pix_clk_100hz * 100) + phantom_timing->h_total - 1),
673                           (int64_t)phantom_timing->h_total);
674         pipe_data->pipe_config.subvp_data.processing_delay_lines =
675                 div64_s64(((div64_s64((int64_t)dc->caps.subvp_fw_processing_delay_us, 1000000)) *
676                            (phantom_timing->pix_clk_100hz * 100) + phantom_timing->h_total - 1),
677                           (int64_t)phantom_timing->h_total);
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) {
683                         pipe_data->pipe_config.subvp_data.phantom_pipe_index = phantom_pipe->pipe_idx;
684                         break;
685                 }
686         }
687 }
688
689 /**
690  * ***************************************************************************************
691  * dc_dmub_setup_subvp_dmub_command: Populate the DMCUB SubVP command
692  *
693  * This function loops through each pipe and populates the DMUB
694  * SubVP CMD info based on the pipe (e.g. SubVP, VBLANK).
695  *
696  * @param [in] dc: current dc state
697  * @param [in] context: new dc state
698  * @param [in] cmd: DMUB cmd to be populated with SubVP info
699  *
700  * @return: void
701  *
702  * ***************************************************************************************
703  */
704 void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
705                 struct dc_state *context,
706                 bool enable)
707 {
708         uint8_t cmd_pipe_index = 0;
709         uint32_t i, pipe_idx;
710         uint8_t subvp_count = 0;
711         union dmub_rb_cmd cmd;
712         struct pipe_ctx *subvp_pipes[2];
713         uint32_t wm_val_refclk = 0;
714
715         memset(&cmd, 0, sizeof(cmd));
716         // FW command for SUBVP
717         cmd.fw_assisted_mclk_switch_v2.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH;
718         cmd.fw_assisted_mclk_switch_v2.header.sub_type = DMUB_CMD__HANDLE_SUBVP_CMD;
719         cmd.fw_assisted_mclk_switch_v2.header.payload_bytes =
720                         sizeof(cmd.fw_assisted_mclk_switch_v2) - sizeof(cmd.fw_assisted_mclk_switch_v2.header);
721
722         for (i = 0; i < dc->res_pool->pipe_count; i++) {
723                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
724
725                 if (!pipe->stream)
726                         continue;
727
728                 if (pipe->plane_state && !pipe->top_pipe &&
729                                 pipe->stream->mall_stream_config.type == SUBVP_MAIN)
730                         subvp_pipes[subvp_count++] = pipe;
731         }
732
733         if (enable) {
734                 // For each pipe that is a "main" SUBVP pipe, fill in pipe data for DMUB SUBVP cmd
735                 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
736                         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
737
738                         if (!pipe->stream)
739                                 continue;
740
741                         if (pipe->plane_state && pipe->stream->mall_stream_config.paired_stream &&
742                                         pipe->stream->mall_stream_config.type == SUBVP_MAIN) {
743                                 populate_subvp_cmd_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++);
744                         } else if (pipe->plane_state && pipe->stream->mall_stream_config.type == SUBVP_NONE) {
745                                 // Don't need to check for ActiveDRAMClockChangeMargin < 0, not valid in cases where
746                                 // we run through DML without calculating "natural" P-state support
747                                 populate_subvp_cmd_vblank_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++);
748
749                         }
750                         pipe_idx++;
751                 }
752                 if (subvp_count == 2) {
753                         update_subvp_prefetch_end_to_mall_start(dc, context, &cmd, subvp_pipes);
754                 }
755                 cmd.fw_assisted_mclk_switch_v2.config_data.pstate_allow_width_us = dc->caps.subvp_pstate_allow_width_us;
756                 cmd.fw_assisted_mclk_switch_v2.config_data.vertical_int_margin_us = dc->caps.subvp_vertical_int_margin_us;
757
758                 // Store the original watermark value for this SubVP config so we can lower it when the
759                 // MCLK switch starts
760                 wm_val_refclk = context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns *
761                                 dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 / 1000;
762
763                 cmd.fw_assisted_mclk_switch_v2.config_data.watermark_a_cache = wm_val_refclk < 0xFFFF ? wm_val_refclk : 0xFFFF;
764         }
765         dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
766         dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
767         dc_dmub_srv_wait_idle(dc->ctx->dmub_srv);
768 }
769 #endif
770
771 bool dc_dmub_srv_get_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv, struct dmub_diagnostic_data *diag_data)
772 {
773         if (!dc_dmub_srv || !dc_dmub_srv->dmub || !diag_data)
774                 return false;
775         return dmub_srv_get_diagnostic_data(dc_dmub_srv->dmub, diag_data);
776 }
777
778 void dc_dmub_srv_log_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv)
779 {
780         struct dmub_diagnostic_data diag_data = {0};
781
782         if (!dc_dmub_srv || !dc_dmub_srv->dmub) {
783                 DC_LOG_ERROR("%s: invalid parameters.", __func__);
784                 return;
785         }
786
787         if (!dc_dmub_srv_get_diagnostic_data(dc_dmub_srv, &diag_data)) {
788                 DC_LOG_ERROR("%s: dc_dmub_srv_get_diagnostic_data failed.", __func__);
789                 return;
790         }
791
792         DC_LOG_DEBUG(
793                 "DMCUB STATE\n"
794                 "    dmcub_version      : %08x\n"
795                 "    scratch  [0]       : %08x\n"
796                 "    scratch  [1]       : %08x\n"
797                 "    scratch  [2]       : %08x\n"
798                 "    scratch  [3]       : %08x\n"
799                 "    scratch  [4]       : %08x\n"
800                 "    scratch  [5]       : %08x\n"
801                 "    scratch  [6]       : %08x\n"
802                 "    scratch  [7]       : %08x\n"
803                 "    scratch  [8]       : %08x\n"
804                 "    scratch  [9]       : %08x\n"
805                 "    scratch [10]       : %08x\n"
806                 "    scratch [11]       : %08x\n"
807                 "    scratch [12]       : %08x\n"
808                 "    scratch [13]       : %08x\n"
809                 "    scratch [14]       : %08x\n"
810                 "    scratch [15]       : %08x\n"
811                 "    pc                 : %08x\n"
812                 "    unk_fault_addr     : %08x\n"
813                 "    inst_fault_addr    : %08x\n"
814                 "    data_fault_addr    : %08x\n"
815                 "    inbox1_rptr        : %08x\n"
816                 "    inbox1_wptr        : %08x\n"
817                 "    inbox1_size        : %08x\n"
818                 "    inbox0_rptr        : %08x\n"
819                 "    inbox0_wptr        : %08x\n"
820                 "    inbox0_size        : %08x\n"
821                 "    is_enabled         : %d\n"
822                 "    is_soft_reset      : %d\n"
823                 "    is_secure_reset    : %d\n"
824                 "    is_traceport_en    : %d\n"
825                 "    is_cw0_en          : %d\n"
826                 "    is_cw6_en          : %d\n",
827                 diag_data.dmcub_version,
828                 diag_data.scratch[0],
829                 diag_data.scratch[1],
830                 diag_data.scratch[2],
831                 diag_data.scratch[3],
832                 diag_data.scratch[4],
833                 diag_data.scratch[5],
834                 diag_data.scratch[6],
835                 diag_data.scratch[7],
836                 diag_data.scratch[8],
837                 diag_data.scratch[9],
838                 diag_data.scratch[10],
839                 diag_data.scratch[11],
840                 diag_data.scratch[12],
841                 diag_data.scratch[13],
842                 diag_data.scratch[14],
843                 diag_data.scratch[15],
844                 diag_data.pc,
845                 diag_data.undefined_address_fault_addr,
846                 diag_data.inst_fetch_fault_addr,
847                 diag_data.data_write_fault_addr,
848                 diag_data.inbox1_rptr,
849                 diag_data.inbox1_wptr,
850                 diag_data.inbox1_size,
851                 diag_data.inbox0_rptr,
852                 diag_data.inbox0_wptr,
853                 diag_data.inbox0_size,
854                 diag_data.is_dmcub_enabled,
855                 diag_data.is_dmcub_soft_reset,
856                 diag_data.is_dmcub_secure_reset,
857                 diag_data.is_traceport_en,
858                 diag_data.is_cw0_enabled,
859                 diag_data.is_cw6_enabled);
860 }