diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 252 |
1 files changed, 246 insertions, 6 deletions
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 2293a92df3be..ded13026c8ff 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -245,7 +245,9 @@ bool dc_dmub_srv_cmd_run_list(struct dc_dmub_srv *dc_dmub_srv, unsigned int coun if (status == DMUB_STATUS_POWER_STATE_D3) return false; - dmub_srv_wait_for_idle(dmub, 100000); + status = dmub_srv_wait_for_idle(dmub, 100000); + if (status != DMUB_STATUS_OK) + return false; /* Requeue the command. */ status = dmub_srv_cmd_queue(dmub, &cmd_list[i]); @@ -427,6 +429,7 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru int ramp_up_num_steps = 1; // TODO: Ramp is currently disabled. Reenable it. uint8_t visual_confirm_enabled; int pipe_idx = 0; + struct dc_stream_status *stream_status = NULL; if (dc == NULL) return false; @@ -450,7 +453,8 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru * that does not use FAMS, we are in an FPO + VActive scenario. * Assign vactive stretch margin in this case. */ - if (!pipe->stream->fpo_in_use) { + stream_status = dc_state_get_stream_status(context, pipe->stream); + if (stream_status && !stream_status->fpo_in_use) { cmd.fw_assisted_mclk_switch.config_data.vactive_stretch_margin_us = dc->debug.fpo_vactive_margin_us; break; } @@ -461,7 +465,11 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru for (i = 0, k = 0; context && i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; - if (resource_is_pipe_type(pipe, OTG_MASTER) && pipe->stream->fpo_in_use) { + if (!resource_is_pipe_type(pipe, OTG_MASTER)) + continue; + + stream_status = dc_state_get_stream_status(context, pipe->stream); + if (stream_status && stream_status->fpo_in_use) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; uint8_t min_refresh_in_hz = (pipe->stream->timing.min_refresh_in_uhz + 999999) / 1000000; @@ -511,7 +519,8 @@ void dc_dmub_srv_get_visual_confirm_color_cmd(struct dc *dc, struct pipe_ctx *pi union dmub_rb_cmd cmd = { 0 }; unsigned int panel_inst = 0; - dc_get_edp_link_panel_inst(dc, pipe_ctx->stream->link, &panel_inst); + if (!dc_get_edp_link_panel_inst(dc, pipe_ctx->stream->link, &panel_inst)) + return; memset(&cmd, 0, sizeof(cmd)); @@ -556,7 +565,7 @@ static void populate_subvp_cmd_drr_info(struct dc *dc, { struct dc_stream_state *phantom_stream = dc_state_get_paired_subvp_stream(context, subvp_pipe->stream); struct dc_crtc_timing *main_timing = &subvp_pipe->stream->timing; - struct dc_crtc_timing *phantom_timing = &phantom_stream->timing; + struct dc_crtc_timing *phantom_timing; struct dc_crtc_timing *drr_timing = &vblank_pipe->stream->timing; uint16_t drr_frame_us = 0; uint16_t min_drr_supported_us = 0; @@ -570,6 +579,11 @@ static void populate_subvp_cmd_drr_info(struct dc *dc, uint16_t min_vtotal_supported = 0; uint16_t max_vtotal_supported = 0; + if (!phantom_stream) + return; + + phantom_timing = &phantom_stream->timing; + pipe_data->pipe_config.vblank_data.drr_info.drr_in_use = true; pipe_data->pipe_config.vblank_data.drr_info.use_ramping = false; // for now don't use ramping pipe_data->pipe_config.vblank_data.drr_info.drr_window_size_ms = 4; // hardcode 4ms DRR window for now @@ -693,7 +707,13 @@ static void update_subvp_prefetch_end_to_mall_start(struct dc *dc, struct dmub_cmd_fw_assisted_mclk_switch_pipe_data_v2 *pipe_data = NULL; phantom_stream0 = dc_state_get_paired_subvp_stream(context, subvp_pipes[0]->stream); + if (!phantom_stream0) + return; + phantom_stream1 = dc_state_get_paired_subvp_stream(context, subvp_pipes[1]->stream); + if (!phantom_stream1) + return; + phantom_timing0 = &phantom_stream0->timing; phantom_timing1 = &phantom_stream1->timing; @@ -748,9 +768,14 @@ static void populate_subvp_cmd_pipe_info(struct dc *dc, &cmd->fw_assisted_mclk_switch_v2.config_data.pipe_data[cmd_pipe_index]; struct dc_stream_state *phantom_stream = dc_state_get_paired_subvp_stream(context, subvp_pipe->stream); struct dc_crtc_timing *main_timing = &subvp_pipe->stream->timing; - struct dc_crtc_timing *phantom_timing = &phantom_stream->timing; + struct dc_crtc_timing *phantom_timing; uint32_t out_num_stream, out_den_stream, out_num_plane, out_den_plane, out_num, out_den; + if (!phantom_stream) + return; + + phantom_timing = &phantom_stream->timing; + pipe_data->mode = SUBVP; pipe_data->pipe_config.subvp_data.pix_clk_100hz = subvp_pipe->stream->timing.pix_clk_100hz; pipe_data->pipe_config.subvp_data.htotal = subvp_pipe->stream->timing.h_total; @@ -1248,6 +1273,9 @@ static void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle) cmd.idle_opt_notify_idle.cntl_data.driver_idle = allow_idle; + if (dc->work_arounds.skip_psr_ips_crtc_disable) + cmd.idle_opt_notify_idle.cntl_data.skip_otg_disable = true; + if (allow_idle) { volatile struct dmub_shared_state_ips_driver *ips_driver = &dc_dmub_srv->dmub->shared_state[DMUB_SHARED_SHARE_FEATURE__IPS_DRIVER].data.ips_driver; @@ -1296,6 +1324,7 @@ static void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle) } ips_driver->signals = new_signals; + dc_dmub_srv->driver_signals = ips_driver->signals; } DC_LOG_IPS( @@ -1339,6 +1368,7 @@ static void dc_dmub_srv_exit_low_power_state(const struct dc *dc) ips2_exit_count = ips_fw->ips2_exit_count; ips_driver->signals.all = 0; + dc_dmub_srv->driver_signals = ips_driver->signals; DC_LOG_IPS( "%s (allow ips1=%d ips2=%d) (commit ips1=%d ips2=%d) (count rcg=%d ips1=%d ips2=%d)", @@ -1458,6 +1488,36 @@ void dc_dmub_srv_set_power_state(struct dc_dmub_srv *dc_dmub_srv, enum dc_acpi_c dmub_srv_set_power_state(dmub, DMUB_POWER_STATE_D3); } +bool dc_dmub_srv_should_detect(struct dc_dmub_srv *dc_dmub_srv) +{ + volatile const struct dmub_shared_state_ips_fw *ips_fw; + bool reallow_idle = false, should_detect = false; + + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return false; + + if (dc_dmub_srv->dmub->shared_state && + dc_dmub_srv->dmub->meta_info.feature_bits.bits.shared_state_link_detection) { + ips_fw = &dc_dmub_srv->dmub->shared_state[DMUB_SHARED_SHARE_FEATURE__IPS_FW].data.ips_fw; + return ips_fw->signals.bits.detection_required; + } + + /* Detection may require reading scratch 0 - exit out of idle prior to the read. */ + if (dc_dmub_srv->idle_allowed) { + dc_dmub_srv_apply_idle_power_optimizations(dc_dmub_srv->ctx->dc, false); + reallow_idle = true; + } + + should_detect = dmub_srv_should_detect(dc_dmub_srv->dmub); + + /* Re-enter idle if we're not about to immediately redetect links. */ + if (!should_detect && reallow_idle && dc_dmub_srv->idle_exit_counter == 0 && + !dc_dmub_srv->ctx->dc->debug.disable_dmub_reallow_idle) + dc_dmub_srv_apply_idle_power_optimizations(dc_dmub_srv->ctx->dc, true); + + return should_detect; +} + 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; @@ -1465,6 +1525,8 @@ void dc_dmub_srv_apply_idle_power_optimizations(const struct dc *dc, bool allow_ if (!dc_dmub_srv || !dc_dmub_srv->dmub) return; + allow_idle &= (!dc->debug.ips_disallow_entry); + if (dc_dmub_srv->idle_allowed == allow_idle) return; @@ -1595,3 +1657,181 @@ bool dc_wake_and_execute_gpint(const struct dc_context *ctx, enum dmub_gpint_com return result; } +void dc_dmub_srv_fams2_update_config(struct dc *dc, + struct dc_state *context, + bool enable) +{ + uint8_t num_cmds = 1; + uint32_t i; + union dmub_rb_cmd cmd[MAX_STREAMS + 1]; + struct dmub_rb_cmd_fams2 *global_cmd = &cmd[0].fams2_config; + + memset(cmd, 0, sizeof(union dmub_rb_cmd) * (MAX_STREAMS + 1)); + /* fill in generic command header */ + global_cmd->header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; + global_cmd->header.sub_type = DMUB_CMD__FAMS2_CONFIG; + global_cmd->header.payload_bytes = sizeof(struct dmub_rb_cmd_fams2) - sizeof(struct dmub_cmd_header); + + /* send global configuration parameters */ + global_cmd->config.global.max_allow_delay_us = 100 * 1000; //100ms + global_cmd->config.global.lock_wait_time_us = 5000; //5ms + global_cmd->config.global.recovery_timeout_us = 5000; //5ms + global_cmd->config.global.hwfq_flip_programming_delay_us = 100; //100us + + /* copy static feature configuration */ + global_cmd->config.global.features.all = dc->debug.fams2_config.all; + + /* apply feature configuration based on current driver state */ + global_cmd->config.global.features.bits.enable_visual_confirm = dc->debug.visual_confirm == VISUAL_CONFIRM_FAMS2; + global_cmd->config.global.features.bits.enable = enable; + + /* construct per-stream configs */ + if (enable) { + for (i = 0; i < context->bw_ctx.bw.dcn.fams2_stream_count; i++) { + struct dmub_rb_cmd_fams2 *stream_cmd = &cmd[i+1].fams2_config; + + /* configure command header */ + stream_cmd->header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; + stream_cmd->header.sub_type = DMUB_CMD__FAMS2_CONFIG; + stream_cmd->header.payload_bytes = sizeof(struct dmub_rb_cmd_fams2) - sizeof(struct dmub_cmd_header); + stream_cmd->header.multi_cmd_pending = 1; + /* copy stream static state */ + memcpy(&stream_cmd->config.stream, + &context->bw_ctx.bw.dcn.fams2_stream_params[i], + sizeof(struct dmub_fams2_stream_static_state)); + } + } + + if (enable && context->bw_ctx.bw.dcn.fams2_stream_count) { + /* set multi pending for global, and unset for last stream cmd */ + global_cmd->config.global.num_streams = context->bw_ctx.bw.dcn.fams2_stream_count; + global_cmd->header.multi_cmd_pending = 1; + cmd[context->bw_ctx.bw.dcn.fams2_stream_count].fams2_config.header.multi_cmd_pending = 0; + num_cmds += context->bw_ctx.bw.dcn.fams2_stream_count; + } + + dm_execute_dmub_cmd_list(dc->ctx, num_cmds, cmd, DM_DMUB_WAIT_TYPE_WAIT); +} + +void dc_dmub_srv_fams2_drr_update(struct dc *dc, + uint32_t tg_inst, + uint32_t vtotal_min, + uint32_t vtotal_max, + uint32_t vtotal_mid, + uint32_t vtotal_mid_frame_num, + bool program_manual_trigger) +{ + union dmub_rb_cmd cmd = { 0 }; + + cmd.fams2_drr_update.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; + cmd.fams2_drr_update.header.sub_type = DMUB_CMD__FAMS2_DRR_UPDATE; + cmd.fams2_drr_update.dmub_optc_state_req.tg_inst = tg_inst; + cmd.fams2_drr_update.dmub_optc_state_req.v_total_max = vtotal_max; + cmd.fams2_drr_update.dmub_optc_state_req.v_total_min = vtotal_min; + cmd.fams2_drr_update.dmub_optc_state_req.v_total_mid = vtotal_mid; + cmd.fams2_drr_update.dmub_optc_state_req.v_total_mid_frame_num = vtotal_mid_frame_num; + cmd.fams2_drr_update.dmub_optc_state_req.program_manual_trigger = program_manual_trigger; + + cmd.fams2_drr_update.header.payload_bytes = sizeof(cmd.fams2_drr_update) - sizeof(cmd.fams2_drr_update.header); + + dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); +} + +void dc_dmub_srv_fams2_passthrough_flip( + struct dc *dc, + struct dc_state *state, + struct dc_stream_state *stream, + struct dc_surface_update *srf_updates, + int surface_count) +{ + int plane_index; + union dmub_rb_cmd cmds[MAX_PLANES]; + struct dc_plane_address *address; + struct dc_plane_state *plane_state; + int num_cmds = 0; + struct dc_stream_status *stream_status = dc_stream_get_status(stream); + + if (surface_count <= 0 || stream_status == NULL) + return; + + memset(cmds, 0, sizeof(union dmub_rb_cmd) * MAX_PLANES); + + /* build command for each surface update */ + for (plane_index = 0; plane_index < surface_count; plane_index++) { + plane_state = srf_updates[plane_index].surface; + address = &plane_state->address; + + /* skip if there is no address update for plane */ + if (!srf_updates[plane_index].flip_addr) + continue; + + /* build command header */ + cmds[num_cmds].fams2_flip.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH; + cmds[num_cmds].fams2_flip.header.sub_type = DMUB_CMD__FAMS2_FLIP; + cmds[num_cmds].fams2_flip.header.payload_bytes = sizeof(struct dmub_rb_cmd_fams2_flip); + + /* for chaining multiple commands, all but last command should set to 1 */ + cmds[num_cmds].fams2_flip.header.multi_cmd_pending = 1; + + /* set topology info */ + cmds[num_cmds].fams2_flip.flip_info.pipe_mask = dc_plane_get_pipe_mask(state, plane_state); + if (stream_status) + cmds[num_cmds].fams2_flip.flip_info.otg_inst = stream_status->primary_otg_inst; + + cmds[num_cmds].fams2_flip.flip_info.config.bits.is_immediate = plane_state->flip_immediate; + + /* build address info for command */ + switch (address->type) { + case PLN_ADDR_TYPE_GRAPHICS: + if (address->grph.addr.quad_part == 0) { + BREAK_TO_DEBUGGER(); + break; + } + + cmds[num_cmds].fams2_flip.flip_info.addr_info.meta_addr_lo = + address->grph.meta_addr.low_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.meta_addr_hi = + (uint16_t)address->grph.meta_addr.high_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.surf_addr_lo = + address->grph.addr.low_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.surf_addr_hi = + (uint16_t)address->grph.addr.high_part; + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + if (address->video_progressive.luma_addr.quad_part == 0 || + address->video_progressive.chroma_addr.quad_part == 0) { + BREAK_TO_DEBUGGER(); + break; + } + + cmds[num_cmds].fams2_flip.flip_info.addr_info.meta_addr_lo = + address->video_progressive.luma_meta_addr.low_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.meta_addr_hi = + (uint16_t)address->video_progressive.luma_meta_addr.high_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.meta_addr_c_lo = + address->video_progressive.chroma_meta_addr.low_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.meta_addr_c_hi = + (uint16_t)address->video_progressive.chroma_meta_addr.high_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.surf_addr_lo = + address->video_progressive.luma_addr.low_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.surf_addr_hi = + (uint16_t)address->video_progressive.luma_addr.high_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.surf_addr_c_lo = + address->video_progressive.chroma_addr.low_part; + cmds[num_cmds].fams2_flip.flip_info.addr_info.surf_addr_c_hi = + (uint16_t)address->video_progressive.chroma_addr.high_part; + break; + default: + // Should never be hit + BREAK_TO_DEBUGGER(); + break; + } + + num_cmds++; + } + + if (num_cmds > 0) { + cmds[num_cmds - 1].fams2_flip.header.multi_cmd_pending = 0; + dm_execute_dmub_cmd_list(dc->ctx, num_cmds, cmds, DM_DMUB_WAIT_TYPE_WAIT); + } +} |