Commit 7a1eb668 authored by Dillon Varone's avatar Dillon Varone Committed by Alex Deucher
Browse files

drm/amd/display: Perform outstanding programming on full updates



[WHY]
In certain scenarios DC can internally trigger back to back full updates
which will miss some required programming that is normally deferred
until post update via optimize_bandwidth.

[HOW]
In back to back update scenarios, wait for pending updates to complete
and perform any strictly required outstanding programming.

Reviewed-by: default avatarAlvin Lee <alvin.lee2@amd.com>
Signed-off-by: default avatarDillon Varone <dillon.varone@amd.com>
Signed-off-by: default avatarTom Chung <chiahsuan.chung@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 67ea53a4
Loading
Loading
Loading
Loading
+3 −118
Original line number Diff line number Diff line
@@ -1352,80 +1352,6 @@ static void disable_vbios_mode_if_required(
	}
}

/**
 * wait_for_blank_complete - wait for all active OPPs to finish pending blank
 * pattern updates
 *
 * @dc: [in] dc reference
 * @context: [in] hardware context in use
 */
static void wait_for_blank_complete(struct dc *dc,
		struct dc_state *context)
{
	struct pipe_ctx *opp_head;
	struct dce_hwseq *hws = dc->hwseq;
	int i;

	if (!hws->funcs.wait_for_blank_complete)
		return;

	for (i = 0; i < MAX_PIPES; i++) {
		opp_head = &context->res_ctx.pipe_ctx[i];

		if (!resource_is_pipe_type(opp_head, OPP_HEAD) ||
				dc_state_get_pipe_subvp_type(context, opp_head) == SUBVP_PHANTOM)
			continue;

		hws->funcs.wait_for_blank_complete(opp_head->stream_res.opp);
	}
}

static void wait_for_odm_update_pending_complete(struct dc *dc, struct dc_state *context)
{
	struct pipe_ctx *otg_master;
	struct timing_generator *tg;
	int i;

	for (i = 0; i < MAX_PIPES; i++) {
		otg_master = &context->res_ctx.pipe_ctx[i];
		if (!resource_is_pipe_type(otg_master, OTG_MASTER) ||
				dc_state_get_pipe_subvp_type(context, otg_master) == SUBVP_PHANTOM)
			continue;
		tg = otg_master->stream_res.tg;
		if (tg->funcs->wait_odm_doublebuffer_pending_clear)
			tg->funcs->wait_odm_doublebuffer_pending_clear(tg);
	}

	/* ODM update may require to reprogram blank pattern for each OPP */
	wait_for_blank_complete(dc, context);
}

static void wait_for_no_pipes_pending(struct dc *dc, struct dc_state *context)
{
	int i;
	PERF_TRACE();
	for (i = 0; i < MAX_PIPES; i++) {
		int count = 0;
		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];

		if (!pipe->plane_state || dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM)
			continue;

		/* Timeout 100 ms */
		while (count < 100000) {
			/* Must set to false to start with, due to OR in update function */
			pipe->plane_state->status.is_flip_pending = false;
			dc->hwss.update_pending_status(pipe);
			if (!pipe->plane_state->status.is_flip_pending)
				break;
			udelay(1);
			count++;
		}
		ASSERT(!pipe->plane_state->status.is_flip_pending);
	}
	PERF_TRACE();
}

/* Public functions */

struct dc *dc_create(const struct dc_init_data *init_params)
@@ -2109,12 +2035,12 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
	if (context->stream_count > get_seamless_boot_stream_count(context) ||
		context->stream_count == 0) {
		/* Must wait for no flips to be pending before doing optimize bw */
		wait_for_no_pipes_pending(dc, context);
		hwss_wait_for_no_pipes_pending(dc, context);
		/*
		 * optimized dispclk depends on ODM setup. Need to wait for ODM
		 * update pending complete before optimizing bandwidth.
		 */
		wait_for_odm_update_pending_complete(dc, context);
		hwss_wait_for_odm_update_pending_complete(dc, context);
		/* pplib is notified if disp_num changed */
		dc->hwss.optimize_bandwidth(dc, context);
		/* Need to do otg sync again as otg could be out of sync due to otg
@@ -3786,47 +3712,6 @@ static void commit_planes_for_stream_fast(struct dc *dc,
		top_pipe_to_program->stream->update_flags.raw = 0;
}

static void wait_for_outstanding_hw_updates(struct dc *dc, struct dc_state *dc_context)
{
/*
 * This function calls HWSS to wait for any potentially double buffered
 * operations to complete. It should be invoked as a pre-amble prior
 * to full update programming before asserting any HW locks.
 */
	int pipe_idx;
	int opp_inst;
	int opp_count = dc->res_pool->res_cap->num_opp;
	struct hubp *hubp;
	int mpcc_inst;
	const struct pipe_ctx *pipe_ctx;

	for (pipe_idx = 0; pipe_idx < dc->res_pool->pipe_count; pipe_idx++) {
		pipe_ctx = &dc_context->res_ctx.pipe_ctx[pipe_idx];

		if (!pipe_ctx->stream)
			continue;

		if (pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear)
			pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear(pipe_ctx->stream_res.tg);

		hubp = pipe_ctx->plane_res.hubp;
		if (!hubp)
			continue;

		mpcc_inst = hubp->inst;
		// MPCC inst is equal to pipe index in practice
		for (opp_inst = 0; opp_inst < opp_count; opp_inst++) {
			if ((dc->res_pool->opps[opp_inst] != NULL) &&
				(dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst])) {
				dc->res_pool->mpc->funcs->wait_for_idle(dc->res_pool->mpc, mpcc_inst);
				dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst] = false;
				break;
			}
		}
	}
	wait_for_odm_update_pending_complete(dc, dc_context);
}

static void commit_planes_for_stream(struct dc *dc,
		struct dc_surface_update *srf_updates,
		int surface_count,
@@ -3850,7 +3735,7 @@ static void commit_planes_for_stream(struct dc *dc,

	dc_z10_restore(dc);
	if (update_type == UPDATE_TYPE_FULL)
		wait_for_outstanding_hw_updates(dc, context);
		hwss_process_outstanding_hw_updates(dc, dc->current_state);

	for (i = 0; i < dc->res_pool->pipe_count; i++) {
		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
+123 −0
Original line number Diff line number Diff line
@@ -978,3 +978,126 @@ void get_surface_tile_visual_confirm_color(
		break;
	}
}

/**
 * hwss_wait_for_blank_complete - wait for all active OPPs to finish pending blank
 * pattern updates
 *
 * @dc: [in] dc reference
 * @context: [in] hardware context in use
 */
void hwss_wait_for_all_blank_complete(struct dc *dc,
		struct dc_state *context)
{
	struct pipe_ctx *opp_head;
	struct dce_hwseq *hws = dc->hwseq;
	int i;

	if (!hws->funcs.wait_for_blank_complete)
		return;

	for (i = 0; i < MAX_PIPES; i++) {
		opp_head = &context->res_ctx.pipe_ctx[i];

		if (!resource_is_pipe_type(opp_head, OPP_HEAD) ||
				dc_state_get_pipe_subvp_type(context, opp_head) == SUBVP_PHANTOM)
			continue;

		hws->funcs.wait_for_blank_complete(opp_head->stream_res.opp);
	}
}

void hwss_wait_for_odm_update_pending_complete(struct dc *dc, struct dc_state *context)
{
	struct pipe_ctx *otg_master;
	struct timing_generator *tg;
	int i;

	for (i = 0; i < MAX_PIPES; i++) {
		otg_master = &context->res_ctx.pipe_ctx[i];
		if (!resource_is_pipe_type(otg_master, OTG_MASTER) ||
				dc_state_get_pipe_subvp_type(context, otg_master) == SUBVP_PHANTOM)
			continue;
		tg = otg_master->stream_res.tg;
		if (tg->funcs->wait_odm_doublebuffer_pending_clear)
			tg->funcs->wait_odm_doublebuffer_pending_clear(tg);
	}

	/* ODM update may require to reprogram blank pattern for each OPP */
	hwss_wait_for_all_blank_complete(dc, context);
}

void hwss_wait_for_no_pipes_pending(struct dc *dc, struct dc_state *context)
{
	int i;
	for (i = 0; i < MAX_PIPES; i++) {
		int count = 0;
		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];

		if (!pipe->plane_state || dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM)
			continue;

		/* Timeout 100 ms */
		while (count < 100000) {
			/* Must set to false to start with, due to OR in update function */
			pipe->plane_state->status.is_flip_pending = false;
			dc->hwss.update_pending_status(pipe);
			if (!pipe->plane_state->status.is_flip_pending)
				break;
			udelay(1);
			count++;
		}
		ASSERT(!pipe->plane_state->status.is_flip_pending);
	}
}

void hwss_wait_for_outstanding_hw_updates(struct dc *dc, struct dc_state *dc_context)
{
/*
 * This function calls HWSS to wait for any potentially double buffered
 * operations to complete. It should be invoked as a pre-amble prior
 * to full update programming before asserting any HW locks.
 */
	int pipe_idx;
	int opp_inst;
	int opp_count = dc->res_pool->res_cap->num_opp;
	struct hubp *hubp;
	int mpcc_inst;
	const struct pipe_ctx *pipe_ctx;

	for (pipe_idx = 0; pipe_idx < dc->res_pool->pipe_count; pipe_idx++) {
		pipe_ctx = &dc_context->res_ctx.pipe_ctx[pipe_idx];

		if (!pipe_ctx->stream)
			continue;

		if (pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear)
			pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear(pipe_ctx->stream_res.tg);

		hubp = pipe_ctx->plane_res.hubp;
		if (!hubp)
			continue;

		mpcc_inst = hubp->inst;
		// MPCC inst is equal to pipe index in practice
		for (opp_inst = 0; opp_inst < opp_count; opp_inst++) {
			if ((dc->res_pool->opps[opp_inst] != NULL) &&
				(dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst])) {
				dc->res_pool->mpc->funcs->wait_for_idle(dc->res_pool->mpc, mpcc_inst);
				dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst] = false;
				break;
			}
		}
	}
	hwss_wait_for_odm_update_pending_complete(dc, dc_context);
}

void hwss_process_outstanding_hw_updates(struct dc *dc, struct dc_state *dc_context)
{
	/* wait for outstanding updates */
	hwss_wait_for_outstanding_hw_updates(dc, dc_context);

	/* perform outstanding post update programming */
	if (dc->hwss.program_outstanding_updates)
		dc->hwss.program_outstanding_updates(dc, dc_context);
}
+10 −0
Original line number Diff line number Diff line
@@ -1846,3 +1846,13 @@ void dcn32_interdependent_update_lock(struct dc *dc,
			dc->hwss.pipe_control_lock(dc, pipe, false);
	}
}

void dcn32_program_outstanding_updates(struct dc *dc,
		struct dc_state *context)
{
	struct hubbub *hubbub = dc->res_pool->hubbub;

	/* update compbuf if required */
	if (hubbub->funcs->program_compbuf_size)
		hubbub->funcs->program_compbuf_size(hubbub, context->bw_ctx.bw.dcn.compbuf_size_kb, true);
}
+4 −0
Original line number Diff line number Diff line
@@ -133,4 +133,8 @@ void dcn32_prepare_bandwidth(struct dc *dc,

void dcn32_interdependent_update_lock(struct dc *dc,
		struct dc_state *context, bool lock);

void dcn32_program_outstanding_updates(struct dc *dc,
		struct dc_state *context);

#endif /* __DC_HWSS_DCN32_H__ */
+1 −0
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = {
	.blank_phantom = dcn32_blank_phantom,
	.is_pipe_topology_transition_seamless = dcn32_is_pipe_topology_transition_seamless,
	.calculate_pix_rate_divider = dcn32_calculate_pix_rate_divider,
	.program_outstanding_updates = dcn32_program_outstanding_updates,
};

static const struct hwseq_private_funcs dcn32_private_funcs = {
Loading