Commit fa2123db authored by Mikita Lipski's avatar Mikita Lipski Committed by Alex Deucher
Browse files

drm/amd/display: Multi display synchronization logic



This feature synchronizes multiple displays with various timings
to a display with the highest refresh rate
it is enabled if edid caps flag multi_display_sync is set to one

There are limitations on refresh rates allowed
that can be synchronized. That would
prevent from underflow and other potential
corruptions.

Multi display synchronization is using the
same functions as timing_sync in order to minimize
redunduncy and decision to disable synchronization is
based on trigger parametre set in DM

Feature is developed for DCN1 and DCE11

Signed-off-by: default avatarMikita Lipski <mikita.lipski@amd.com>
Reviewed-by: default avatarMikita Lipski <Mikita.Lipski@amd.com>
Acked-by: default avatarHarry Wentland <harry.wentland@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 79c63123
Loading
Loading
Loading
Loading
+53 −1
Original line number Diff line number Diff line
@@ -2298,6 +2298,56 @@ static int create_fake_sink(struct amdgpu_dm_connector *aconnector)
	return 0;
}

static void set_multisync_trigger_params(
		struct dc_stream_state *stream)
{
	if (stream->triggered_crtc_reset.enabled) {
		stream->triggered_crtc_reset.event = CRTC_EVENT_VSYNC_RISING;
		stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_LINE;
	}
}

static void set_master_stream(struct dc_stream_state *stream_set[],
			      int stream_count)
{
	int j, highest_rfr = 0, master_stream = 0;

	for (j = 0;  j < stream_count; j++) {
		if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) {
			int refresh_rate = 0;

			refresh_rate = (stream_set[j]->timing.pix_clk_khz*1000)/
				(stream_set[j]->timing.h_total*stream_set[j]->timing.v_total);
			if (refresh_rate > highest_rfr) {
				highest_rfr = refresh_rate;
				master_stream = j;
			}
		}
	}
	for (j = 0;  j < stream_count; j++) {
		if (stream_set[j] && j != master_stream)
			stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
	}
}

static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
{
	int i = 0;

	if (context->stream_count < 2)
		return;
	for (i = 0; i < context->stream_count ; i++) {
		if (!context->streams[i])
			continue;
		/* TODO: add a function to read AMD VSDB bits and will set
		 * crtc_sync_master.multi_sync_enabled flag
		 * For now its set to false
		 */
		set_multisync_trigger_params(context->streams[i]);
	}
	set_master_stream(context->streams, context->stream_count);
}

static struct dc_stream_state *
create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
		       const struct drm_display_mode *drm_mode,
@@ -4132,8 +4182,10 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
		}
	}

	if (dm_state->context)
	if (dm_state->context) {
		dm_enable_per_frame_crtc_master_sync(dm_state->context);
		WARN_ON(!dc_commit_state(dm->dc, dm_state->context));
	}

	for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
		struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
+25 −1
Original line number Diff line number Diff line
@@ -713,6 +713,28 @@ void dc_destroy(struct dc **dc)
	*dc = NULL;
}

static void enable_timing_multisync(
		struct dc *dc,
		struct dc_state *ctx)
{
	int i = 0, multisync_count = 0;
	int pipe_count = dc->res_pool->pipe_count;
	struct pipe_ctx *multisync_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].stream->triggered_crtc_reset.enabled)
			continue;
		multisync_pipes[multisync_count] = &ctx->res_ctx.pipe_ctx[i];
		multisync_count++;
	}

	if (multisync_count > 1) {
		dc->hwss.enable_per_frame_crtc_position_reset(
			dc, multisync_count, multisync_pipes);
	}
}

static void program_timing_sync(
		struct dc *dc,
		struct dc_state *ctx)
@@ -891,6 +913,8 @@ 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 (context->stream_count > 1)
		enable_timing_multisync(dc, context);
		program_timing_sync(dc, context);

	dc_enable_stereo(dc, context, dc_streams, context->stream_count);
+3 −0
Original line number Diff line number Diff line
@@ -607,6 +607,9 @@ struct dc_stream_state {

	/* from stream struct */
	struct kref refcount;

	struct crtc_trigger_info triggered_crtc_reset;

};

struct dc_stream_update {
+16 −0
Original line number Diff line number Diff line
@@ -673,6 +673,22 @@ enum dc_timing_3d_format {
	TIMING_3D_FORMAT_MAX,
};

enum trigger_delay {
	TRIGGER_DELAY_NEXT_PIXEL = 0,
	TRIGGER_DELAY_NEXT_LINE,
};

enum crtc_event {
	CRTC_EVENT_VSYNC_RISING = 0,
	CRTC_EVENT_VSYNC_FALLING
};

struct crtc_trigger_info {
	bool enabled;
	struct dc_stream_state *event_source;
	enum crtc_event event;
	enum trigger_delay delay;
};

struct dc_crtc_timing {

+38 −8
Original line number Diff line number Diff line
@@ -2457,20 +2457,16 @@ static void dce110_enable_timing_synchronization(

	for (i = 1 /* skip the master */; i < group_size; i++)
		grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger(
					grouped_pipes[i]->stream_res.tg, gsl_params.gsl_group);


				grouped_pipes[i]->stream_res.tg,
				gsl_params.gsl_group);

	for (i = 1 /* skip the master */; i < group_size; i++) {
		DC_SYNC_INFO("GSL: waiting for reset to occur.\n");
		wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg);
		/* Regardless of success of the wait above, remove the reset or
		 * the driver will start timing out on Display requests. */
		DC_SYNC_INFO("GSL: disabling trigger-reset.\n");
		grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger(grouped_pipes[i]->stream_res.tg);
		grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger(
				grouped_pipes[i]->stream_res.tg);
	}


	/* GSL Vblank synchronization is a one time sync mechanism, assumption
	 * is that the sync'ed displays will not drift out of sync over time*/
	DC_SYNC_INFO("GSL: Restoring register states.\n");
@@ -2480,6 +2476,39 @@ static void dce110_enable_timing_synchronization(
	DC_SYNC_INFO("GSL: Set-up complete.\n");
}

static void dce110_enable_per_frame_crtc_position_reset(
		struct dc *dc,
		int group_size,
		struct pipe_ctx *grouped_pipes[])
{
	struct dc_context *dc_ctx = dc->ctx;
	struct dcp_gsl_params gsl_params = { 0 };
	int i;

	gsl_params.gsl_group = 0;
	gsl_params.gsl_master = grouped_pipes[0]->stream->triggered_crtc_reset.event_source->status.primary_otg_inst;

	for (i = 0; i < group_size; i++)
		grouped_pipes[i]->stream_res.tg->funcs->setup_global_swap_lock(
					grouped_pipes[i]->stream_res.tg, &gsl_params);

	DC_SYNC_INFO("GSL: enabling trigger-reset\n");

	for (i = 1; i < group_size; i++)
		grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset(
				grouped_pipes[i]->stream_res.tg,
				gsl_params.gsl_master,
				&grouped_pipes[i]->stream->triggered_crtc_reset);

	DC_SYNC_INFO("GSL: waiting for reset to occur.\n");
	for (i = 1; i < group_size; i++)
		wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg);

	for (i = 0; i < group_size; i++)
		grouped_pipes[i]->stream_res.tg->funcs->tear_down_global_swap_lock(grouped_pipes[i]->stream_res.tg);

}

static void init_hw(struct dc *dc)
{
	int i;
@@ -2976,6 +3005,7 @@ static const struct hw_sequencer_funcs dce110_funcs = {
	.power_down = dce110_power_down,
	.enable_accelerated_mode = dce110_enable_accelerated_mode,
	.enable_timing_synchronization = dce110_enable_timing_synchronization,
	.enable_per_frame_crtc_position_reset = dce110_enable_per_frame_crtc_position_reset,
	.update_info_frame = dce110_update_info_frame,
	.enable_stream = dce110_enable_stream,
	.disable_stream = dce110_disable_stream,
Loading