Merge tag 'drm-misc-fixes-2022-08-16' of git://anongit.freedesktop.org/drm/drm-misc...
[sfrench/cifs-2.6.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
33 #define CTX dc_dmub_srv->ctx
34 #define DC_LOGGER CTX->logger
35
36 static void dc_dmub_srv_construct(struct dc_dmub_srv *dc_srv, struct dc *dc,
37                                   struct dmub_srv *dmub)
38 {
39         dc_srv->dmub = dmub;
40         dc_srv->ctx = dc->ctx;
41 }
42
43 struct dc_dmub_srv *dc_dmub_srv_create(struct dc *dc, struct dmub_srv *dmub)
44 {
45         struct dc_dmub_srv *dc_srv =
46                 kzalloc(sizeof(struct dc_dmub_srv), GFP_KERNEL);
47
48         if (dc_srv == NULL) {
49                 BREAK_TO_DEBUGGER();
50                 return NULL;
51         }
52
53         dc_dmub_srv_construct(dc_srv, dc, dmub);
54
55         return dc_srv;
56 }
57
58 void dc_dmub_srv_destroy(struct dc_dmub_srv **dmub_srv)
59 {
60         if (*dmub_srv) {
61                 kfree(*dmub_srv);
62                 *dmub_srv = NULL;
63         }
64 }
65
66 void dc_dmub_srv_cmd_queue(struct dc_dmub_srv *dc_dmub_srv,
67                            union dmub_rb_cmd *cmd)
68 {
69         struct dmub_srv *dmub = dc_dmub_srv->dmub;
70         struct dc_context *dc_ctx = dc_dmub_srv->ctx;
71         enum dmub_status status;
72
73         status = dmub_srv_cmd_queue(dmub, cmd);
74         if (status == DMUB_STATUS_OK)
75                 return;
76
77         if (status != DMUB_STATUS_QUEUE_FULL)
78                 goto error;
79
80         /* Execute and wait for queue to become empty again. */
81         dc_dmub_srv_cmd_execute(dc_dmub_srv);
82         dc_dmub_srv_wait_idle(dc_dmub_srv);
83
84         /* Requeue the command. */
85         status = dmub_srv_cmd_queue(dmub, cmd);
86         if (status == DMUB_STATUS_OK)
87                 return;
88
89 error:
90         DC_ERROR("Error queuing DMUB command: status=%d\n", status);
91         dc_dmub_srv_log_diagnostic_data(dc_dmub_srv);
92 }
93
94 void dc_dmub_srv_cmd_execute(struct dc_dmub_srv *dc_dmub_srv)
95 {
96         struct dmub_srv *dmub = dc_dmub_srv->dmub;
97         struct dc_context *dc_ctx = dc_dmub_srv->ctx;
98         enum dmub_status status;
99
100         status = dmub_srv_cmd_execute(dmub);
101         if (status != DMUB_STATUS_OK) {
102                 DC_ERROR("Error starting DMUB execution: status=%d\n", status);
103                 dc_dmub_srv_log_diagnostic_data(dc_dmub_srv);
104         }
105 }
106
107 void dc_dmub_srv_wait_idle(struct dc_dmub_srv *dc_dmub_srv)
108 {
109         struct dmub_srv *dmub = dc_dmub_srv->dmub;
110         struct dc_context *dc_ctx = dc_dmub_srv->ctx;
111         enum dmub_status status;
112
113         status = dmub_srv_wait_for_idle(dmub, 100000);
114         if (status != DMUB_STATUS_OK) {
115                 DC_ERROR("Error waiting for DMUB idle: status=%d\n", status);
116                 dc_dmub_srv_log_diagnostic_data(dc_dmub_srv);
117         }
118 }
119
120 void dc_dmub_srv_clear_inbox0_ack(struct dc_dmub_srv *dmub_srv)
121 {
122         struct dmub_srv *dmub = dmub_srv->dmub;
123         struct dc_context *dc_ctx = dmub_srv->ctx;
124         enum dmub_status status = DMUB_STATUS_OK;
125
126         status = dmub_srv_clear_inbox0_ack(dmub);
127         if (status != DMUB_STATUS_OK) {
128                 DC_ERROR("Error clearing INBOX0 ack: status=%d\n", status);
129                 dc_dmub_srv_log_diagnostic_data(dmub_srv);
130         }
131 }
132
133 void dc_dmub_srv_wait_for_inbox0_ack(struct dc_dmub_srv *dmub_srv)
134 {
135         struct dmub_srv *dmub = dmub_srv->dmub;
136         struct dc_context *dc_ctx = dmub_srv->ctx;
137         enum dmub_status status = DMUB_STATUS_OK;
138
139         status = dmub_srv_wait_for_inbox0_ack(dmub, 100000);
140         if (status != DMUB_STATUS_OK) {
141                 DC_ERROR("Error waiting for INBOX0 HW Lock Ack\n");
142                 dc_dmub_srv_log_diagnostic_data(dmub_srv);
143         }
144 }
145
146 void dc_dmub_srv_send_inbox0_cmd(struct dc_dmub_srv *dmub_srv,
147                 union dmub_inbox0_data_register data)
148 {
149         struct dmub_srv *dmub = dmub_srv->dmub;
150         struct dc_context *dc_ctx = dmub_srv->ctx;
151         enum dmub_status status = DMUB_STATUS_OK;
152
153         status = dmub_srv_send_inbox0_cmd(dmub, data);
154         if (status != DMUB_STATUS_OK) {
155                 DC_ERROR("Error sending INBOX0 cmd\n");
156                 dc_dmub_srv_log_diagnostic_data(dmub_srv);
157         }
158 }
159
160 bool dc_dmub_srv_cmd_with_reply_data(struct dc_dmub_srv *dc_dmub_srv, union dmub_rb_cmd *cmd)
161 {
162         struct dmub_srv *dmub;
163         enum dmub_status status;
164
165         if (!dc_dmub_srv || !dc_dmub_srv->dmub)
166                 return false;
167
168         dmub = dc_dmub_srv->dmub;
169
170         status = dmub_srv_cmd_with_reply_data(dmub, cmd);
171         if (status != DMUB_STATUS_OK) {
172                 DC_LOG_DEBUG("No reply for DMUB command: status=%d\n", status);
173                 return false;
174         }
175
176         return true;
177 }
178
179 void dc_dmub_srv_wait_phy_init(struct dc_dmub_srv *dc_dmub_srv)
180 {
181         struct dmub_srv *dmub = dc_dmub_srv->dmub;
182         struct dc_context *dc_ctx = dc_dmub_srv->ctx;
183         enum dmub_status status;
184
185         for (;;) {
186                 /* Wait up to a second for PHY init. */
187                 status = dmub_srv_wait_for_phy_init(dmub, 1000000);
188                 if (status == DMUB_STATUS_OK)
189                         /* Initialization OK */
190                         break;
191
192                 DC_ERROR("DMCUB PHY init failed: status=%d\n", status);
193                 ASSERT(0);
194
195                 if (status != DMUB_STATUS_TIMEOUT)
196                         /*
197                          * Server likely initialized or we don't have
198                          * DMCUB HW support - this won't end.
199                          */
200                         break;
201
202                 /* Continue spinning so we don't hang the ASIC. */
203         }
204 }
205
206 bool dc_dmub_srv_notify_stream_mask(struct dc_dmub_srv *dc_dmub_srv,
207                                     unsigned int stream_mask)
208 {
209         struct dmub_srv *dmub;
210         const uint32_t timeout = 30;
211
212         if (!dc_dmub_srv || !dc_dmub_srv->dmub)
213                 return false;
214
215         dmub = dc_dmub_srv->dmub;
216
217         return dmub_srv_send_gpint_command(
218                        dmub, DMUB_GPINT__IDLE_OPT_NOTIFY_STREAM_MASK,
219                        stream_mask, timeout) == DMUB_STATUS_OK;
220 }
221
222 bool dc_dmub_srv_is_restore_required(struct dc_dmub_srv *dc_dmub_srv)
223 {
224         struct dmub_srv *dmub;
225         struct dc_context *dc_ctx;
226         union dmub_fw_boot_status boot_status;
227         enum dmub_status status;
228
229         if (!dc_dmub_srv || !dc_dmub_srv->dmub)
230                 return false;
231
232         dmub = dc_dmub_srv->dmub;
233         dc_ctx = dc_dmub_srv->ctx;
234
235         status = dmub_srv_get_fw_boot_status(dmub, &boot_status);
236         if (status != DMUB_STATUS_OK) {
237                 DC_ERROR("Error querying DMUB boot status: error=%d\n", status);
238                 return false;
239         }
240
241         return boot_status.bits.restore_required;
242 }
243
244 bool dc_dmub_srv_get_dmub_outbox0_msg(const struct dc *dc, struct dmcub_trace_buf_entry *entry)
245 {
246         struct dmub_srv *dmub = dc->ctx->dmub_srv->dmub;
247         return dmub_srv_get_outbox0_msg(dmub, entry);
248 }
249
250 void dc_dmub_trace_event_control(struct dc *dc, bool enable)
251 {
252         dm_helpers_dmub_outbox_interrupt_control(dc->ctx, enable);
253 }
254
255 void dc_dmub_srv_drr_update_cmd(struct dc *dc, uint32_t tg_inst, uint32_t vtotal_min, uint32_t vtotal_max)
256 {
257         union dmub_rb_cmd cmd = { 0 };
258
259         cmd.drr_update.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH;
260         cmd.drr_update.header.sub_type = DMUB_CMD__FAMS_DRR_UPDATE;
261         cmd.drr_update.dmub_optc_state_req.v_total_max = vtotal_max;
262         cmd.drr_update.dmub_optc_state_req.v_total_min = vtotal_min;
263         cmd.drr_update.dmub_optc_state_req.tg_inst = tg_inst;
264
265         cmd.drr_update.header.payload_bytes = sizeof(cmd.drr_update) - sizeof(cmd.drr_update.header);
266
267         // Send the command to the DMCUB.
268         dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
269         dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
270         dc_dmub_srv_wait_idle(dc->ctx->dmub_srv);
271 }
272
273 void dc_dmub_srv_set_drr_manual_trigger_cmd(struct dc *dc, uint32_t tg_inst)
274 {
275         union dmub_rb_cmd cmd = { 0 };
276
277         cmd.drr_update.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH;
278         // TODO: Uncomment once FW headers are promoted
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 = dc->debug.visual_confirm == VISUAL_CONFIRM_FAMS;
327
328         if (dc == NULL)
329                 return false;
330
331         // Format command.
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;
336
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);
341
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);
346         }
347
348         cmd.fw_assisted_mclk_switch.header.payload_bytes =
349                 sizeof(cmd.fw_assisted_mclk_switch) - sizeof(cmd.fw_assisted_mclk_switch.header);
350
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);
355
356         return true;
357 }
358
359 void dc_dmub_srv_query_caps_cmd(struct dmub_srv *dmub)
360 {
361         union dmub_rb_cmd cmd = { 0 };
362         enum dmub_status status;
363
364         if (!dmub) {
365                 return;
366         }
367
368         memset(&cmd, 0, sizeof(cmd));
369
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);
375
376         /* Send command to fw */
377         status = dmub_srv_cmd_with_reply_data(dmub, &cmd);
378
379         ASSERT(status == DMUB_STATUS_OK);
380
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));
387         }
388 }
389
390 #ifdef CONFIG_DRM_AMD_DC_DCN
391 /**
392  * ***********************************************************************************************
393  * populate_subvp_cmd_drr_info: Helper to populate DRR pipe info for the DMCUB subvp command
394  *
395  * Populate the DMCUB SubVP command with DRR pipe info. All the information required for calculating
396  * the SubVP + DRR microschedule is populated here.
397  *
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
402  *
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
407  *
408  * @return: void
409  *
410  * ***********************************************************************************************
411  */
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)
416 {
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;
431
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
435
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);
445
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);
458
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;
461 }
462
463 /**
464  * ***********************************************************************************************
465  * populate_subvp_cmd_vblank_pipe_info: Helper to populate VBLANK pipe info for the DMUB subvp command
466  *
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.
470  *
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
476  *
477  * @return: void
478  *
479  * ***********************************************************************************************
480  */
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)
486 {
487         uint32_t i;
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];
491
492         // Find the SubVP pipe
493         for (i = 0; i < dc->res_pool->pipe_count; i++) {
494                 pipe = &context->res_ctx.pipe_ctx[i];
495
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)
499                         continue;
500
501                 // Find the SubVP pipe
502                 if (pipe->stream->mall_stream_config.type == SUBVP_MAIN)
503                         break;
504         }
505
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;
516
517         if (vblank_pipe->stream->ignore_msa_timing_param)
518                 populate_subvp_cmd_drr_info(dc, pipe, vblank_pipe, pipe_data);
519 }
520
521 /**
522  * ***********************************************************************************************
523  * update_subvp_prefetch_end_to_mall_start: Helper for SubVP + SubVP case
524  *
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.
529  *
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)
534  *
535  * @return: void
536  *
537  * ***********************************************************************************************
538  */
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[])
543 {
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;
550
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);
555
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);
572         }
573 }
574
575 /**
576  * ***************************************************************************************
577  * setup_subvp_dmub_command: Helper to populate the SubVP pipe info for the DMUB subvp command
578  *
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
581  * SubVP command.
582  *
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
588  *
589  * @return: void
590  *
591  * ***************************************************************************************
592  */
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)
598 {
599         uint32_t j;
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
605         pipe_data->mode = SUBVP;
606         pipe_data->pipe_config.subvp_data.pix_clk_100hz = subvp_pipe->stream->timing.pix_clk_100hz;
607         pipe_data->pipe_config.subvp_data.htotal = subvp_pipe->stream->timing.h_total;
608         pipe_data->pipe_config.subvp_data.vtotal = subvp_pipe->stream->timing.v_total;
609         pipe_data->pipe_config.subvp_data.main_vblank_start =
610                         main_timing->v_total - main_timing->v_front_porch;
611         pipe_data->pipe_config.subvp_data.main_vblank_end =
612                         main_timing->v_total - main_timing->v_front_porch - main_timing->v_addressable;
613         pipe_data->pipe_config.subvp_data.mall_region_lines = phantom_timing->v_addressable;
614         pipe_data->pipe_config.subvp_data.main_pipe_index = subvp_pipe->pipe_idx;
615
616         // Prefetch lines is equal to VACTIVE + BP + VSYNC
617         pipe_data->pipe_config.subvp_data.prefetch_lines =
618                         phantom_timing->v_total - phantom_timing->v_front_porch;
619
620         // Round up
621         pipe_data->pipe_config.subvp_data.prefetch_to_mall_start_lines =
622                 div64_s64(((div64_s64((int64_t)dc->caps.subvp_prefetch_end_to_mall_start_us, 1000000)) *
623                            (phantom_timing->pix_clk_100hz * 100) + phantom_timing->h_total - 1),
624                           (int64_t)phantom_timing->h_total);
625         pipe_data->pipe_config.subvp_data.processing_delay_lines =
626                 div64_s64(((div64_s64((int64_t)dc->caps.subvp_fw_processing_delay_us, 1000000)) *
627                            (phantom_timing->pix_clk_100hz * 100) + phantom_timing->h_total - 1),
628                           (int64_t)phantom_timing->h_total);
629         // Find phantom pipe index based on phantom stream
630         for (j = 0; j < dc->res_pool->pipe_count; j++) {
631                 struct pipe_ctx *phantom_pipe = &context->res_ctx.pipe_ctx[j];
632
633                 if (phantom_pipe->stream == subvp_pipe->stream->mall_stream_config.paired_stream) {
634                         pipe_data->pipe_config.subvp_data.phantom_pipe_index = phantom_pipe->pipe_idx;
635                         break;
636                 }
637         }
638 }
639
640 /**
641  * ***************************************************************************************
642  * dc_dmub_setup_subvp_dmub_command: Populate the DMCUB SubVP command
643  *
644  * This function loops through each pipe and populates the DMUB
645  * SubVP CMD info based on the pipe (e.g. SubVP, VBLANK).
646  *
647  * @param [in] dc: current dc state
648  * @param [in] context: new dc state
649  * @param [in] cmd: DMUB cmd to be populated with SubVP info
650  *
651  * @return: void
652  *
653  * ***************************************************************************************
654  */
655 void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
656                 struct dc_state *context,
657                 bool enable)
658 {
659         uint8_t cmd_pipe_index = 0;
660         uint32_t i, pipe_idx;
661         uint8_t subvp_count = 0;
662         union dmub_rb_cmd cmd;
663         struct pipe_ctx *subvp_pipes[2];
664         uint32_t wm_val_refclk = 0;
665
666         memset(&cmd, 0, sizeof(cmd));
667         // FW command for SUBVP
668         cmd.fw_assisted_mclk_switch_v2.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH;
669         cmd.fw_assisted_mclk_switch_v2.header.sub_type = DMUB_CMD__HANDLE_SUBVP_CMD;
670         cmd.fw_assisted_mclk_switch_v2.header.payload_bytes =
671                         sizeof(cmd.fw_assisted_mclk_switch_v2) - sizeof(cmd.fw_assisted_mclk_switch_v2.header);
672
673         for (i = 0; i < dc->res_pool->pipe_count; i++) {
674                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
675
676                 if (!pipe->stream)
677                         continue;
678
679                 if (pipe->plane_state && !pipe->top_pipe &&
680                                 pipe->stream->mall_stream_config.type == SUBVP_MAIN)
681                         subvp_pipes[subvp_count++] = pipe;
682         }
683
684         if (enable) {
685                 // For each pipe that is a "main" SUBVP pipe, fill in pipe data for DMUB SUBVP cmd
686                 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
687                         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
688
689                         if (!pipe->stream)
690                                 continue;
691
692                         if (pipe->plane_state && pipe->stream->mall_stream_config.paired_stream &&
693                                         pipe->stream->mall_stream_config.type == SUBVP_MAIN) {
694                                 populate_subvp_cmd_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++);
695                         } else if (pipe->plane_state && pipe->stream->mall_stream_config.type == SUBVP_NONE) {
696                                 // Don't need to check for ActiveDRAMClockChangeMargin < 0, not valid in cases where
697                                 // we run through DML without calculating "natural" P-state support
698                                 populate_subvp_cmd_vblank_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++);
699
700                         }
701                         pipe_idx++;
702                 }
703                 if (subvp_count == 2) {
704                         update_subvp_prefetch_end_to_mall_start(dc, context, &cmd, subvp_pipes);
705                 }
706                 cmd.fw_assisted_mclk_switch_v2.config_data.pstate_allow_width_us = dc->caps.subvp_pstate_allow_width_us;
707                 cmd.fw_assisted_mclk_switch_v2.config_data.vertical_int_margin_us = dc->caps.subvp_vertical_int_margin_us;
708
709                 // Store the original watermark value for this SubVP config so we can lower it when the
710                 // MCLK switch starts
711                 wm_val_refclk = context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns *
712                                 dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 / 1000;
713
714                 cmd.fw_assisted_mclk_switch_v2.config_data.watermark_a_cache = wm_val_refclk < 0xFFFF ? wm_val_refclk : 0xFFFF;
715         }
716         dc_dmub_srv_cmd_queue(dc->ctx->dmub_srv, &cmd);
717         dc_dmub_srv_cmd_execute(dc->ctx->dmub_srv);
718         dc_dmub_srv_wait_idle(dc->ctx->dmub_srv);
719 }
720 #endif
721
722 bool dc_dmub_srv_get_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv, struct dmub_diagnostic_data *diag_data)
723 {
724         if (!dc_dmub_srv || !dc_dmub_srv->dmub || !diag_data)
725                 return false;
726         return dmub_srv_get_diagnostic_data(dc_dmub_srv->dmub, diag_data);
727 }
728
729 void dc_dmub_srv_log_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv)
730 {
731         struct dmub_diagnostic_data diag_data = {0};
732
733         if (!dc_dmub_srv || !dc_dmub_srv->dmub) {
734                 DC_LOG_ERROR("%s: invalid parameters.", __func__);
735                 return;
736         }
737
738         if (!dc_dmub_srv_get_diagnostic_data(dc_dmub_srv, &diag_data)) {
739                 DC_LOG_ERROR("%s: dc_dmub_srv_get_diagnostic_data failed.", __func__);
740                 return;
741         }
742
743         DC_LOG_DEBUG(
744                 "DMCUB STATE\n"
745                 "    dmcub_version      : %08x\n"
746                 "    scratch  [0]       : %08x\n"
747                 "    scratch  [1]       : %08x\n"
748                 "    scratch  [2]       : %08x\n"
749                 "    scratch  [3]       : %08x\n"
750                 "    scratch  [4]       : %08x\n"
751                 "    scratch  [5]       : %08x\n"
752                 "    scratch  [6]       : %08x\n"
753                 "    scratch  [7]       : %08x\n"
754                 "    scratch  [8]       : %08x\n"
755                 "    scratch  [9]       : %08x\n"
756                 "    scratch [10]       : %08x\n"
757                 "    scratch [11]       : %08x\n"
758                 "    scratch [12]       : %08x\n"
759                 "    scratch [13]       : %08x\n"
760                 "    scratch [14]       : %08x\n"
761                 "    scratch [15]       : %08x\n"
762                 "    pc                 : %08x\n"
763                 "    unk_fault_addr     : %08x\n"
764                 "    inst_fault_addr    : %08x\n"
765                 "    data_fault_addr    : %08x\n"
766                 "    inbox1_rptr        : %08x\n"
767                 "    inbox1_wptr        : %08x\n"
768                 "    inbox1_size        : %08x\n"
769                 "    inbox0_rptr        : %08x\n"
770                 "    inbox0_wptr        : %08x\n"
771                 "    inbox0_size        : %08x\n"
772                 "    is_enabled         : %d\n"
773                 "    is_soft_reset      : %d\n"
774                 "    is_secure_reset    : %d\n"
775                 "    is_traceport_en    : %d\n"
776                 "    is_cw0_en          : %d\n"
777                 "    is_cw6_en          : %d\n",
778                 diag_data.dmcub_version,
779                 diag_data.scratch[0],
780                 diag_data.scratch[1],
781                 diag_data.scratch[2],
782                 diag_data.scratch[3],
783                 diag_data.scratch[4],
784                 diag_data.scratch[5],
785                 diag_data.scratch[6],
786                 diag_data.scratch[7],
787                 diag_data.scratch[8],
788                 diag_data.scratch[9],
789                 diag_data.scratch[10],
790                 diag_data.scratch[11],
791                 diag_data.scratch[12],
792                 diag_data.scratch[13],
793                 diag_data.scratch[14],
794                 diag_data.scratch[15],
795                 diag_data.pc,
796                 diag_data.undefined_address_fault_addr,
797                 diag_data.inst_fetch_fault_addr,
798                 diag_data.data_write_fault_addr,
799                 diag_data.inbox1_rptr,
800                 diag_data.inbox1_wptr,
801                 diag_data.inbox1_size,
802                 diag_data.inbox0_rptr,
803                 diag_data.inbox0_wptr,
804                 diag_data.inbox0_size,
805                 diag_data.is_dmcub_enabled,
806                 diag_data.is_dmcub_soft_reset,
807                 diag_data.is_dmcub_secure_reset,
808                 diag_data.is_traceport_en,
809                 diag_data.is_cw0_enabled,
810                 diag_data.is_cw6_enabled);
811 }