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 1861a147a7fa1f31e17dcc28e1b067c5dfa0f7c4..f977f29907df5898bc88c712dff6f7c510fb5516 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 5987391824613b21e8d955895bcdd2ceaafbd0ce..c250f6de5136e1832a556b2eda275d8cfaab8011 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 a43c008bd0f29d0c625604c59d3d7213caf2a720..963b58803ae5bae70d5be1530249284fb0581478 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 e293b3050cd0af0ca823700da7d9e8b575146d16..dc1752e9f461fbe7dbee4f35fdb1fc333a40f7bc 100644 (file)
@@ -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,
index eea2254b15e4a58a6e34eba40a6ec3034e1f481b..9204c3ef323b22c57f4050c669b179300e01f8a3 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 de4723f33c60bb9707319839c5458727db417cc5..a17fe8ab290400a35d617771c99035582fcb9d91 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 6bba191cd33e3a34a3bb6f86da020ac096a17b46..33a36c02b2f8f6b09032e738de53d2cbc5ae180a 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 eac08926b57419815d1f68a1236e8f37ae86c25b..6a4dcafb9bba5c3c9f0cb87a2b001c1edcd9c010 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 3e99bb9c70ab8e3dcaa099cad2c45655302e857d..1db1ca19411d8d4b6a1115a439d75b155fc40b06 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 e9a0005288d32168d1995353725cffca1918adbc..73e7b690e82c90660c0437785cbd9c25aaa84cdf 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 4926771ddeeeaf785ee1e948155052c38d71ef70..bb347319de83a4670ffa7983a6ba6c3e98a57059 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 a17e5de3b100de810da4a0e6dd4e73f53e04c35f..c920c4b6077d43977b567af6d648bae2cae0855f 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 80e1a32bc63dac04216072d047bec4b805975eca..2c031586f4e64e903b8cfc8c9462dbfbb8891d62 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 d50f4bd06b5d9be7b798476494e8161726221617..05053f3b4ab7b770a3ce14717d957eb80402b3f2 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);