Commit 4866b0bf authored by Martin Leung's avatar Martin Leung Committed by Alex Deucher
Browse files

drm/amd/display: implement dc_mode_memclk



why:
Need interface to lower clocks when in dc (power save)
mode. Must be able to work with p_state unsupported cases
Can cause flicker when OS notifies us of dc state change

how:
added dal3 interface for KMD
added pathway to query smu for this softmax
added blank before clock change to override underflow
added logic to change clk based on pstatesupport and softmax
added logic in prepare/optimize_bw to conform while changing
clocks

Reviewed-by: default avatarAric Cyr <Aric.Cyr@amd.com>
Acked-by: default avatarPavle Kotarac <Pavle.Kotarac@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarMartin Leung <Martin.Leung@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent b4771435
Loading
Loading
Loading
Loading
+41 −3
Original line number Diff line number Diff line
@@ -252,6 +252,7 @@ static void dcn3_update_clocks(struct clk_mgr *clk_mgr_base,
	bool update_dispclk = false;
	bool enter_display_off = false;
	bool dpp_clock_lowered = false;
	bool update_pstate_unsupported_clk = false;
	struct dmcu *dmcu = clk_mgr_base->ctx->dc->res_pool->dmcu;
	bool force_reset = false;
	bool update_uclk = false;
@@ -299,14 +300,29 @@ static void dcn3_update_clocks(struct clk_mgr *clk_mgr_base,
	clk_mgr_base->clks.prev_p_state_change_support = clk_mgr_base->clks.p_state_change_support;
	total_plane_count = clk_mgr_helper_get_active_plane_cnt(dc, context);
	p_state_change_support = new_clocks->p_state_change_support || (total_plane_count == 0);
	if (should_update_pstate_support(safe_to_lower, p_state_change_support, clk_mgr_base->clks.p_state_change_support)) {

	// invalidate the current P-State forced min in certain dc_mode_softmax situations
	if (dc->clk_mgr->dc_mode_softmax_enabled && safe_to_lower && !p_state_change_support) {
		if ((new_clocks->dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000) !=
				(clk_mgr_base->clks.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000))
			update_pstate_unsupported_clk = true;
	}

	if (should_update_pstate_support(safe_to_lower, p_state_change_support, clk_mgr_base->clks.p_state_change_support) ||
			update_pstate_unsupported_clk) {
		clk_mgr_base->clks.p_state_change_support = p_state_change_support;

		/* to disable P-State switching, set UCLK min = max */
		if (!clk_mgr_base->clks.p_state_change_support)
		if (!clk_mgr_base->clks.p_state_change_support) {
			if (dc->clk_mgr->dc_mode_softmax_enabled &&
				new_clocks->dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
				dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK,
					dc->clk_mgr->bw_params->dc_mode_softmax_memclk);
			else
				dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK,
					clk_mgr_base->bw_params->clk_table.entries[clk_mgr_base->bw_params->clk_table.num_entries - 1].memclk_mhz);
		}
	}

	/* Always update saved value, even if new value not set due to P-State switching unsupported */
	if (should_set_clock(safe_to_lower, new_clocks->dramclk_khz, clk_mgr_base->clks.dramclk_khz)) {
@@ -421,6 +437,24 @@ static void dcn3_set_hard_max_memclk(struct clk_mgr *clk_mgr_base)
			clk_mgr_base->bw_params->clk_table.entries[clk_mgr_base->bw_params->clk_table.num_entries - 1].memclk_mhz);
}

static void dcn3_set_max_memclk(struct clk_mgr *clk_mgr_base, unsigned int memclk_mhz)
{
	struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);

	if (!clk_mgr->smu_present)
		return;

	dcn30_smu_set_hard_max_by_freq(clk_mgr, PPCLK_UCLK, memclk_mhz);
}
static void dcn3_set_min_memclk(struct clk_mgr *clk_mgr_base, unsigned int memclk_mhz)
{
	struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);

	if (!clk_mgr->smu_present)
		return;
	dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, memclk_mhz);
}

/* Get current memclk states, update bounding box */
static void dcn3_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base)
{
@@ -436,6 +470,8 @@ static void dcn3_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base)
			&num_levels);
	clk_mgr_base->bw_params->clk_table.num_entries = num_levels ? num_levels : 1;

	clk_mgr_base->bw_params->dc_mode_softmax_memclk = dcn30_smu_get_dc_mode_max_dpm_freq(clk_mgr, PPCLK_UCLK);

	/* Refresh bounding box */
	clk_mgr_base->ctx->dc->res_pool->funcs->update_bw_bounding_box(
			clk_mgr->base.ctx->dc, clk_mgr_base->bw_params);
@@ -505,6 +541,8 @@ static struct clk_mgr_funcs dcn3_funcs = {
		.notify_wm_ranges = dcn3_notify_wm_ranges,
		.set_hard_min_memclk = dcn3_set_hard_min_memclk,
		.set_hard_max_memclk = dcn3_set_hard_max_memclk,
		.set_max_memclk = dcn3_set_max_memclk,
		.set_min_memclk = dcn3_set_min_memclk,
		.get_memclk_states_from_smu = dcn3_get_memclk_states_from_smu,
		.are_clock_states_equal = dcn3_are_clock_states_equal,
		.enable_pme_wa = dcn3_enable_pme_wa,
+92 −0
Original line number Diff line number Diff line
@@ -3605,6 +3605,98 @@ void dc_lock_memory_clock_frequency(struct dc *dc)
			core_link_enable_stream(dc->current_state, &dc->current_state->res_ctx.pipe_ctx[i]);
}

static void blank_and_force_memclk(struct dc *dc, bool apply, unsigned int memclk_mhz)
{
	struct dc_state *context = dc->current_state;
	struct hubp *hubp;
	struct pipe_ctx *pipe;
	int i;

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

		if (pipe->stream != NULL) {
			dc->hwss.disable_pixel_data(dc, pipe, true);

			// wait for double buffer
			pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VACTIVE);
			pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VBLANK);
			pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VACTIVE);

			hubp = pipe->plane_res.hubp;
			hubp->funcs->set_blank_regs(hubp, true);
		}
	}

	dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, memclk_mhz);
	dc->clk_mgr->funcs->set_min_memclk(dc->clk_mgr, memclk_mhz);

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

		if (pipe->stream != NULL) {
			dc->hwss.disable_pixel_data(dc, pipe, false);

			hubp = pipe->plane_res.hubp;
			hubp->funcs->set_blank_regs(hubp, false);
		}
	}
}


/**
 * dc_enable_dcmode_clk_limit() - lower clocks in dc (battery) mode
 * @dc: pointer to dc of the dm calling this
 * @enable: True = transition to DC mode, false = transition back to AC mode
 *
 * Some SoCs define additional clock limits when in DC mode, DM should
 * invoke this function when the platform undergoes a power source transition
 * so DC can apply/unapply the limit. This interface may be disruptive to
 * the onscreen content.
 *
 * Context: Triggered by OS through DM interface, or manually by escape calls.
 * Need to hold a dclock when doing so.
 *
 * Return: none (void function)
 *
 */
void dc_enable_dcmode_clk_limit(struct dc *dc, bool enable)
{
	uint32_t hw_internal_rev = dc->ctx->asic_id.hw_internal_rev;
	unsigned int softMax, maxDPM, funcMin;
	bool p_state_change_support;

	if (!ASICREV_IS_BEIGE_GOBY_P(hw_internal_rev))
		return;

	softMax = dc->clk_mgr->bw_params->dc_mode_softmax_memclk;
	maxDPM = dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz;
	funcMin = (dc->clk_mgr->clks.dramclk_khz + 999) / 1000;
	p_state_change_support = dc->clk_mgr->clks.p_state_change_support;

	if (enable && !dc->clk_mgr->dc_mode_softmax_enabled) {
		if (p_state_change_support) {
			if (funcMin <= softMax)
				dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, softMax);
			// else: No-Op
		} else {
			if (funcMin <= softMax)
				blank_and_force_memclk(dc, true, softMax);
			// else: No-Op
		}
	} else if (!enable && dc->clk_mgr->dc_mode_softmax_enabled) {
		if (p_state_change_support) {
			if (funcMin <= softMax)
				dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, maxDPM);
			// else: No-Op
		} else {
			if (funcMin <= softMax)
				blank_and_force_memclk(dc, true, maxDPM);
			// else: No-Op
		}
	}
	dc->clk_mgr->dc_mode_softmax_enabled = enable;
}
bool dc_is_plane_eligible_for_idle_optimizations(struct dc *dc, struct dc_plane_state *plane,
		struct dc_cursor_attributes *cursor_attr)
{
+3 −0
Original line number Diff line number Diff line
@@ -1432,6 +1432,9 @@ void dc_unlock_memory_clock_frequency(struct dc *dc);
 */
void dc_lock_memory_clock_frequency(struct dc *dc);

/* set soft max for memclk, to be used for AC/DC switching clock limitations */
void dc_enable_dcmode_clk_limit(struct dc *dc, bool enable);

/* cleanup on driver unload */
void dc_hardware_release(struct dc *dc);

+11 −3
Original line number Diff line number Diff line
@@ -928,6 +928,16 @@ bool hubp2_is_flip_pending(struct hubp *hubp)
}

void hubp2_set_blank(struct hubp *hubp, bool blank)
{
	hubp2_set_blank_regs(hubp, blank);

	if (blank) {
		hubp->mpcc_id = 0xf;
		hubp->opp_id = OPP_ID_INVALID;
	}
}

void hubp2_set_blank_regs(struct hubp *hubp, bool blank)
{
	struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp);
	uint32_t blank_en = blank ? 1 : 0;
@@ -950,9 +960,6 @@ void hubp2_set_blank(struct hubp *hubp, bool blank)
					HUBP_NO_OUTSTANDING_REQ, 1,
					1, 200);
		}

		hubp->mpcc_id = 0xf;
		hubp->opp_id = OPP_ID_INVALID;
	}
}

@@ -1602,6 +1609,7 @@ static struct hubp_funcs dcn20_hubp_funcs = {
	.hubp_setup_interdependent = hubp2_setup_interdependent,
	.hubp_set_vm_system_aperture_settings = hubp2_set_vm_system_aperture_settings,
	.set_blank = hubp2_set_blank,
	.set_blank_regs = hubp2_set_blank_regs,
	.dcc_control = hubp2_dcc_control,
	.mem_program_viewport = min_set_viewport,
	.set_cursor_attributes	= hubp2_cursor_set_attributes,
+1 −0
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ void hubp2_program_surface_config(
bool hubp2_is_flip_pending(struct hubp *hubp);

void hubp2_set_blank(struct hubp *hubp, bool blank);
void hubp2_set_blank_regs(struct hubp *hubp, bool blank);

void hubp2_cursor_set_position(
		struct hubp *hubp,
Loading