From 9f0fc191371843c4fc000a226b0a26b6c059aacd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 19:40:19 +0200 Subject: Merging upstream version 6.7.7. Signed-off-by: Daniel Baumann --- drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 403 ++++++++++++++++++++++++--- 1 file changed, 363 insertions(+), 40 deletions(-) (limited to 'drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c') diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index 4c5ef3ef8d..05b3433cbb 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -32,6 +32,7 @@ #include "../basics/conversion.h" #include "cursor_reg_cache.h" #include "resource.h" +#include "clk_mgr.h" #define CTX dc_dmub_srv->ctx #define DC_LOGGER CTX->logger @@ -79,44 +80,119 @@ void dc_dmub_srv_wait_idle(struct dc_dmub_srv *dc_dmub_srv) } } -void dc_dmub_srv_clear_inbox0_ack(struct dc_dmub_srv *dmub_srv) +void dc_dmub_srv_clear_inbox0_ack(struct dc_dmub_srv *dc_dmub_srv) { - struct dmub_srv *dmub = dmub_srv->dmub; - struct dc_context *dc_ctx = dmub_srv->ctx; + struct dmub_srv *dmub = dc_dmub_srv->dmub; + struct dc_context *dc_ctx = dc_dmub_srv->ctx; enum dmub_status status = DMUB_STATUS_OK; status = dmub_srv_clear_inbox0_ack(dmub); if (status != DMUB_STATUS_OK) { DC_ERROR("Error clearing INBOX0 ack: status=%d\n", status); - dc_dmub_srv_log_diagnostic_data(dmub_srv); + dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); } } -void dc_dmub_srv_wait_for_inbox0_ack(struct dc_dmub_srv *dmub_srv) +void dc_dmub_srv_wait_for_inbox0_ack(struct dc_dmub_srv *dc_dmub_srv) { - struct dmub_srv *dmub = dmub_srv->dmub; - struct dc_context *dc_ctx = dmub_srv->ctx; + struct dmub_srv *dmub = dc_dmub_srv->dmub; + struct dc_context *dc_ctx = dc_dmub_srv->ctx; enum dmub_status status = DMUB_STATUS_OK; status = dmub_srv_wait_for_inbox0_ack(dmub, 100000); if (status != DMUB_STATUS_OK) { DC_ERROR("Error waiting for INBOX0 HW Lock Ack\n"); - dc_dmub_srv_log_diagnostic_data(dmub_srv); + dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); } } -void dc_dmub_srv_send_inbox0_cmd(struct dc_dmub_srv *dmub_srv, - union dmub_inbox0_data_register data) +void dc_dmub_srv_send_inbox0_cmd(struct dc_dmub_srv *dc_dmub_srv, + union dmub_inbox0_data_register data) { - struct dmub_srv *dmub = dmub_srv->dmub; - struct dc_context *dc_ctx = dmub_srv->ctx; + struct dmub_srv *dmub = dc_dmub_srv->dmub; + struct dc_context *dc_ctx = dc_dmub_srv->ctx; enum dmub_status status = DMUB_STATUS_OK; status = dmub_srv_send_inbox0_cmd(dmub, data); if (status != DMUB_STATUS_OK) { DC_ERROR("Error sending INBOX0 cmd\n"); - dc_dmub_srv_log_diagnostic_data(dmub_srv); + dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); + } +} + +bool dc_dmub_srv_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_srv, + unsigned int count, + union dmub_rb_cmd *cmd_list) +{ + struct dc_context *dc_ctx; + struct dmub_srv *dmub; + enum dmub_status status; + int i; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return false; + + dc_ctx = dc_dmub_srv->ctx; + dmub = dc_dmub_srv->dmub; + + for (i = 0 ; i < count; i++) { + // Queue command + status = dmub_srv_cmd_queue(dmub, &cmd_list[i]); + + if (status == DMUB_STATUS_QUEUE_FULL) { + /* Execute and wait for queue to become empty again. */ + dmub_srv_cmd_execute(dmub); + dmub_srv_wait_for_idle(dmub, 100000); + + /* Requeue the command. */ + status = dmub_srv_cmd_queue(dmub, &cmd_list[i]); + } + + if (status != DMUB_STATUS_OK) { + DC_ERROR("Error queueing DMUB command: status=%d\n", status); + dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); + return false; + } + } + + status = dmub_srv_cmd_execute(dmub); + if (status != DMUB_STATUS_OK) { + DC_ERROR("Error starting DMUB execution: status=%d\n", status); + dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); + return false; + } + + return true; +} + +bool dc_dmub_srv_wait_for_idle(struct dc_dmub_srv *dc_dmub_srv, + enum dm_dmub_wait_type wait_type, + union dmub_rb_cmd *cmd_list) +{ + struct dmub_srv *dmub; + enum dmub_status status; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return false; + + dmub = dc_dmub_srv->dmub; + + // Wait for DMUB to process command + if (wait_type != DM_DMUB_WAIT_TYPE_NO_WAIT) { + status = dmub_srv_wait_for_idle(dmub, 100000); + + if (status != DMUB_STATUS_OK) { + DC_LOG_DEBUG("No reply for DMUB command: status=%d\n", status); + dc_dmub_srv_log_diagnostic_data(dc_dmub_srv); + return false; + } + + // Copy data back from ring buffer into command + if (wait_type == DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) + dmub_rb_get_return_data(&dmub->inbox1_rb, cmd_list); } + + return true; } bool dc_dmub_srv_cmd_run(struct dc_dmub_srv *dc_dmub_srv, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) @@ -207,17 +283,11 @@ bool dc_dmub_srv_optimized_init_done(struct dc_dmub_srv *dc_dmub_srv) bool dc_dmub_srv_notify_stream_mask(struct dc_dmub_srv *dc_dmub_srv, unsigned int stream_mask) { - struct dmub_srv *dmub; - const uint32_t timeout = 30; - if (!dc_dmub_srv || !dc_dmub_srv->dmub) return false; - dmub = dc_dmub_srv->dmub; - - return dmub_srv_send_gpint_command( - dmub, DMUB_GPINT__IDLE_OPT_NOTIFY_STREAM_MASK, - stream_mask, timeout) == DMUB_STATUS_OK; + return dc_wake_and_execute_gpint(dc_dmub_srv->ctx, DMUB_GPINT__IDLE_OPT_NOTIFY_STREAM_MASK, + stream_mask, NULL, DM_DMUB_WAIT_TYPE_WAIT); } bool dc_dmub_srv_is_restore_required(struct dc_dmub_srv *dc_dmub_srv) @@ -266,7 +336,7 @@ void dc_dmub_srv_drr_update_cmd(struct dc *dc, uint32_t tg_inst, uint32_t vtotal cmd.drr_update.header.payload_bytes = sizeof(cmd.drr_update) - sizeof(cmd.drr_update.header); // Send the command to the DMCUB. - dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); + dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); } void dc_dmub_srv_set_drr_manual_trigger_cmd(struct dc *dc, uint32_t tg_inst) @@ -280,7 +350,7 @@ void dc_dmub_srv_set_drr_manual_trigger_cmd(struct dc *dc, uint32_t tg_inst) cmd.drr_update.header.payload_bytes = sizeof(cmd.drr_update) - sizeof(cmd.drr_update.header); // Send the command to the DMCUB. - dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); + dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); } static uint8_t dc_dmub_srv_get_pipes_for_stream(struct dc *dc, struct dc_stream_state *stream) @@ -373,7 +443,7 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru sizeof(cmd.fw_assisted_mclk_switch) - sizeof(cmd.fw_assisted_mclk_switch.header); // Send the command to the DMCUB. - dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); + dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); return true; } @@ -394,7 +464,7 @@ void dc_dmub_srv_query_caps_cmd(struct dc_dmub_srv *dc_dmub_srv) cmd.query_feature_caps.header.payload_bytes = sizeof(struct dmub_cmd_query_feature_caps_data); /* If command was processed, copy feature caps to dmub srv */ - if (dm_execute_dmub_cmd(dc_dmub_srv->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && + if (dc_wake_and_execute_dmub_cmd(dc_dmub_srv->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && cmd.query_feature_caps.header.ret_status == 0) { memcpy(&dc_dmub_srv->dmub->feature_caps, &cmd.query_feature_caps.query_feature_caps_data, @@ -419,7 +489,7 @@ void dc_dmub_srv_get_visual_confirm_color_cmd(struct dc *dc, struct pipe_ctx *pi cmd.visual_confirm_color.visual_confirm_color_data.visual_confirm_color.panel_inst = panel_inst; // If command was processed, copy feature caps to dmub srv - if (dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && + if (dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && cmd.visual_confirm_color.header.ret_status == 0) { memcpy(&dc->ctx->dmub_srv->dmub->visual_confirm_color, &cmd.visual_confirm_color.visual_confirm_color_data, @@ -552,7 +622,8 @@ static void populate_subvp_cmd_vblank_pipe_info(struct dc *dc, pipe_data->pipe_config.vblank_data.vblank_end = vblank_pipe->stream->timing.v_total - vblank_pipe->stream->timing.v_front_porch - vblank_pipe->stream->timing.v_addressable; - if (vblank_pipe->stream->ignore_msa_timing_param) + if (vblank_pipe->stream->ignore_msa_timing_param && + (vblank_pipe->stream->allow_freesync || vblank_pipe->stream->vrr_active_variable || vblank_pipe->stream->vrr_active_fixed)) populate_subvp_cmd_drr_info(dc, pipe, vblank_pipe, pipe_data); } @@ -645,7 +716,8 @@ static void populate_subvp_cmd_pipe_info(struct dc *dc, main_timing->v_total - main_timing->v_front_porch - main_timing->v_addressable; pipe_data->pipe_config.subvp_data.mall_region_lines = phantom_timing->v_addressable; pipe_data->pipe_config.subvp_data.main_pipe_index = subvp_pipe->stream_res.tg->inst; - pipe_data->pipe_config.subvp_data.is_drr = subvp_pipe->stream->ignore_msa_timing_param; + pipe_data->pipe_config.subvp_data.is_drr = subvp_pipe->stream->ignore_msa_timing_param && + (subvp_pipe->stream->allow_freesync || subvp_pipe->stream->vrr_active_variable || subvp_pipe->stream->vrr_active_fixed); /* Calculate the scaling factor from the src and dst height. * e.g. If 3840x2160 being downscaled to 1920x1080, the scaling factor is 1/2. @@ -779,7 +851,7 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc, cmd.fw_assisted_mclk_switch_v2.config_data.watermark_a_cache = wm_val_refclk < 0xFFFF ? wm_val_refclk : 0xFFFF; } - dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); + dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); } bool dc_dmub_srv_get_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv, struct dmub_diagnostic_data *diag_data) @@ -1016,7 +1088,7 @@ void dc_send_update_cursor_info_to_dmu( pipe_idx, pCtx->plane_res.hubp, pCtx->plane_res.dpp); /* Combine 2nd cmds update_curosr_info to DMU */ - dm_execute_dmub_cmd_list(pCtx->stream->ctx, 2, cmd, DM_DMUB_WAIT_TYPE_WAIT); + dc_wake_and_execute_dmub_cmd_list(pCtx->stream->ctx, 2, cmd, DM_DMUB_WAIT_TYPE_WAIT); } } @@ -1030,28 +1102,279 @@ bool dc_dmub_check_min_version(struct dmub_srv *srv) void dc_dmub_srv_enable_dpia_trace(const struct dc *dc) { struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv; - struct dmub_srv *dmub; - enum dmub_status status; - static const uint32_t timeout_us = 30; if (!dc_dmub_srv || !dc_dmub_srv->dmub) { DC_LOG_ERROR("%s: invalid parameters.", __func__); return; } - dmub = dc_dmub_srv->dmub; - - status = dmub_srv_send_gpint_command(dmub, DMUB_GPINT__SET_TRACE_BUFFER_MASK_WORD1, 0x0010, timeout_us); - if (status != DMUB_STATUS_OK) { + if (!dc_wake_and_execute_gpint(dc->ctx, DMUB_GPINT__SET_TRACE_BUFFER_MASK_WORD1, + 0x0010, NULL, DM_DMUB_WAIT_TYPE_WAIT)) { DC_LOG_ERROR("timeout updating trace buffer mask word\n"); return; } - status = dmub_srv_send_gpint_command(dmub, DMUB_GPINT__UPDATE_TRACE_BUFFER_MASK, 0x0000, timeout_us); - if (status != DMUB_STATUS_OK) { + if (!dc_wake_and_execute_gpint(dc->ctx, DMUB_GPINT__UPDATE_TRACE_BUFFER_MASK, + 0x0000, NULL, DM_DMUB_WAIT_TYPE_WAIT)) { DC_LOG_ERROR("timeout updating trace buffer mask word\n"); return; } DC_LOG_DEBUG("Enabled DPIA trace\n"); -} \ No newline at end of file +} + +void dc_dmub_srv_subvp_save_surf_addr(const struct dc_dmub_srv *dc_dmub_srv, const struct dc_plane_address *addr, uint8_t subvp_index) +{ + dmub_srv_subvp_save_surf_addr(dc_dmub_srv->dmub, addr, subvp_index); +} + +bool dc_dmub_srv_is_hw_pwr_up(struct dc_dmub_srv *dc_dmub_srv, bool wait) +{ + struct dc_context *dc_ctx; + enum dmub_status status; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return true; + + if (dc_dmub_srv->ctx->dc->debug.dmcub_emulation) + return true; + + dc_ctx = dc_dmub_srv->ctx; + + if (wait) { + status = dmub_srv_wait_for_hw_pwr_up(dc_dmub_srv->dmub, 500000); + if (status != DMUB_STATUS_OK) { + DC_ERROR("Error querying DMUB hw power up status: error=%d\n", status); + return false; + } + } else + return dmub_srv_is_hw_pwr_up(dc_dmub_srv->dmub); + + return true; +} + +static void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle) +{ + union dmub_rb_cmd cmd = {0}; + + if (dc->debug.dmcub_emulation) + return; + + memset(&cmd, 0, sizeof(cmd)); + cmd.idle_opt_notify_idle.header.type = DMUB_CMD__IDLE_OPT; + cmd.idle_opt_notify_idle.header.sub_type = DMUB_CMD__IDLE_OPT_DCN_NOTIFY_IDLE; + cmd.idle_opt_notify_idle.header.payload_bytes = + sizeof(cmd.idle_opt_notify_idle) - + sizeof(cmd.idle_opt_notify_idle.header); + + cmd.idle_opt_notify_idle.cntl_data.driver_idle = allow_idle; + + if (allow_idle) { + if (dc->hwss.set_idle_state) + dc->hwss.set_idle_state(dc, true); + } + + /* NOTE: This does not use the "wake" interface since this is part of the wake path. */ + dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); +} + +static void dc_dmub_srv_exit_low_power_state(const struct dc *dc) +{ + const uint32_t max_num_polls = 10000; + uint32_t allow_state = 0; + uint32_t commit_state = 0; + uint32_t i; + + if (dc->debug.dmcub_emulation) + return; + + if (!dc->idle_optimizations_allowed) + return; + + if (!dc->ctx->dmub_srv || !dc->ctx->dmub_srv->dmub) + return; + + if (dc->hwss.get_idle_state && + dc->hwss.set_idle_state && + dc->clk_mgr->funcs->exit_low_power_state) { + + allow_state = dc->hwss.get_idle_state(dc); + dc->hwss.set_idle_state(dc, false); + + if (!(allow_state & DMUB_IPS2_ALLOW_MASK)) { + // Wait for evaluation time + udelay(dc->debug.ips2_eval_delay_us); + commit_state = dc->hwss.get_idle_state(dc); + if (!(commit_state & DMUB_IPS2_COMMIT_MASK)) { + // Tell PMFW to exit low power state + dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr); + + // Wait for IPS2 entry upper bound + udelay(dc->debug.ips2_entry_delay_us); + dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr); + + for (i = 0; i < max_num_polls; ++i) { + commit_state = dc->hwss.get_idle_state(dc); + if (commit_state & DMUB_IPS2_COMMIT_MASK) + break; + + udelay(1); + } + ASSERT(i < max_num_polls); + + if (!dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true)) + ASSERT(0); + + /* TODO: See if we can return early here - IPS2 should go + * back directly to IPS0 and clear the flags, but it will + * be safer to directly notify DMCUB of this. + */ + allow_state = dc->hwss.get_idle_state(dc); + } + } + + dc_dmub_srv_notify_idle(dc, false); + if (!(allow_state & DMUB_IPS1_ALLOW_MASK)) { + for (i = 0; i < max_num_polls; ++i) { + commit_state = dc->hwss.get_idle_state(dc); + if (commit_state & DMUB_IPS1_COMMIT_MASK) + break; + + udelay(1); + } + ASSERT(i < max_num_polls); + } + } + + if (!dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true)) + ASSERT(0); +} + +void dc_dmub_srv_set_power_state(struct dc_dmub_srv *dc_dmub_srv, enum dc_acpi_cm_power_state powerState) +{ + struct dmub_srv *dmub; + + if (!dc_dmub_srv) + return; + + dmub = dc_dmub_srv->dmub; + + if (powerState == DC_ACPI_CM_POWER_STATE_D0) + dmub_srv_set_power_state(dmub, DMUB_POWER_STATE_D0); + else + dmub_srv_set_power_state(dmub, DMUB_POWER_STATE_D3); +} + +void dc_dmub_srv_apply_idle_power_optimizations(const struct dc *dc, bool allow_idle) +{ + struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return; + + if (dc_dmub_srv->idle_allowed == allow_idle) + return; + + /* + * Entering a low power state requires a driver notification. + * Powering up the hardware requires notifying PMFW and DMCUB. + * Clearing the driver idle allow requires a DMCUB command. + * DMCUB commands requires the DMCUB to be powered up and restored. + * + * Exit out early to prevent an infinite loop of DMCUB commands + * triggering exit low power - use software state to track this. + */ + dc_dmub_srv->idle_allowed = allow_idle; + + if (!allow_idle) + dc_dmub_srv_exit_low_power_state(dc); + else + dc_dmub_srv_notify_idle(dc, allow_idle); +} + +bool dc_wake_and_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, + enum dm_dmub_wait_type wait_type) +{ + return dc_wake_and_execute_dmub_cmd_list(ctx, 1, cmd, wait_type); +} + +bool dc_wake_and_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, + union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) +{ + struct dc_dmub_srv *dc_dmub_srv = ctx->dmub_srv; + bool result = false, reallow_idle = false; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return false; + + if (count == 0) + return true; + + if (dc_dmub_srv->idle_allowed) { + dc_dmub_srv_apply_idle_power_optimizations(ctx->dc, false); + reallow_idle = true; + } + + /* + * These may have different implementations in DM, so ensure + * that we guide it to the expected helper. + */ + if (count > 1) + result = dm_execute_dmub_cmd_list(ctx, count, cmd, wait_type); + else + result = dm_execute_dmub_cmd(ctx, cmd, wait_type); + + if (result && reallow_idle) + dc_dmub_srv_apply_idle_power_optimizations(ctx->dc, true); + + return result; +} + +static bool dc_dmub_execute_gpint(const struct dc_context *ctx, enum dmub_gpint_command command_code, + uint16_t param, uint32_t *response, enum dm_dmub_wait_type wait_type) +{ + struct dc_dmub_srv *dc_dmub_srv = ctx->dmub_srv; + const uint32_t wait_us = wait_type == DM_DMUB_WAIT_TYPE_NO_WAIT ? 0 : 30; + enum dmub_status status; + + if (response) + *response = 0; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return false; + + status = dmub_srv_send_gpint_command(dc_dmub_srv->dmub, command_code, param, wait_us); + if (status != DMUB_STATUS_OK) { + if (status == DMUB_STATUS_TIMEOUT && wait_type == DM_DMUB_WAIT_TYPE_NO_WAIT) + return true; + + return false; + } + + if (response && wait_type == DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) + dmub_srv_get_gpint_response(dc_dmub_srv->dmub, response); + + return true; +} + +bool dc_wake_and_execute_gpint(const struct dc_context *ctx, enum dmub_gpint_command command_code, + uint16_t param, uint32_t *response, enum dm_dmub_wait_type wait_type) +{ + struct dc_dmub_srv *dc_dmub_srv = ctx->dmub_srv; + bool result = false, reallow_idle = false; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return false; + + if (dc_dmub_srv->idle_allowed) { + dc_dmub_srv_apply_idle_power_optimizations(ctx->dc, false); + reallow_idle = true; + } + + result = dc_dmub_execute_gpint(ctx, command_code, param, response, wait_type); + + if (result && reallow_idle) + dc_dmub_srv_apply_idle_power_optimizations(ctx->dc, true); + + return result; +} -- cgit v1.2.3