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

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
......@@ -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,13 +300,28 @@ 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)
dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK,
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 */
......@@ -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,
......
......@@ -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)
{
......
......@@ -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);
......
......@@ -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,
......
......@@ -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,
......
......@@ -615,6 +615,11 @@ void dcn20_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx)
pipe_ctx->pipe_idx);
}
void dcn20_disable_pixel_data(struct dc *dc, struct pipe_ctx *pipe_ctx, bool blank)
{
dcn20_blank_pixel_data(dc, pipe_ctx, blank);
}
static int calc_mpc_flow_ctrl_cnt(const struct dc_stream_state *stream,
int opp_cnt)
{
......@@ -1840,6 +1845,11 @@ void dcn20_optimize_bandwidth(
dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
true);
if (dc->clk_mgr->dc_mode_softmax_enabled)
if (dc->clk_mgr->clks.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&
context->bw_ctx.bw.dcn.clk.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->dc_mode_softmax_memclk);
dc->clk_mgr->funcs->update_clocks(
dc->clk_mgr,
context,
......
......@@ -53,6 +53,10 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx);
void dcn20_unblank_stream(struct pipe_ctx *pipe_ctx,
struct dc_link_settings *link_settings);
void dcn20_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx);
void dcn20_disable_pixel_data(
struct dc *dc,
struct pipe_ctx *pipe_ctx,
bool blank);
void dcn20_blank_pixel_data(
struct dc *dc,
struct pipe_ctx *pipe_ctx,
......
......@@ -490,6 +490,7 @@ static struct hubp_funcs dcn30_hubp_funcs = {
.hubp_setup_interdependent = hubp2_setup_interdependent,
.hubp_set_vm_system_aperture_settings = hubp3_set_vm_system_aperture_settings,
.set_blank = hubp2_set_blank,
.set_blank_regs = hubp2_set_blank_regs,
.dcc_control = hubp3_dcc_control,
.mem_program_viewport = min_set_viewport,
.set_cursor_attributes = hubp2_cursor_set_attributes,
......
......@@ -344,6 +344,17 @@ void dcn30_enable_writeback(
dwb->funcs->enable(dwb, &wb_info->dwb_params);
}
void dcn30_prepare_bandwidth(struct dc *dc,
struct dc_state *context)
{
if (dc->clk_mgr->dc_mode_softmax_enabled)
if (dc->clk_mgr->clks.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&
context->bw_ctx.bw.dcn.clk.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz);
dcn20_prepare_bandwidth(dc, context);
}
void dcn30_disable_writeback(
struct dc *dc,
unsigned int dwb_pipe_inst)
......
......@@ -27,7 +27,7 @@
#define __DC_HWSS_DCN30_H__
#include "hw_sequencer_private.h"
#include "dcn20/dcn20_hwseq.h"
struct dc;
void dcn30_init_hw(struct dc *dc);
......@@ -47,6 +47,9 @@ void dcn30_disable_writeback(
struct dc *dc,
unsigned int dwb_pipe_inst);
void dcn30_prepare_bandwidth(struct dc *dc,
struct dc_state *context);
bool dcn30_mmhubbub_warmup(
struct dc *dc,
unsigned int num_dwb,
......
......@@ -55,6 +55,7 @@ static const struct hw_sequencer_funcs dcn30_funcs = {
.enable_audio_stream = dce110_enable_audio_stream,
.disable_audio_stream = dce110_disable_audio_stream,
.disable_plane = dcn20_disable_plane,
.disable_pixel_data = dcn20_disable_pixel_data,
.pipe_control_lock = dcn20_pipe_control_lock,
.interdependent_update_lock = dcn10_lock_all_pipes,
.cursor_lock = dcn10_cursor_lock,
......
......@@ -211,6 +211,8 @@ struct dummy_pstate_entry {
struct clk_bw_params {
unsigned int vram_type;
unsigned int num_channels;
unsigned int dispclk_vco_khz;
unsigned int dc_mode_softmax_memclk;
struct clk_limit_table clk_table;
struct wm_table wm_table;
struct dummy_pstate_entry dummy_pstate_table[4];
......@@ -261,6 +263,10 @@ struct clk_mgr_funcs {
/* Send message to PMFW to set hard max memclk frequency to highest DPM */
void (*set_hard_max_memclk)(struct clk_mgr *clk_mgr);
/* Custom set a memclk freq range*/
void (*set_max_memclk)(struct clk_mgr *clk_mgr, unsigned int memclk_mhz);
void (*set_min_memclk)(struct clk_mgr *clk_mgr, unsigned int memclk_mhz);
/* Get current memclk states from PMFW, update relevant structures */
void (*get_memclk_states_from_smu)(struct clk_mgr *clk_mgr);
......@@ -274,6 +280,7 @@ struct clk_mgr {
struct dc_clocks clks;
bool psr_allow_active_cache;
bool force_smu_not_present;
bool dc_mode_softmax_enabled;
int dprefclk_khz; // Used by program pixel clock in clock source funcs, need to figureout where this goes
int dentist_vco_freq_khz;
struct clk_state_registers_and_bypass boot_snapshot;
......
......@@ -139,6 +139,7 @@ struct hubp_funcs {
bool (*hubp_is_flip_pending)(struct hubp *hubp);
void (*set_blank)(struct hubp *hubp, bool blank);
void (*set_blank_regs)(struct hubp *hubp, bool blank);
void (*set_hubp_blank_en)(struct hubp *hubp, bool blank);
void (*set_cursor_attributes)(
......
......@@ -64,6 +64,7 @@ struct hw_sequencer_funcs {
enum dc_status (*apply_ctx_to_hw)(struct dc *dc,
struct dc_state *context);
void (*disable_plane)(struct dc *dc, struct pipe_ctx *pipe_ctx);
void (*disable_pixel_data)(struct dc *dc, struct pipe_ctx *pipe_ctx, bool blank);
void (*apply_ctx_for_surface)(struct dc *dc,
const struct dc_stream_state *stream,
int num_planes, struct dc_state *context);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment