drm/amd/display: implement dc_mode_memclk
authorMartin Leung <Martin.Leung@amd.com>
Fri, 10 Dec 2021 23:04:07 +0000 (15:04 -0800)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 14 Dec 2021 21:08:41 +0000 (16:08 -0500)
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: Aric Cyr <Aric.Cyr@amd.com>
Acked-by: Pavle Kotarac <Pavle.Kotarac@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Martin Leung <Martin.Leung@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
14 files changed:
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c
drivers/gpu/drm/amd/display/dc/core/dc.c
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.h
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.h
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hubp.c
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.c
drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h
drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h

index 1861a14..f977f29 100644 (file)
@@ -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,
index 5987391..c250f6d 100644 (file)
@@ -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)
 {
index a43c008..963b588 100644 (file)
@@ -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);
 
index e293b30..dc1752e 100644 (file)
@@ -929,6 +929,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,
index eea2254..9204c3e 100644 (file)
@@ -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,
index de4723f..a17fe8a 100644 (file)
@@ -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,
index 6bba191..33a36c0 100644 (file)
@@ -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,
index eac0892..6a4dcaf 100644 (file)
@@ -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,
index 3e99bb9..1db1ca1 100644 (file)
@@ -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)
index e9a0005..73e7b69 100644 (file)
@@ -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,
index 4926771..bb34731 100644 (file)
@@ -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,
index a17e5de..c920c4b 100644 (file)
@@ -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;
index 80e1a32..2c03158 100644 (file)
@@ -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)(
index d50f4bd..05053f3 100644 (file)
@@ -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);