Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Martin Leung and committed by
Alex Deucher
4866b0bf b4771435

+188 -7
+41 -3
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c
··· 252 252 bool update_dispclk = false; 253 253 bool enter_display_off = false; 254 254 bool dpp_clock_lowered = false; 255 + bool update_pstate_unsupported_clk = false; 255 256 struct dmcu *dmcu = clk_mgr_base->ctx->dc->res_pool->dmcu; 256 257 bool force_reset = false; 257 258 bool update_uclk = false; ··· 300 299 clk_mgr_base->clks.prev_p_state_change_support = clk_mgr_base->clks.p_state_change_support; 301 300 total_plane_count = clk_mgr_helper_get_active_plane_cnt(dc, context); 302 301 p_state_change_support = new_clocks->p_state_change_support || (total_plane_count == 0); 303 - if (should_update_pstate_support(safe_to_lower, p_state_change_support, clk_mgr_base->clks.p_state_change_support)) { 302 + 303 + // invalidate the current P-State forced min in certain dc_mode_softmax situations 304 + if (dc->clk_mgr->dc_mode_softmax_enabled && safe_to_lower && !p_state_change_support) { 305 + if ((new_clocks->dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000) != 306 + (clk_mgr_base->clks.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)) 307 + update_pstate_unsupported_clk = true; 308 + } 309 + 310 + if (should_update_pstate_support(safe_to_lower, p_state_change_support, clk_mgr_base->clks.p_state_change_support) || 311 + update_pstate_unsupported_clk) { 304 312 clk_mgr_base->clks.p_state_change_support = p_state_change_support; 305 313 306 314 /* to disable P-State switching, set UCLK min = max */ 307 - if (!clk_mgr_base->clks.p_state_change_support) 308 - dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, 315 + if (!clk_mgr_base->clks.p_state_change_support) { 316 + if (dc->clk_mgr->dc_mode_softmax_enabled && 317 + new_clocks->dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000) 318 + dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, 319 + dc->clk_mgr->bw_params->dc_mode_softmax_memclk); 320 + else 321 + dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, 309 322 clk_mgr_base->bw_params->clk_table.entries[clk_mgr_base->bw_params->clk_table.num_entries - 1].memclk_mhz); 323 + } 310 324 } 311 325 312 326 /* Always update saved value, even if new value not set due to P-State switching unsupported */ ··· 437 421 clk_mgr_base->bw_params->clk_table.entries[clk_mgr_base->bw_params->clk_table.num_entries - 1].memclk_mhz); 438 422 } 439 423 424 + static void dcn3_set_max_memclk(struct clk_mgr *clk_mgr_base, unsigned int memclk_mhz) 425 + { 426 + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 427 + 428 + if (!clk_mgr->smu_present) 429 + return; 430 + 431 + dcn30_smu_set_hard_max_by_freq(clk_mgr, PPCLK_UCLK, memclk_mhz); 432 + } 433 + static void dcn3_set_min_memclk(struct clk_mgr *clk_mgr_base, unsigned int memclk_mhz) 434 + { 435 + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 436 + 437 + if (!clk_mgr->smu_present) 438 + return; 439 + dcn30_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, memclk_mhz); 440 + } 441 + 440 442 /* Get current memclk states, update bounding box */ 441 443 static void dcn3_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) 442 444 { ··· 469 435 &clk_mgr_base->bw_params->clk_table.entries[0].memclk_mhz, 470 436 &num_levels); 471 437 clk_mgr_base->bw_params->clk_table.num_entries = num_levels ? num_levels : 1; 438 + 439 + clk_mgr_base->bw_params->dc_mode_softmax_memclk = dcn30_smu_get_dc_mode_max_dpm_freq(clk_mgr, PPCLK_UCLK); 472 440 473 441 /* Refresh bounding box */ 474 442 clk_mgr_base->ctx->dc->res_pool->funcs->update_bw_bounding_box( ··· 541 505 .notify_wm_ranges = dcn3_notify_wm_ranges, 542 506 .set_hard_min_memclk = dcn3_set_hard_min_memclk, 543 507 .set_hard_max_memclk = dcn3_set_hard_max_memclk, 508 + .set_max_memclk = dcn3_set_max_memclk, 509 + .set_min_memclk = dcn3_set_min_memclk, 544 510 .get_memclk_states_from_smu = dcn3_get_memclk_states_from_smu, 545 511 .are_clock_states_equal = dcn3_are_clock_states_equal, 546 512 .enable_pme_wa = dcn3_enable_pme_wa,
+92
drivers/gpu/drm/amd/display/dc/core/dc.c
··· 3605 3605 core_link_enable_stream(dc->current_state, &dc->current_state->res_ctx.pipe_ctx[i]); 3606 3606 } 3607 3607 3608 + static void blank_and_force_memclk(struct dc *dc, bool apply, unsigned int memclk_mhz) 3609 + { 3610 + struct dc_state *context = dc->current_state; 3611 + struct hubp *hubp; 3612 + struct pipe_ctx *pipe; 3613 + int i; 3614 + 3615 + for (i = 0; i < dc->res_pool->pipe_count; i++) { 3616 + pipe = &context->res_ctx.pipe_ctx[i]; 3617 + 3618 + if (pipe->stream != NULL) { 3619 + dc->hwss.disable_pixel_data(dc, pipe, true); 3620 + 3621 + // wait for double buffer 3622 + pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VACTIVE); 3623 + pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VBLANK); 3624 + pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VACTIVE); 3625 + 3626 + hubp = pipe->plane_res.hubp; 3627 + hubp->funcs->set_blank_regs(hubp, true); 3628 + } 3629 + } 3630 + 3631 + dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, memclk_mhz); 3632 + dc->clk_mgr->funcs->set_min_memclk(dc->clk_mgr, memclk_mhz); 3633 + 3634 + for (i = 0; i < dc->res_pool->pipe_count; i++) { 3635 + pipe = &context->res_ctx.pipe_ctx[i]; 3636 + 3637 + if (pipe->stream != NULL) { 3638 + dc->hwss.disable_pixel_data(dc, pipe, false); 3639 + 3640 + hubp = pipe->plane_res.hubp; 3641 + hubp->funcs->set_blank_regs(hubp, false); 3642 + } 3643 + } 3644 + } 3645 + 3646 + 3647 + /** 3648 + * dc_enable_dcmode_clk_limit() - lower clocks in dc (battery) mode 3649 + * @dc: pointer to dc of the dm calling this 3650 + * @enable: True = transition to DC mode, false = transition back to AC mode 3651 + * 3652 + * Some SoCs define additional clock limits when in DC mode, DM should 3653 + * invoke this function when the platform undergoes a power source transition 3654 + * so DC can apply/unapply the limit. This interface may be disruptive to 3655 + * the onscreen content. 3656 + * 3657 + * Context: Triggered by OS through DM interface, or manually by escape calls. 3658 + * Need to hold a dclock when doing so. 3659 + * 3660 + * Return: none (void function) 3661 + * 3662 + */ 3663 + void dc_enable_dcmode_clk_limit(struct dc *dc, bool enable) 3664 + { 3665 + uint32_t hw_internal_rev = dc->ctx->asic_id.hw_internal_rev; 3666 + unsigned int softMax, maxDPM, funcMin; 3667 + bool p_state_change_support; 3668 + 3669 + if (!ASICREV_IS_BEIGE_GOBY_P(hw_internal_rev)) 3670 + return; 3671 + 3672 + softMax = dc->clk_mgr->bw_params->dc_mode_softmax_memclk; 3673 + maxDPM = dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz; 3674 + funcMin = (dc->clk_mgr->clks.dramclk_khz + 999) / 1000; 3675 + p_state_change_support = dc->clk_mgr->clks.p_state_change_support; 3676 + 3677 + if (enable && !dc->clk_mgr->dc_mode_softmax_enabled) { 3678 + if (p_state_change_support) { 3679 + if (funcMin <= softMax) 3680 + dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, softMax); 3681 + // else: No-Op 3682 + } else { 3683 + if (funcMin <= softMax) 3684 + blank_and_force_memclk(dc, true, softMax); 3685 + // else: No-Op 3686 + } 3687 + } else if (!enable && dc->clk_mgr->dc_mode_softmax_enabled) { 3688 + if (p_state_change_support) { 3689 + if (funcMin <= softMax) 3690 + dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, maxDPM); 3691 + // else: No-Op 3692 + } else { 3693 + if (funcMin <= softMax) 3694 + blank_and_force_memclk(dc, true, maxDPM); 3695 + // else: No-Op 3696 + } 3697 + } 3698 + dc->clk_mgr->dc_mode_softmax_enabled = enable; 3699 + } 3608 3700 bool dc_is_plane_eligible_for_idle_optimizations(struct dc *dc, struct dc_plane_state *plane, 3609 3701 struct dc_cursor_attributes *cursor_attr) 3610 3702 {
+3
drivers/gpu/drm/amd/display/dc/dc.h
··· 1432 1432 */ 1433 1433 void dc_lock_memory_clock_frequency(struct dc *dc); 1434 1434 1435 + /* set soft max for memclk, to be used for AC/DC switching clock limitations */ 1436 + void dc_enable_dcmode_clk_limit(struct dc *dc, bool enable); 1437 + 1435 1438 /* cleanup on driver unload */ 1436 1439 void dc_hardware_release(struct dc *dc); 1437 1440
+11 -3
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.c
··· 929 929 930 930 void hubp2_set_blank(struct hubp *hubp, bool blank) 931 931 { 932 + hubp2_set_blank_regs(hubp, blank); 933 + 934 + if (blank) { 935 + hubp->mpcc_id = 0xf; 936 + hubp->opp_id = OPP_ID_INVALID; 937 + } 938 + } 939 + 940 + void hubp2_set_blank_regs(struct hubp *hubp, bool blank) 941 + { 932 942 struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); 933 943 uint32_t blank_en = blank ? 1 : 0; 934 944 ··· 960 950 HUBP_NO_OUTSTANDING_REQ, 1, 961 951 1, 200); 962 952 } 963 - 964 - hubp->mpcc_id = 0xf; 965 - hubp->opp_id = OPP_ID_INVALID; 966 953 } 967 954 } 968 955 ··· 1609 1602 .hubp_setup_interdependent = hubp2_setup_interdependent, 1610 1603 .hubp_set_vm_system_aperture_settings = hubp2_set_vm_system_aperture_settings, 1611 1604 .set_blank = hubp2_set_blank, 1605 + .set_blank_regs = hubp2_set_blank_regs, 1612 1606 .dcc_control = hubp2_dcc_control, 1613 1607 .mem_program_viewport = min_set_viewport, 1614 1608 .set_cursor_attributes = hubp2_cursor_set_attributes,
+1
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.h
··· 330 330 bool hubp2_is_flip_pending(struct hubp *hubp); 331 331 332 332 void hubp2_set_blank(struct hubp *hubp, bool blank); 333 + void hubp2_set_blank_regs(struct hubp *hubp, bool blank); 333 334 334 335 void hubp2_cursor_set_position( 335 336 struct hubp *hubp,
+10
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
··· 615 615 pipe_ctx->pipe_idx); 616 616 } 617 617 618 + void dcn20_disable_pixel_data(struct dc *dc, struct pipe_ctx *pipe_ctx, bool blank) 619 + { 620 + dcn20_blank_pixel_data(dc, pipe_ctx, blank); 621 + } 622 + 618 623 static int calc_mpc_flow_ctrl_cnt(const struct dc_stream_state *stream, 619 624 int opp_cnt) 620 625 { ··· 1844 1839 &context->bw_ctx.bw.dcn.watermarks, 1845 1840 dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, 1846 1841 true); 1842 + 1843 + if (dc->clk_mgr->dc_mode_softmax_enabled) 1844 + if (dc->clk_mgr->clks.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 && 1845 + context->bw_ctx.bw.dcn.clk.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000) 1846 + dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->dc_mode_softmax_memclk); 1847 1847 1848 1848 dc->clk_mgr->funcs->update_clocks( 1849 1849 dc->clk_mgr,
+4
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.h
··· 53 53 void dcn20_unblank_stream(struct pipe_ctx *pipe_ctx, 54 54 struct dc_link_settings *link_settings); 55 55 void dcn20_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx); 56 + void dcn20_disable_pixel_data( 57 + struct dc *dc, 58 + struct pipe_ctx *pipe_ctx, 59 + bool blank); 56 60 void dcn20_blank_pixel_data( 57 61 struct dc *dc, 58 62 struct pipe_ctx *pipe_ctx,
+1
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hubp.c
··· 490 490 .hubp_setup_interdependent = hubp2_setup_interdependent, 491 491 .hubp_set_vm_system_aperture_settings = hubp3_set_vm_system_aperture_settings, 492 492 .set_blank = hubp2_set_blank, 493 + .set_blank_regs = hubp2_set_blank_regs, 493 494 .dcc_control = hubp3_dcc_control, 494 495 .mem_program_viewport = min_set_viewport, 495 496 .set_cursor_attributes = hubp2_cursor_set_attributes,
+11
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
··· 344 344 dwb->funcs->enable(dwb, &wb_info->dwb_params); 345 345 } 346 346 347 + void dcn30_prepare_bandwidth(struct dc *dc, 348 + struct dc_state *context) 349 + { 350 + if (dc->clk_mgr->dc_mode_softmax_enabled) 351 + if (dc->clk_mgr->clks.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 && 352 + context->bw_ctx.bw.dcn.clk.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000) 353 + 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); 354 + 355 + dcn20_prepare_bandwidth(dc, context); 356 + } 357 + 347 358 void dcn30_disable_writeback( 348 359 struct dc *dc, 349 360 unsigned int dwb_pipe_inst)
+4 -1
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
··· 27 27 #define __DC_HWSS_DCN30_H__ 28 28 29 29 #include "hw_sequencer_private.h" 30 - 30 + #include "dcn20/dcn20_hwseq.h" 31 31 struct dc; 32 32 33 33 void dcn30_init_hw(struct dc *dc); ··· 46 46 void dcn30_disable_writeback( 47 47 struct dc *dc, 48 48 unsigned int dwb_pipe_inst); 49 + 50 + void dcn30_prepare_bandwidth(struct dc *dc, 51 + struct dc_state *context); 49 52 50 53 bool dcn30_mmhubbub_warmup( 51 54 struct dc *dc,
+1
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.c
··· 55 55 .enable_audio_stream = dce110_enable_audio_stream, 56 56 .disable_audio_stream = dce110_disable_audio_stream, 57 57 .disable_plane = dcn20_disable_plane, 58 + .disable_pixel_data = dcn20_disable_pixel_data, 58 59 .pipe_control_lock = dcn20_pipe_control_lock, 59 60 .interdependent_update_lock = dcn10_lock_all_pipes, 60 61 .cursor_lock = dcn10_cursor_lock,
+7
drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h
··· 211 211 struct clk_bw_params { 212 212 unsigned int vram_type; 213 213 unsigned int num_channels; 214 + unsigned int dispclk_vco_khz; 215 + unsigned int dc_mode_softmax_memclk; 214 216 struct clk_limit_table clk_table; 215 217 struct wm_table wm_table; 216 218 struct dummy_pstate_entry dummy_pstate_table[4]; ··· 263 261 /* Send message to PMFW to set hard max memclk frequency to highest DPM */ 264 262 void (*set_hard_max_memclk)(struct clk_mgr *clk_mgr); 265 263 264 + /* Custom set a memclk freq range*/ 265 + void (*set_max_memclk)(struct clk_mgr *clk_mgr, unsigned int memclk_mhz); 266 + void (*set_min_memclk)(struct clk_mgr *clk_mgr, unsigned int memclk_mhz); 267 + 266 268 /* Get current memclk states from PMFW, update relevant structures */ 267 269 void (*get_memclk_states_from_smu)(struct clk_mgr *clk_mgr); 268 270 ··· 280 274 struct dc_clocks clks; 281 275 bool psr_allow_active_cache; 282 276 bool force_smu_not_present; 277 + bool dc_mode_softmax_enabled; 283 278 int dprefclk_khz; // Used by program pixel clock in clock source funcs, need to figureout where this goes 284 279 int dentist_vco_freq_khz; 285 280 struct clk_state_registers_and_bypass boot_snapshot;
+1
drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
··· 139 139 bool (*hubp_is_flip_pending)(struct hubp *hubp); 140 140 141 141 void (*set_blank)(struct hubp *hubp, bool blank); 142 + void (*set_blank_regs)(struct hubp *hubp, bool blank); 142 143 void (*set_hubp_blank_en)(struct hubp *hubp, bool blank); 143 144 144 145 void (*set_cursor_attributes)(
+1
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h
··· 64 64 enum dc_status (*apply_ctx_to_hw)(struct dc *dc, 65 65 struct dc_state *context); 66 66 void (*disable_plane)(struct dc *dc, struct pipe_ctx *pipe_ctx); 67 + void (*disable_pixel_data)(struct dc *dc, struct pipe_ctx *pipe_ctx, bool blank); 67 68 void (*apply_ctx_for_surface)(struct dc *dc, 68 69 const struct dc_stream_state *stream, 69 70 int num_planes, struct dc_state *context);