aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/display/dc/core/dc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/core/dc.c')
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc.c541
1 files changed, 515 insertions, 26 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 146fd4b864b2..51c9563ad137 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -22,9 +22,6 @@
* Authors: AMD
*/
-#include <linux/slab.h>
-#include <linux/mm.h>
-
#include "dm_services.h"
#include "dc.h"
@@ -347,10 +344,16 @@ static bool create_link_encoders(struct dc *dc)
*/
static void destroy_link_encoders(struct dc *dc)
{
- unsigned int num_usb4_dpia = dc->res_pool->res_cap->num_usb4_dpia;
- unsigned int num_dig_link_enc = dc->res_pool->res_cap->num_dig_link_enc;
+ unsigned int num_usb4_dpia;
+ unsigned int num_dig_link_enc;
int i;
+ if (!dc->res_pool)
+ return;
+
+ num_usb4_dpia = dc->res_pool->res_cap->num_usb4_dpia;
+ num_dig_link_enc = dc->res_pool->res_cap->num_dig_link_enc;
+
/* A platform without USB4 DPIA endpoints has a fixed mapping between DIG
* link encoders and physical display endpoints and does not require
* additional link encoder objects.
@@ -859,6 +862,8 @@ static bool dc_construct_ctx(struct dc *dc,
dc_ctx->dc_sink_id_count = 0;
dc_ctx->dc_stream_id_count = 0;
dc_ctx->dce_environment = init_params->dce_environment;
+ dc_ctx->dcn_reg_offsets = init_params->dcn_reg_offsets;
+ dc_ctx->nbio_reg_offsets = init_params->nbio_reg_offsets;
/* Create logger */
@@ -1082,6 +1087,16 @@ static void disable_dangling_plane(struct dc *dc, struct dc_state *context)
dc->current_state->stream_count != context->stream_count)
should_disable = true;
+ if (old_stream && !dc->current_state->res_ctx.pipe_ctx[i].top_pipe) {
+ struct pipe_ctx *old_pipe, *new_pipe;
+
+ old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
+ new_pipe = &context->res_ctx.pipe_ctx[i];
+
+ if (old_pipe->plane_state && !new_pipe->plane_state)
+ should_disable = true;
+ }
+
if (should_disable && old_stream) {
dc_rem_all_planes_for_stream(dc, old_stream, dangling_context);
disable_all_writeback_pipes_for_stream(dc, old_stream, dangling_context);
@@ -1228,6 +1243,9 @@ struct dc *dc_create(const struct dc_init_data *init_params)
dc->versions.dmcu_version = dc->res_pool->dmcu->dmcu_version;
}
+ dc->dcn_reg_offsets = init_params->dcn_reg_offsets;
+ dc->nbio_reg_offsets = init_params->nbio_reg_offsets;
+
/* Populate versioning information */
dc->versions.dc_ver = DC_VER;
@@ -1333,7 +1351,9 @@ static void program_timing_sync(
struct pipe_ctx *unsynced_pipes[MAX_PIPES] = { NULL };
for (i = 0; i < pipe_count; i++) {
- if (!ctx->res_ctx.pipe_ctx[i].stream || ctx->res_ctx.pipe_ctx[i].top_pipe)
+ if (!ctx->res_ctx.pipe_ctx[i].stream
+ || ctx->res_ctx.pipe_ctx[i].top_pipe
+ || ctx->res_ctx.pipe_ctx[i].prev_odm_pipe)
continue;
unsynced_pipes[i] = &ctx->res_ctx.pipe_ctx[i];
@@ -1742,8 +1762,11 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
result = dc->hwss.apply_ctx_to_hw(dc, context);
- if (result != DC_OK)
+ if (result != DC_OK) {
+ /* Application of dc_state to hardware stopped. */
+ dc->current_state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
return result;
+ }
dc_trigger_sync(dc, context);
@@ -1905,7 +1928,8 @@ static bool is_flip_pending_in_pipes(struct dc *dc, struct dc_state *context)
for (i = 0; i < MAX_PIPES; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
- if (!pipe->plane_state)
+ // Don't check flip pending on phantom pipes
+ if (!pipe->plane_state || (pipe->stream && pipe->stream->mall_stream_config.type == SUBVP_PHANTOM))
continue;
/* Must set to false to start with, due to OR in update function */
@@ -2406,6 +2430,96 @@ static enum surface_update_type check_update_surfaces_for_stream(
return overall_type;
}
+static bool dc_check_is_fullscreen_video(struct rect src, struct rect clip_rect)
+{
+ int view_height, view_width, clip_x, clip_y, clip_width, clip_height;
+
+ view_height = src.height;
+ view_width = src.width;
+
+ clip_x = clip_rect.x;
+ clip_y = clip_rect.y;
+
+ clip_width = clip_rect.width;
+ clip_height = clip_rect.height;
+
+ /* check for centered video accounting for off by 1 scaling truncation */
+ if ((view_height - clip_y - clip_height <= clip_y + 1) &&
+ (view_width - clip_x - clip_width <= clip_x + 1) &&
+ (view_height - clip_y - clip_height >= clip_y - 1) &&
+ (view_width - clip_x - clip_width >= clip_x - 1)) {
+
+ /* when OS scales up/down to letter box, it may end up
+ * with few blank pixels on the border due to truncating.
+ * Add offset margin to account for this
+ */
+ if (clip_x <= 4 || clip_y <= 4)
+ return true;
+ }
+
+ return false;
+}
+
+static enum surface_update_type check_boundary_crossing_for_windowed_mpo_with_odm(struct dc *dc,
+ struct dc_surface_update *srf_updates, int surface_count,
+ enum surface_update_type update_type)
+{
+ enum surface_update_type new_update_type = update_type;
+ int i, j;
+ struct pipe_ctx *pipe = NULL;
+ struct dc_stream_state *stream;
+
+ /* Check that we are in windowed MPO with ODM
+ * - look for MPO pipe by scanning pipes for first pipe matching
+ * surface that has moved ( position change )
+ * - MPO pipe will have top pipe
+ * - check that top pipe has ODM pointer
+ */
+ if ((surface_count > 1) && dc->config.enable_windowed_mpo_odm) {
+ for (i = 0; i < surface_count; i++) {
+ if (srf_updates[i].surface && srf_updates[i].scaling_info
+ && srf_updates[i].surface->update_flags.bits.position_change) {
+
+ for (j = 0; j < dc->res_pool->pipe_count; j++) {
+ if (srf_updates[i].surface == dc->current_state->res_ctx.pipe_ctx[j].plane_state) {
+ pipe = &dc->current_state->res_ctx.pipe_ctx[j];
+ stream = pipe->stream;
+ break;
+ }
+ }
+
+ if (pipe && pipe->top_pipe && (get_num_odm_splits(pipe->top_pipe) > 0) && stream
+ && !dc_check_is_fullscreen_video(stream->src, srf_updates[i].scaling_info->clip_rect)) {
+ struct rect old_clip_rect, new_clip_rect;
+ bool old_clip_rect_left, old_clip_rect_right, old_clip_rect_middle;
+ bool new_clip_rect_left, new_clip_rect_right, new_clip_rect_middle;
+
+ old_clip_rect = srf_updates[i].surface->clip_rect;
+ new_clip_rect = srf_updates[i].scaling_info->clip_rect;
+
+ old_clip_rect_left = ((old_clip_rect.x + old_clip_rect.width) <= (stream->src.x + (stream->src.width/2)));
+ old_clip_rect_right = (old_clip_rect.x >= (stream->src.x + (stream->src.width/2)));
+ old_clip_rect_middle = !old_clip_rect_left && !old_clip_rect_right;
+
+ new_clip_rect_left = ((new_clip_rect.x + new_clip_rect.width) <= (stream->src.x + (stream->src.width/2)));
+ new_clip_rect_right = (new_clip_rect.x >= (stream->src.x + (stream->src.width/2)));
+ new_clip_rect_middle = !new_clip_rect_left && !new_clip_rect_right;
+
+ if (old_clip_rect_left && new_clip_rect_middle)
+ new_update_type = UPDATE_TYPE_FULL;
+ else if (old_clip_rect_middle && new_clip_rect_right)
+ new_update_type = UPDATE_TYPE_FULL;
+ else if (old_clip_rect_right && new_clip_rect_middle)
+ new_update_type = UPDATE_TYPE_FULL;
+ else if (old_clip_rect_middle && new_clip_rect_left)
+ new_update_type = UPDATE_TYPE_FULL;
+ }
+ }
+ }
+ }
+ return new_update_type;
+}
+
/*
* dc_check_update_surfaces_for_stream() - Determine update type (fast, med, or full)
*
@@ -2437,6 +2551,10 @@ enum surface_update_type dc_check_update_surfaces_for_stream(
updates[i].surface->update_flags.raw = 0xFFFFFFFF;
}
+ if (type == UPDATE_TYPE_MED)
+ type = check_boundary_crossing_for_windowed_mpo_with_odm(dc,
+ updates, surface_count, type);
+
if (type == UPDATE_TYPE_FAST) {
// If there's an available clock comparator, we use that.
if (dc->clk_mgr->funcs->are_clock_states_equal) {
@@ -2716,6 +2834,137 @@ static void copy_stream_update_to_stream(struct dc *dc,
}
}
+void dc_reset_state(struct dc *dc, struct dc_state *context)
+{
+ dc_resource_state_destruct(context);
+
+ /* clear the structure, but don't reset the reference count */
+ memset(context, 0, offsetof(struct dc_state, refcount));
+
+ init_state(dc, context);
+}
+
+static bool update_planes_and_stream_state(struct dc *dc,
+ struct dc_surface_update *srf_updates, int surface_count,
+ struct dc_stream_state *stream,
+ struct dc_stream_update *stream_update,
+ enum surface_update_type *new_update_type,
+ struct dc_state **new_context)
+{
+ struct dc_state *context;
+ int i, j;
+ enum surface_update_type update_type;
+ const struct dc_stream_status *stream_status;
+ struct dc_context *dc_ctx = dc->ctx;
+
+ stream_status = dc_stream_get_status(stream);
+
+ if (!stream_status) {
+ if (surface_count) /* Only an error condition if surf_count non-zero*/
+ ASSERT(false);
+
+ return false; /* Cannot commit surface to stream that is not committed */
+ }
+
+ context = dc->current_state;
+
+ update_type = dc_check_update_surfaces_for_stream(
+ dc, srf_updates, surface_count, stream_update, stream_status);
+
+ /* update current stream with the new updates */
+ copy_stream_update_to_stream(dc, context, stream, stream_update);
+
+ /* do not perform surface update if surface has invalid dimensions
+ * (all zero) and no scaling_info is provided
+ */
+ if (surface_count > 0) {
+ for (i = 0; i < surface_count; i++) {
+ if ((srf_updates[i].surface->src_rect.width == 0 ||
+ srf_updates[i].surface->src_rect.height == 0 ||
+ srf_updates[i].surface->dst_rect.width == 0 ||
+ srf_updates[i].surface->dst_rect.height == 0) &&
+ (!srf_updates[i].scaling_info ||
+ srf_updates[i].scaling_info->src_rect.width == 0 ||
+ srf_updates[i].scaling_info->src_rect.height == 0 ||
+ srf_updates[i].scaling_info->dst_rect.width == 0 ||
+ srf_updates[i].scaling_info->dst_rect.height == 0)) {
+ DC_ERROR("Invalid src/dst rects in surface update!\n");
+ return false;
+ }
+ }
+ }
+
+ if (update_type >= update_surface_trace_level)
+ update_surface_trace(dc, srf_updates, surface_count);
+
+ if (update_type >= UPDATE_TYPE_FULL) {
+ struct dc_plane_state *new_planes[MAX_SURFACES] = {0};
+
+ for (i = 0; i < surface_count; i++)
+ new_planes[i] = srf_updates[i].surface;
+
+ /* initialize scratch memory for building context */
+ context = dc_create_state(dc);
+ if (context == NULL) {
+ DC_ERROR("Failed to allocate new validate context!\n");
+ return false;
+ }
+
+ dc_resource_state_copy_construct(
+ dc->current_state, context);
+
+ /*remove old surfaces from context */
+ if (!dc_rem_all_planes_for_stream(dc, stream, context)) {
+
+ BREAK_TO_DEBUGGER();
+ goto fail;
+ }
+
+ /* add surface to context */
+ if (!dc_add_all_planes_for_stream(dc, stream, new_planes, surface_count, context)) {
+
+ BREAK_TO_DEBUGGER();
+ goto fail;
+ }
+ }
+
+ /* save update parameters into surface */
+ for (i = 0; i < surface_count; i++) {
+ struct dc_plane_state *surface = srf_updates[i].surface;
+
+ copy_surface_update_to_plane(surface, &srf_updates[i]);
+
+ if (update_type >= UPDATE_TYPE_MED) {
+ for (j = 0; j < dc->res_pool->pipe_count; j++) {
+ struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
+
+ if (pipe_ctx->plane_state != surface)
+ continue;
+
+ resource_build_scaling_params(pipe_ctx);
+ }
+ }
+ }
+
+ if (update_type == UPDATE_TYPE_FULL) {
+ if (!dc->res_pool->funcs->validate_bandwidth(dc, context, false)) {
+ BREAK_TO_DEBUGGER();
+ goto fail;
+ }
+ }
+
+ *new_context = context;
+ *new_update_type = update_type;
+
+ return true;
+
+fail:
+ dc_release_state(context);
+
+ return false;
+
+}
+
static void commit_planes_do_stream_update(struct dc *dc,
struct dc_stream_state *stream,
struct dc_stream_update *stream_update,
@@ -2917,6 +3166,13 @@ static void commit_planes_for_stream(struct dc *dc,
int i, j;
struct pipe_ctx *top_pipe_to_program = NULL;
bool should_lock_all_pipes = (update_type != UPDATE_TYPE_FAST);
+ bool subvp_prev_use = false;
+
+ // Once we apply the new subvp context to hardware it won't be in the
+ // dc->current_state anymore, so we have to cache it before we apply
+ // the new SubVP context
+ subvp_prev_use = false;
+
dc_z10_restore(dc);
@@ -2955,6 +3211,15 @@ static void commit_planes_for_stream(struct dc *dc,
}
}
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
+
+ // Check old context for SubVP
+ subvp_prev_use |= (old_pipe->stream && old_pipe->stream->mall_stream_config.type == SUBVP_PHANTOM);
+ if (subvp_prev_use)
+ break;
+ }
+
if (stream->test_pattern.type != DP_TEST_PATTERN_VIDEO_MODE) {
struct pipe_ctx *mpcc_pipe;
struct pipe_ctx *odm_pipe;
@@ -2964,7 +3229,7 @@ static void commit_planes_for_stream(struct dc *dc,
odm_pipe->ttu_regs.min_ttu_vblank = MAX_TTU;
}
- if ((update_type != UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed)
+ if ((update_type != UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed) {
if (top_pipe_to_program &&
top_pipe_to_program->stream_res.tg->funcs->lock_doublebuffer_enable) {
if (should_use_dmub_lock(stream->link)) {
@@ -2982,10 +3247,16 @@ static void commit_planes_for_stream(struct dc *dc,
top_pipe_to_program->stream_res.tg->funcs->lock_doublebuffer_enable(
top_pipe_to_program->stream_res.tg);
}
+ }
if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) {
+ if (dc->hwss.subvp_pipe_control_lock)
+ dc->hwss.subvp_pipe_control_lock(dc, context, true, should_lock_all_pipes, NULL, subvp_prev_use);
dc->hwss.interdependent_update_lock(dc, context, true);
+
} else {
+ if (dc->hwss.subvp_pipe_control_lock)
+ dc->hwss.subvp_pipe_control_lock(dc, context, true, should_lock_all_pipes, top_pipe_to_program, subvp_prev_use);
/* Lock the top pipe while updating plane addrs, since freesync requires
* plane addr update event triggers to be synchronized.
* top_pipe_to_program is expected to never be NULL
@@ -2993,8 +3264,40 @@ static void commit_planes_for_stream(struct dc *dc,
dc->hwss.pipe_control_lock(dc, top_pipe_to_program, true);
}
+ if (update_type != UPDATE_TYPE_FAST) {
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *new_pipe = &context->res_ctx.pipe_ctx[i];
+
+ if ((new_pipe->stream && new_pipe->stream->mall_stream_config.type == SUBVP_PHANTOM) ||
+ subvp_prev_use) {
+ // If old context or new context has phantom pipes, apply
+ // the phantom timings now. We can't change the phantom
+ // pipe configuration safely without driver acquiring
+ // the DMCUB lock first.
+ dc->hwss.apply_ctx_to_hw(dc, context);
+ break;
+ }
+ }
+ }
+
dc_dmub_update_dirty_rect(dc, surface_count, stream, srf_updates, context);
+ if (update_type != UPDATE_TYPE_FAST) {
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *new_pipe = &context->res_ctx.pipe_ctx[i];
+
+ if ((new_pipe->stream && new_pipe->stream->mall_stream_config.type == SUBVP_PHANTOM) ||
+ subvp_prev_use) {
+ // If old context or new context has phantom pipes, apply
+ // the phantom timings now. We can't change the phantom
+ // pipe configuration safely without driver acquiring
+ // the DMCUB lock first.
+ dc->hwss.apply_ctx_to_hw(dc, context);
+ break;
+ }
+ }
+ }
+
// Stream updates
if (stream_update)
commit_planes_do_stream_update(dc, stream, stream_update, update_type, context);
@@ -3009,12 +3312,27 @@ static void commit_planes_for_stream(struct dc *dc,
if (dc->hwss.program_front_end_for_ctx)
dc->hwss.program_front_end_for_ctx(dc, context);
+ if (update_type != UPDATE_TYPE_FAST)
+ if (dc->hwss.commit_subvp_config)
+ dc->hwss.commit_subvp_config(dc, context);
+
if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) {
dc->hwss.interdependent_update_lock(dc, context, false);
} else {
dc->hwss.pipe_control_lock(dc, top_pipe_to_program, false);
}
dc->hwss.post_unlock_program_front_end(dc, context);
+
+ /* Since phantom pipe programming is moved to post_unlock_program_front_end,
+ * move the SubVP lock to after the phantom pipes have been setup
+ */
+ if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) {
+ if (dc->hwss.subvp_pipe_control_lock)
+ dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, NULL, subvp_prev_use);
+ } else {
+ if (dc->hwss.subvp_pipe_control_lock)
+ dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, NULL, subvp_prev_use);
+ }
return;
}
@@ -3138,27 +3456,27 @@ static void commit_planes_for_stream(struct dc *dc,
}
-#ifdef CONFIG_DRM_AMD_DC_DCN
- if (update_type != UPDATE_TYPE_FAST)
- if (dc->hwss.commit_subvp_config)
- dc->hwss.commit_subvp_config(dc, context);
-#endif
- if (should_lock_all_pipes && dc->hwss.interdependent_update_lock)
- dc->hwss.interdependent_update_lock(dc, context, false);
- else
- dc->hwss.pipe_control_lock(dc, top_pipe_to_program, false);
+ if (update_type != UPDATE_TYPE_FAST)
+ if (dc->hwss.commit_subvp_config)
+ dc->hwss.commit_subvp_config(dc, context);
+
+ if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) {
+ dc->hwss.interdependent_update_lock(dc, context, false);
+ } else {
+ dc->hwss.pipe_control_lock(dc, top_pipe_to_program, false);
+ }
- if ((update_type != UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed)
+ if ((update_type != UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed) {
if (top_pipe_to_program->stream_res.tg->funcs->lock_doublebuffer_enable) {
top_pipe_to_program->stream_res.tg->funcs->wait_for_state(
- top_pipe_to_program->stream_res.tg,
- CRTC_STATE_VACTIVE);
+ top_pipe_to_program->stream_res.tg,
+ CRTC_STATE_VACTIVE);
top_pipe_to_program->stream_res.tg->funcs->wait_for_state(
- top_pipe_to_program->stream_res.tg,
- CRTC_STATE_VBLANK);
+ top_pipe_to_program->stream_res.tg,
+ CRTC_STATE_VBLANK);
top_pipe_to_program->stream_res.tg->funcs->wait_for_state(
- top_pipe_to_program->stream_res.tg,
- CRTC_STATE_VACTIVE);
+ top_pipe_to_program->stream_res.tg,
+ CRTC_STATE_VACTIVE);
if (stream && should_use_dmub_lock(stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
@@ -3175,10 +3493,23 @@ static void commit_planes_for_stream(struct dc *dc,
top_pipe_to_program->stream_res.tg->funcs->lock_doublebuffer_disable(
top_pipe_to_program->stream_res.tg);
}
+ }
- if (update_type != UPDATE_TYPE_FAST)
+ if (update_type != UPDATE_TYPE_FAST) {
dc->hwss.post_unlock_program_front_end(dc, context);
+ /* Since phantom pipe programming is moved to post_unlock_program_front_end,
+ * move the SubVP lock to after the phantom pipes have been setup
+ */
+ if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) {
+ if (dc->hwss.subvp_pipe_control_lock)
+ dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, NULL, subvp_prev_use);
+ } else {
+ if (dc->hwss.subvp_pipe_control_lock)
+ dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, top_pipe_to_program, subvp_prev_use);
+ }
+ }
+
// Fire manual trigger only when bottom plane is flipped
for (j = 0; j < dc->res_pool->pipe_count; j++) {
struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
@@ -3197,6 +3528,152 @@ static void commit_planes_for_stream(struct dc *dc,
}
}
+static bool commit_minimal_transition_state(struct dc *dc,
+ struct dc_state *transition_base_context)
+{
+ struct dc_state *transition_context = dc_create_state(dc);
+ enum pipe_split_policy tmp_policy;
+ enum dc_status ret = DC_ERROR_UNEXPECTED;
+ unsigned int i, j;
+
+ if (!transition_context)
+ return false;
+
+ tmp_policy = dc->debug.pipe_split_policy;
+ dc->debug.pipe_split_policy = MPC_SPLIT_AVOID;
+
+ dc_resource_state_copy_construct(transition_base_context, transition_context);
+
+ //commit minimal state
+ if (dc->res_pool->funcs->validate_bandwidth(dc, transition_context, false)) {
+ for (i = 0; i < transition_context->stream_count; i++) {
+ struct dc_stream_status *stream_status = &transition_context->stream_status[i];
+
+ for (j = 0; j < stream_status->plane_count; j++) {
+ struct dc_plane_state *plane_state = stream_status->plane_states[j];
+
+ /* force vsync flip when reconfiguring pipes to prevent underflow
+ * and corruption
+ */
+ plane_state->flip_immediate = false;
+ }
+ }
+
+ ret = dc_commit_state_no_check(dc, transition_context);
+ }
+
+ //always release as dc_commit_state_no_check retains in good case
+ dc_release_state(transition_context);
+
+ //restore previous pipe split policy
+ dc->debug.pipe_split_policy = tmp_policy;
+
+ if (ret != DC_OK) {
+ //this should never happen
+ BREAK_TO_DEBUGGER();
+ return false;
+ }
+
+ //force full surface update
+ for (i = 0; i < dc->current_state->stream_count; i++) {
+ for (j = 0; j < dc->current_state->stream_status[i].plane_count; j++) {
+ dc->current_state->stream_status[i].plane_states[j]->update_flags.raw = 0xFFFFFFFF;
+ }
+ }
+
+ return true;
+}
+
+bool dc_update_planes_and_stream(struct dc *dc,
+ struct dc_surface_update *srf_updates, int surface_count,
+ struct dc_stream_state *stream,
+ struct dc_stream_update *stream_update)
+{
+ struct dc_state *context;
+ enum surface_update_type update_type;
+ int i;
+
+ /* In cases where MPO and split or ODM are used transitions can
+ * cause underflow. Apply stream configuration with minimal pipe
+ * split first to avoid unsupported transitions for active pipes.
+ */
+ bool force_minimal_pipe_splitting = false;
+ bool is_plane_addition = false;
+
+ struct dc_stream_status *cur_stream_status = stream_get_status(dc->current_state, stream);
+
+ if (cur_stream_status &&
+ dc->current_state->stream_count > 0 &&
+ dc->debug.pipe_split_policy != MPC_SPLIT_AVOID) {
+ /* determine if minimal transition is required */
+ if (cur_stream_status->plane_count > surface_count) {
+ force_minimal_pipe_splitting = true;
+ } else if (cur_stream_status->plane_count < surface_count) {
+ force_minimal_pipe_splitting = true;
+ is_plane_addition = true;
+ }
+ }
+
+ /* on plane addition, minimal state is the current one */
+ if (force_minimal_pipe_splitting && is_plane_addition &&
+ !commit_minimal_transition_state(dc, dc->current_state))
+ return false;
+
+ if (!update_planes_and_stream_state(
+ dc,
+ srf_updates,
+ surface_count,
+ stream,
+ stream_update,
+ &update_type,
+ &context))
+ return false;
+
+ /* on plane addition, minimal state is the new one */
+ if (force_minimal_pipe_splitting && !is_plane_addition) {
+ if (!commit_minimal_transition_state(dc, context)) {
+ dc_release_state(context);
+ return false;
+ }
+
+ update_type = UPDATE_TYPE_FULL;
+ }
+
+ commit_planes_for_stream(
+ dc,
+ srf_updates,
+ surface_count,
+ stream,
+ stream_update,
+ update_type,
+ context);
+
+ if (dc->current_state != context) {
+
+ /* Since memory free requires elevated IRQL, an interrupt
+ * request is generated by mem free. If this happens
+ * between freeing and reassigning the context, our vsync
+ * interrupt will call into dc and cause a memory
+ * corruption BSOD. Hence, we first reassign the context,
+ * then free the old context.
+ */
+
+ struct dc_state *old = dc->current_state;
+
+ dc->current_state = context;
+ dc_release_state(old);
+
+ // clear any forced full updates
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
+
+ if (pipe_ctx->plane_state && pipe_ctx->stream == stream)
+ pipe_ctx->plane_state->force_full_update = false;
+ }
+ }
+ return true;
+}
+
void dc_commit_updates_for_stream(struct dc *dc,
struct dc_surface_update *srf_updates,
int surface_count,
@@ -3779,10 +4256,18 @@ bool dc_is_plane_eligible_for_idle_optimizations(struct dc *dc, struct dc_plane_
/* cleanup on driver unload */
void dc_hardware_release(struct dc *dc)
{
+ dc_mclk_switch_using_fw_based_vblank_stretch_shut_down(dc);
+
if (dc->hwss.hardware_release)
dc->hwss.hardware_release(dc);
}
+void dc_mclk_switch_using_fw_based_vblank_stretch_shut_down(struct dc *dc)
+{
+ if (dc->current_state)
+ dc->current_state->bw_ctx.bw.dcn.clk.fw_based_mclk_switching_shut_down = true;
+}
+
/*
*****************************************************************************
* Function: dc_is_dmub_outbox_supported -
@@ -3807,6 +4292,10 @@ bool dc_is_dmub_outbox_supported(struct dc *dc)
!dc->debug.dpia_debug.bits.disable_dpia)
return true;
+ if (dc->ctx->asic_id.chip_family == AMDGPU_FAMILY_GC_11_0_2 &&
+ !dc->debug.dpia_debug.bits.disable_dpia)
+ return true;
+
/* dmub aux needs dmub notifications to be enabled */
return dc->debug.enable_dmub_aux_for_legacy_ddc;
}