1*b11107cbSLohita Mudimela // SPDX-License-Identifier: MIT 2*b11107cbSLohita Mudimela // 3*b11107cbSLohita Mudimela // Copyright 2026 Advanced Micro Devices, Inc. 4*b11107cbSLohita Mudimela 5*b11107cbSLohita Mudimela #include "dm_services.h" 6*b11107cbSLohita Mudimela #include "dc.h" 7*b11107cbSLohita Mudimela #include "mod_power.h" 8*b11107cbSLohita Mudimela #include "core_types.h" 9*b11107cbSLohita Mudimela #include "dmcu.h" 10*b11107cbSLohita Mudimela #include "abm.h" 11*b11107cbSLohita Mudimela #include "power_helpers.h" 12*b11107cbSLohita Mudimela #include "dce/dmub_psr.h" 13*b11107cbSLohita Mudimela #include "dal_asic_id.h" 14*b11107cbSLohita Mudimela #include "link_service.h" 15*b11107cbSLohita Mudimela #include <linux/math.h> 16*b11107cbSLohita Mudimela 17*b11107cbSLohita Mudimela #define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ 18*b11107cbSLohita Mudimela #define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */ 19*b11107cbSLohita Mudimela #include "dc/inc/hw/dmcu.h" 20*b11107cbSLohita Mudimela #include "dc/inc/hw/abm.h" 21*b11107cbSLohita Mudimela #include "dmub_cmd.h" 22*b11107cbSLohita Mudimela 23*b11107cbSLohita Mudimela #define MOD_POWER_TO_CORE(mod_power)\ 24*b11107cbSLohita Mudimela container_of(mod_power, struct core_power, mod_public) 25*b11107cbSLohita Mudimela 26*b11107cbSLohita Mudimela static unsigned int calc_psr_num_static_frames(unsigned int vsync_rate_hz) 27*b11107cbSLohita Mudimela { 28*b11107cbSLohita Mudimela /* Initialize fail-safe to 2 static frames. */ 29*b11107cbSLohita Mudimela unsigned int num_frames_static = 2; 30*b11107cbSLohita Mudimela 31*b11107cbSLohita Mudimela /* Calculate number of frames such that at least 30 ms has passed. 32*b11107cbSLohita Mudimela * Round up to ensure the static period is not shorter than 30 ms. 33*b11107cbSLohita Mudimela */ 34*b11107cbSLohita Mudimela if (vsync_rate_hz != 0) 35*b11107cbSLohita Mudimela num_frames_static = DIV_ROUND_UP(30000 * vsync_rate_hz, 1000000); 36*b11107cbSLohita Mudimela 37*b11107cbSLohita Mudimela return num_frames_static; 38*b11107cbSLohita Mudimela } 39*b11107cbSLohita Mudimela 40*b11107cbSLohita Mudimela bool mod_power_psr_notify_mode_change(struct mod_power *mod_power, 41*b11107cbSLohita Mudimela const struct dc_stream_state *stream, 42*b11107cbSLohita Mudimela struct dc_link *link, 43*b11107cbSLohita Mudimela unsigned int stream_index) 44*b11107cbSLohita Mudimela { 45*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 46*b11107cbSLohita Mudimela struct dc *dc = NULL; 47*b11107cbSLohita Mudimela struct psr_config psr_config = {0}; 48*b11107cbSLohita Mudimela struct psr_context psr_context = {0}; 49*b11107cbSLohita Mudimela int active_psr_events = 0; 50*b11107cbSLohita Mudimela 51*b11107cbSLohita Mudimela if ((mod_power == NULL) || (stream == NULL) || (link == NULL)) 52*b11107cbSLohita Mudimela return false; 53*b11107cbSLohita Mudimela 54*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 55*b11107cbSLohita Mudimela dc = core_power->dc; 56*b11107cbSLohita Mudimela 57*b11107cbSLohita Mudimela // NO num_entities check here - already validated by caller 58*b11107cbSLohita Mudimela // stream_index is passed as validated parameter 59*b11107cbSLohita Mudimela active_psr_events = core_power->map[stream_index].psr_events; 60*b11107cbSLohita Mudimela 61*b11107cbSLohita Mudimela /* Calculate PSR configurations */ 62*b11107cbSLohita Mudimela mod_power_calc_psr_configs(&psr_config, link, stream); 63*b11107cbSLohita Mudimela 64*b11107cbSLohita Mudimela psr_config.psr_exit_link_training_required = 65*b11107cbSLohita Mudimela core_power->map[stream_index].caps->psr_exit_link_training_required; 66*b11107cbSLohita Mudimela if (dc->ctx->asic_id.chip_family >= AMDGPU_FAMILY_GC_11_0_1) 67*b11107cbSLohita Mudimela psr_config.allow_smu_optimizations = 68*b11107cbSLohita Mudimela core_power->psr_smu_optimizations_support && dc_is_embedded_signal(stream->signal); 69*b11107cbSLohita Mudimela else 70*b11107cbSLohita Mudimela psr_config.allow_smu_optimizations = 71*b11107cbSLohita Mudimela core_power->psr_smu_optimizations_support && 72*b11107cbSLohita Mudimela mod_power_only_edp(dc->current_state, stream); 73*b11107cbSLohita Mudimela 74*b11107cbSLohita Mudimela psr_config.allow_multi_disp_optimizations = core_power->multi_disp_optimizations_support; 75*b11107cbSLohita Mudimela 76*b11107cbSLohita Mudimela psr_config.rate_control_caps = core_power->map[stream_index].caps->rate_control_caps; 77*b11107cbSLohita Mudimela 78*b11107cbSLohita Mudimela if (active_psr_events & psr_event_os_request_force_ffu) 79*b11107cbSLohita Mudimela psr_config.os_request_force_ffu = true; 80*b11107cbSLohita Mudimela 81*b11107cbSLohita Mudimela /* 82*b11107cbSLohita Mudimela * DSC support: 83*b11107cbSLohita Mudimela * DSC slice height value must be 'mod' by su_y_granularity. 84*b11107cbSLohita Mudimela * According to Panel Vendor, there might be varied conditions to fulfill. 85*b11107cbSLohita Mudimela * Right now, DSC slice height value must be multiple of su_y_granularity. 86*b11107cbSLohita Mudimela * 87*b11107cbSLohita Mudimela * The value of DSC slice height is determined in DSC Driver but it does not 88*b11107cbSLohita Mudimela * propagated out here, so we need to calculate it as below 'slice_height'. 89*b11107cbSLohita Mudimela */ 90*b11107cbSLohita Mudimela psr_su_set_dsc_slice_height(dc, link, 91*b11107cbSLohita Mudimela (struct dc_stream_state *) stream, 92*b11107cbSLohita Mudimela &psr_config); 93*b11107cbSLohita Mudimela 94*b11107cbSLohita Mudimela dc_link_setup_psr(link, stream, &psr_config, &psr_context); 95*b11107cbSLohita Mudimela 96*b11107cbSLohita Mudimela return true; 97*b11107cbSLohita Mudimela } 98*b11107cbSLohita Mudimela 99*b11107cbSLohita Mudimela static void mod_power_psr_set_power_opt(struct mod_power *mod_power, 100*b11107cbSLohita Mudimela struct dc_stream_state *stream, 101*b11107cbSLohita Mudimela unsigned int active_psr_events, 102*b11107cbSLohita Mudimela bool psr_enable_request) 103*b11107cbSLohita Mudimela { 104*b11107cbSLohita Mudimela (void)psr_enable_request; 105*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 106*b11107cbSLohita Mudimela struct dc_link *link = NULL; 107*b11107cbSLohita Mudimela unsigned int stream_index = 0; 108*b11107cbSLohita Mudimela unsigned int power_opt = 0; 109*b11107cbSLohita Mudimela 110*b11107cbSLohita Mudimela if (!stream) 111*b11107cbSLohita Mudimela return; 112*b11107cbSLohita Mudimela 113*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 114*b11107cbSLohita Mudimela stream_index = map_index_from_stream(core_power, stream); 115*b11107cbSLohita Mudimela if (!core_power->map[stream_index].caps->psr_version) 116*b11107cbSLohita Mudimela return; 117*b11107cbSLohita Mudimela 118*b11107cbSLohita Mudimela link = dc_stream_get_link(stream); 119*b11107cbSLohita Mudimela 120*b11107cbSLohita Mudimela if (active_psr_events == 0) { 121*b11107cbSLohita Mudimela /* Static Screen */ 122*b11107cbSLohita Mudimela power_opt |= (psr_power_opt_smu_opt_static_screen | psr_power_opt_z10_static_screen | 123*b11107cbSLohita Mudimela psr_power_opt_ds_disable_allow); 124*b11107cbSLohita Mudimela } 125*b11107cbSLohita Mudimela 126*b11107cbSLohita Mudimela /* psr_power_opt_flag is a configuration parameter into the module that determines 127*b11107cbSLohita Mudimela * which optimizations to enable during psr 128*b11107cbSLohita Mudimela */ 129*b11107cbSLohita Mudimela power_opt &= core_power->map[stream_index].caps->psr_power_opt_flag; 130*b11107cbSLohita Mudimela if (core_power->map[stream_index].psr_power_opt != power_opt) { 131*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, 132*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 133*b11107cbSLohita Mudimela "mod_power set_power_opt: psr_power_opt=0x%04x, power_opt=0x%04x active_psr_events=0x%04x, psr_power_opt_flag=0x%04x", 134*b11107cbSLohita Mudimela core_power->map[stream_index].psr_power_opt, 135*b11107cbSLohita Mudimela power_opt, 136*b11107cbSLohita Mudimela active_psr_events, 137*b11107cbSLohita Mudimela core_power->map[stream_index].caps->psr_power_opt_flag); 138*b11107cbSLohita Mudimela dc_link_set_psr_allow_active(link, NULL, false, false, &power_opt); 139*b11107cbSLohita Mudimela core_power->map[stream_index].psr_power_opt = power_opt; 140*b11107cbSLohita Mudimela } 141*b11107cbSLohita Mudimela } 142*b11107cbSLohita Mudimela 143*b11107cbSLohita Mudimela static bool set_psr_enable(struct mod_power *mod_power, 144*b11107cbSLohita Mudimela struct dc_stream_state *stream, 145*b11107cbSLohita Mudimela bool psr_enable, 146*b11107cbSLohita Mudimela bool wait, 147*b11107cbSLohita Mudimela bool force_static) 148*b11107cbSLohita Mudimela { 149*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 150*b11107cbSLohita Mudimela enum dc_psr_state state = PSR_STATE0; 151*b11107cbSLohita Mudimela unsigned int retry_count; 152*b11107cbSLohita Mudimela const unsigned int max_retry = 1000; 153*b11107cbSLohita Mudimela struct dc_link *link = NULL; 154*b11107cbSLohita Mudimela 155*b11107cbSLohita Mudimela if (mod_power == NULL) 156*b11107cbSLohita Mudimela return false; 157*b11107cbSLohita Mudimela 158*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 159*b11107cbSLohita Mudimela 160*b11107cbSLohita Mudimela if (core_power->num_entities == 0) { 161*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, 162*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 163*b11107cbSLohita Mudimela "set psr enable: ERROR: stream=%p num_entities=%u", 164*b11107cbSLohita Mudimela stream, 165*b11107cbSLohita Mudimela core_power->num_entities); 166*b11107cbSLohita Mudimela return false; 167*b11107cbSLohita Mudimela } 168*b11107cbSLohita Mudimela 169*b11107cbSLohita Mudimela if (psr_enable) { 170*b11107cbSLohita Mudimela unsigned int vsync_rate_hz; 171*b11107cbSLohita Mudimela struct dc_static_screen_params params = {0}; 172*b11107cbSLohita Mudimela 173*b11107cbSLohita Mudimela vsync_rate_hz = (unsigned int)div_u64(div_u64(( 174*b11107cbSLohita Mudimela stream->timing.pix_clk_100hz * 100), 175*b11107cbSLohita Mudimela stream->timing.v_total), 176*b11107cbSLohita Mudimela stream->timing.h_total); 177*b11107cbSLohita Mudimela 178*b11107cbSLohita Mudimela params.triggers.cursor_update = true; 179*b11107cbSLohita Mudimela params.triggers.overlay_update = true; 180*b11107cbSLohita Mudimela params.triggers.surface_update = true; 181*b11107cbSLohita Mudimela params.num_frames = calc_psr_num_static_frames(vsync_rate_hz); 182*b11107cbSLohita Mudimela 183*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, 184*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 185*b11107cbSLohita Mudimela "set psr enable: CALCS: pix_clk_100hz=%u v_total=%u h_total=%u vsync_rate_hz=%u num_frames=%u", 186*b11107cbSLohita Mudimela stream->timing.pix_clk_100hz, 187*b11107cbSLohita Mudimela stream->timing.v_total, 188*b11107cbSLohita Mudimela stream->timing.h_total, 189*b11107cbSLohita Mudimela vsync_rate_hz, 190*b11107cbSLohita Mudimela params.num_frames); 191*b11107cbSLohita Mudimela 192*b11107cbSLohita Mudimela dc_stream_set_static_screen_params(core_power->dc, 193*b11107cbSLohita Mudimela &stream, 1, 194*b11107cbSLohita Mudimela ¶ms); 195*b11107cbSLohita Mudimela } 196*b11107cbSLohita Mudimela 197*b11107cbSLohita Mudimela link = dc_stream_get_link(stream); 198*b11107cbSLohita Mudimela 199*b11107cbSLohita Mudimela if (!dc_link_set_psr_allow_active(link, &psr_enable, false, force_static, NULL)) { 200*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, 201*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 202*b11107cbSLohita Mudimela "set psr enable: ERROR: stream=%p link=%p psr_enable=%d", 203*b11107cbSLohita Mudimela stream, 204*b11107cbSLohita Mudimela link, 205*b11107cbSLohita Mudimela psr_enable); 206*b11107cbSLohita Mudimela return false; 207*b11107cbSLohita Mudimela } 208*b11107cbSLohita Mudimela 209*b11107cbSLohita Mudimela if (wait == true) { 210*b11107cbSLohita Mudimela 211*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, 212*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 213*b11107cbSLohita Mudimela "set psr enable: BEGIN WAIT: psr_enable=%d", 214*b11107cbSLohita Mudimela (int)psr_enable); 215*b11107cbSLohita Mudimela 216*b11107cbSLohita Mudimela for (retry_count = 0; retry_count <= max_retry; retry_count++) { 217*b11107cbSLohita Mudimela dc_link_get_psr_state(link, &state); 218*b11107cbSLohita Mudimela if (psr_enable) { 219*b11107cbSLohita Mudimela if (state != PSR_STATE0 && 220*b11107cbSLohita Mudimela (!force_static || state == PSR_STATE3)) 221*b11107cbSLohita Mudimela break; 222*b11107cbSLohita Mudimela } else { 223*b11107cbSLohita Mudimela if (state == PSR_STATE0) 224*b11107cbSLohita Mudimela break; 225*b11107cbSLohita Mudimela } 226*b11107cbSLohita Mudimela udelay(500); 227*b11107cbSLohita Mudimela } 228*b11107cbSLohita Mudimela 229*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, 230*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 231*b11107cbSLohita Mudimela "set psr enable: END WAIT: psr_enable=%d", 232*b11107cbSLohita Mudimela (int)psr_enable); 233*b11107cbSLohita Mudimela 234*b11107cbSLohita Mudimela /* assert if max retry hit */ 235*b11107cbSLohita Mudimela if (retry_count >= max_retry) { 236*b11107cbSLohita Mudimela ASSERT(0); 237*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, 238*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 239*b11107cbSLohita Mudimela "set psr enable: ERROR: retry_count=%u: Unexpectedly long wait for PSR state change.", 240*b11107cbSLohita Mudimela retry_count); 241*b11107cbSLohita Mudimela } 242*b11107cbSLohita Mudimela } else { 243*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, 244*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 245*b11107cbSLohita Mudimela "set psr enable: PSR state change initiated (wait=false): psr_enable=%d", 246*b11107cbSLohita Mudimela (int)psr_enable); 247*b11107cbSLohita Mudimela } 248*b11107cbSLohita Mudimela 249*b11107cbSLohita Mudimela return true; 250*b11107cbSLohita Mudimela } 251*b11107cbSLohita Mudimela 252*b11107cbSLohita Mudimela bool mod_power_get_psr_event(struct mod_power *mod_power, 253*b11107cbSLohita Mudimela struct dc_stream_state *stream, 254*b11107cbSLohita Mudimela unsigned int *active_psr_events) 255*b11107cbSLohita Mudimela { 256*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 257*b11107cbSLohita Mudimela unsigned int stream_index = 0; 258*b11107cbSLohita Mudimela 259*b11107cbSLohita Mudimela if (mod_power == NULL) 260*b11107cbSLohita Mudimela return false; 261*b11107cbSLohita Mudimela 262*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 263*b11107cbSLohita Mudimela 264*b11107cbSLohita Mudimela if (core_power->num_entities == 0) 265*b11107cbSLohita Mudimela return false; 266*b11107cbSLohita Mudimela 267*b11107cbSLohita Mudimela stream_index = map_index_from_stream(core_power, stream); 268*b11107cbSLohita Mudimela 269*b11107cbSLohita Mudimela if (!core_power->map[stream_index].caps->psr_version) 270*b11107cbSLohita Mudimela return false; 271*b11107cbSLohita Mudimela 272*b11107cbSLohita Mudimela *active_psr_events = core_power->map[stream_index].psr_events; 273*b11107cbSLohita Mudimela 274*b11107cbSLohita Mudimela return true; 275*b11107cbSLohita Mudimela } 276*b11107cbSLohita Mudimela 277*b11107cbSLohita Mudimela bool mod_power_set_psr_event(struct mod_power *mod_power, 278*b11107cbSLohita Mudimela struct dc_stream_state *stream, bool set_event, 279*b11107cbSLohita Mudimela enum psr_event event, bool wait) 280*b11107cbSLohita Mudimela { 281*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 282*b11107cbSLohita Mudimela unsigned int stream_index = 0; 283*b11107cbSLohita Mudimela unsigned int active_psr_events = 0; 284*b11107cbSLohita Mudimela bool psr_enable_request = false; 285*b11107cbSLohita Mudimela bool force_static = false; 286*b11107cbSLohita Mudimela 287*b11107cbSLohita Mudimela if (mod_power == NULL || stream == NULL) 288*b11107cbSLohita Mudimela return false; 289*b11107cbSLohita Mudimela 290*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 291*b11107cbSLohita Mudimela stream_index = map_index_from_stream(core_power, stream); 292*b11107cbSLohita Mudimela 293*b11107cbSLohita Mudimela if (core_power->num_entities == 0) { 294*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, 295*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 296*b11107cbSLohita Mudimela "mod_power set_psr_event: ERROR: stream=%p event=%d num_entities=%u", 297*b11107cbSLohita Mudimela stream, 298*b11107cbSLohita Mudimela (int)event, 299*b11107cbSLohita Mudimela core_power->num_entities); 300*b11107cbSLohita Mudimela return false; 301*b11107cbSLohita Mudimela } 302*b11107cbSLohita Mudimela 303*b11107cbSLohita Mudimela if (!core_power->map[stream_index].caps->psr_version) 304*b11107cbSLohita Mudimela return false; 305*b11107cbSLohita Mudimela 306*b11107cbSLohita Mudimela if (set_event) 307*b11107cbSLohita Mudimela core_power->map[stream_index].psr_events |= event; 308*b11107cbSLohita Mudimela else 309*b11107cbSLohita Mudimela core_power->map[stream_index].psr_events &= ~event; 310*b11107cbSLohita Mudimela 311*b11107cbSLohita Mudimela active_psr_events = core_power->map[stream_index].psr_events; 312*b11107cbSLohita Mudimela 313*b11107cbSLohita Mudimela // ignore other events when we're in forced psr enabled state 314*b11107cbSLohita Mudimela if (active_psr_events & psr_event_dynamic_display_switch && 315*b11107cbSLohita Mudimela event != psr_event_dynamic_display_switch) 316*b11107cbSLohita Mudimela return false; 317*b11107cbSLohita Mudimela 318*b11107cbSLohita Mudimela // ignore other events when we're in forced psr enabled state 319*b11107cbSLohita Mudimela if (active_psr_events & psr_event_os_override_hold && 320*b11107cbSLohita Mudimela event != psr_event_os_override_hold) 321*b11107cbSLohita Mudimela return false; 322*b11107cbSLohita Mudimela 323*b11107cbSLohita Mudimela // ignore other events when we're in forced psr enabled state 324*b11107cbSLohita Mudimela // dds events need to be processed while in dynamic_link_rate_control 325*b11107cbSLohita Mudimela if (active_psr_events & psr_event_dynamic_link_rate_control && 326*b11107cbSLohita Mudimela event != psr_event_dynamic_link_rate_control && 327*b11107cbSLohita Mudimela event != psr_event_dds_defer_stream_enable && 328*b11107cbSLohita Mudimela event != psr_event_dynamic_display_switch) 329*b11107cbSLohita Mudimela return false; 330*b11107cbSLohita Mudimela 331*b11107cbSLohita Mudimela if (active_psr_events & (psr_event_test_harness_disable_psr | psr_event_os_request_disable)) 332*b11107cbSLohita Mudimela psr_enable_request = false; 333*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_pause) 334*b11107cbSLohita Mudimela psr_enable_request = false; 335*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_test_harness_enable_psr) 336*b11107cbSLohita Mudimela psr_enable_request = true; 337*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_dynamic_display_switch) { 338*b11107cbSLohita Mudimela psr_enable_request = true; 339*b11107cbSLohita Mudimela force_static = true; 340*b11107cbSLohita Mudimela } else if (active_psr_events & psr_event_dynamic_link_rate_control) { 341*b11107cbSLohita Mudimela psr_enable_request = true; 342*b11107cbSLohita Mudimela force_static = true; 343*b11107cbSLohita Mudimela } else if (active_psr_events & psr_event_edp_panel_off_disable_psr) 344*b11107cbSLohita Mudimela psr_enable_request = false; 345*b11107cbSLohita Mudimela else if (active_psr_events & (psr_event_hw_programming | 346*b11107cbSLohita Mudimela psr_event_defer_enable | 347*b11107cbSLohita Mudimela psr_event_dds_defer_stream_enable | 348*b11107cbSLohita Mudimela psr_event_vrr_transition | 349*b11107cbSLohita Mudimela psr_event_immediate_flip)) 350*b11107cbSLohita Mudimela psr_enable_request = false; 351*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_big_screen_video) 352*b11107cbSLohita Mudimela psr_enable_request = true; 353*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_full_screen) 354*b11107cbSLohita Mudimela psr_enable_request = false; 355*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_mpo_video_selective_update) 356*b11107cbSLohita Mudimela psr_enable_request = true; 357*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_vsync) 358*b11107cbSLohita Mudimela psr_enable_request = false; 359*b11107cbSLohita Mudimela else if (active_psr_events & psr_event_crc_window_active) 360*b11107cbSLohita Mudimela psr_enable_request = false; 361*b11107cbSLohita Mudimela else 362*b11107cbSLohita Mudimela psr_enable_request = true; 363*b11107cbSLohita Mudimela 364*b11107cbSLohita Mudimela DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, 365*b11107cbSLohita Mudimela WPP_BIT_FLAG_Firmware_PsrState, 366*b11107cbSLohita Mudimela "mod_power set_psr_event: before: psr_enabled=%d -> request: set_event=%d event=0x%04x -> result: psr_events=0x%04x psr_enable_request=%d", 367*b11107cbSLohita Mudimela (int)core_power->map[stream_index].psr_enabled, 368*b11107cbSLohita Mudimela (int)set_event, 369*b11107cbSLohita Mudimela (unsigned int)event, 370*b11107cbSLohita Mudimela (unsigned int)core_power->map[stream_index].psr_events, 371*b11107cbSLohita Mudimela (int)psr_enable_request); 372*b11107cbSLohita Mudimela mod_power_psr_set_power_opt(mod_power, stream, active_psr_events, psr_enable_request); 373*b11107cbSLohita Mudimela 374*b11107cbSLohita Mudimela if (core_power->map[stream_index].psr_enabled != psr_enable_request || force_static) { 375*b11107cbSLohita Mudimela if (set_psr_enable(mod_power, stream, psr_enable_request, wait, force_static)) 376*b11107cbSLohita Mudimela core_power->map[stream_index].psr_enabled = psr_enable_request; 377*b11107cbSLohita Mudimela } 378*b11107cbSLohita Mudimela 379*b11107cbSLohita Mudimela return true; 380*b11107cbSLohita Mudimela } 381*b11107cbSLohita Mudimela 382*b11107cbSLohita Mudimela bool mod_power_get_psr_state(struct mod_power *mod_power, 383*b11107cbSLohita Mudimela const struct dc_stream_state *stream, 384*b11107cbSLohita Mudimela enum dc_psr_state *state) 385*b11107cbSLohita Mudimela { 386*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 387*b11107cbSLohita Mudimela const struct dc_link *link = NULL; 388*b11107cbSLohita Mudimela 389*b11107cbSLohita Mudimela if (!stream) 390*b11107cbSLohita Mudimela return false; 391*b11107cbSLohita Mudimela 392*b11107cbSLohita Mudimela if (mod_power == NULL) 393*b11107cbSLohita Mudimela return false; 394*b11107cbSLohita Mudimela 395*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 396*b11107cbSLohita Mudimela 397*b11107cbSLohita Mudimela if (core_power->num_entities == 0) 398*b11107cbSLohita Mudimela return false; 399*b11107cbSLohita Mudimela 400*b11107cbSLohita Mudimela link = dc_stream_get_link(stream); 401*b11107cbSLohita Mudimela return dc_link_get_psr_state(link, state); 402*b11107cbSLohita Mudimela } 403*b11107cbSLohita Mudimela 404*b11107cbSLohita Mudimela bool mod_power_get_psr_enabled_status(struct mod_power *mod_power, 405*b11107cbSLohita Mudimela const struct dc_stream_state *stream, 406*b11107cbSLohita Mudimela bool *psr_enabled) 407*b11107cbSLohita Mudimela { 408*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 409*b11107cbSLohita Mudimela unsigned int stream_index = 0; 410*b11107cbSLohita Mudimela 411*b11107cbSLohita Mudimela if (mod_power == NULL) 412*b11107cbSLohita Mudimela return false; 413*b11107cbSLohita Mudimela 414*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 415*b11107cbSLohita Mudimela 416*b11107cbSLohita Mudimela if (core_power->num_entities == 0) 417*b11107cbSLohita Mudimela return false; 418*b11107cbSLohita Mudimela 419*b11107cbSLohita Mudimela stream_index = map_index_from_stream(core_power, stream); 420*b11107cbSLohita Mudimela 421*b11107cbSLohita Mudimela if (!core_power->map[stream_index].caps->psr_version) 422*b11107cbSLohita Mudimela return false; 423*b11107cbSLohita Mudimela 424*b11107cbSLohita Mudimela *psr_enabled = core_power->map[stream_index].psr_enabled; 425*b11107cbSLohita Mudimela 426*b11107cbSLohita Mudimela return true; 427*b11107cbSLohita Mudimela } 428*b11107cbSLohita Mudimela 429*b11107cbSLohita Mudimela void mod_power_psr_residency(struct mod_power *mod_power, 430*b11107cbSLohita Mudimela const struct dc_stream_state *stream, 431*b11107cbSLohita Mudimela unsigned int *residency, 432*b11107cbSLohita Mudimela const uint8_t mode) 433*b11107cbSLohita Mudimela { 434*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 435*b11107cbSLohita Mudimela const struct dc_link *link = NULL; 436*b11107cbSLohita Mudimela 437*b11107cbSLohita Mudimela if (!stream) 438*b11107cbSLohita Mudimela return; 439*b11107cbSLohita Mudimela 440*b11107cbSLohita Mudimela if (mod_power == NULL) 441*b11107cbSLohita Mudimela return; 442*b11107cbSLohita Mudimela 443*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 444*b11107cbSLohita Mudimela 445*b11107cbSLohita Mudimela if (core_power->num_entities == 0) 446*b11107cbSLohita Mudimela return; 447*b11107cbSLohita Mudimela 448*b11107cbSLohita Mudimela link = dc_stream_get_link(stream); 449*b11107cbSLohita Mudimela 450*b11107cbSLohita Mudimela if (link != NULL) 451*b11107cbSLohita Mudimela link->dc->link_srv->edp_get_psr_residency(link, residency, mode); 452*b11107cbSLohita Mudimela } 453*b11107cbSLohita Mudimela bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power, 454*b11107cbSLohita Mudimela const struct dc_stream_state *stream, unsigned int *active_psr_events) 455*b11107cbSLohita Mudimela { 456*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 457*b11107cbSLohita Mudimela unsigned int stream_index = 0; 458*b11107cbSLohita Mudimela 459*b11107cbSLohita Mudimela if (!stream) 460*b11107cbSLohita Mudimela return false; 461*b11107cbSLohita Mudimela 462*b11107cbSLohita Mudimela if (mod_power == NULL) 463*b11107cbSLohita Mudimela return false; 464*b11107cbSLohita Mudimela 465*b11107cbSLohita Mudimela if (active_psr_events == NULL) 466*b11107cbSLohita Mudimela return false; 467*b11107cbSLohita Mudimela 468*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 469*b11107cbSLohita Mudimela 470*b11107cbSLohita Mudimela if (core_power->num_entities == 0) 471*b11107cbSLohita Mudimela return false; 472*b11107cbSLohita Mudimela 473*b11107cbSLohita Mudimela stream_index = map_index_from_stream(core_power, stream); 474*b11107cbSLohita Mudimela 475*b11107cbSLohita Mudimela *active_psr_events = core_power->map[stream_index].psr_events; 476*b11107cbSLohita Mudimela return true; 477*b11107cbSLohita Mudimela } 478*b11107cbSLohita Mudimela 479*b11107cbSLohita Mudimela bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power, 480*b11107cbSLohita Mudimela const struct dc_stream_state *stream, 481*b11107cbSLohita Mudimela uint16_t psr_vtotal_idle, 482*b11107cbSLohita Mudimela uint16_t psr_vtotal_su) 483*b11107cbSLohita Mudimela { 484*b11107cbSLohita Mudimela struct core_power *core_power = NULL; 485*b11107cbSLohita Mudimela unsigned int stream_index = 0; 486*b11107cbSLohita Mudimela const struct dc_link *link = NULL; 487*b11107cbSLohita Mudimela 488*b11107cbSLohita Mudimela if (!stream) 489*b11107cbSLohita Mudimela return false; 490*b11107cbSLohita Mudimela 491*b11107cbSLohita Mudimela if (mod_power == NULL) 492*b11107cbSLohita Mudimela return false; 493*b11107cbSLohita Mudimela 494*b11107cbSLohita Mudimela core_power = MOD_POWER_TO_CORE(mod_power); 495*b11107cbSLohita Mudimela 496*b11107cbSLohita Mudimela if (core_power->num_entities == 0) 497*b11107cbSLohita Mudimela return false; 498*b11107cbSLohita Mudimela 499*b11107cbSLohita Mudimela stream_index = map_index_from_stream(core_power, stream); 500*b11107cbSLohita Mudimela 501*b11107cbSLohita Mudimela if (!core_power->map[stream_index].caps->psr_version) 502*b11107cbSLohita Mudimela return false; 503*b11107cbSLohita Mudimela 504*b11107cbSLohita Mudimela link = dc_stream_get_link(stream); 505*b11107cbSLohita Mudimela 506*b11107cbSLohita Mudimela return link->dc->link_srv->edp_set_sink_vtotal_in_psr_active( 507*b11107cbSLohita Mudimela link, psr_vtotal_idle, psr_vtotal_su); 508*b11107cbSLohita Mudimela } 509*b11107cbSLohita Mudimela /* 510*b11107cbSLohita Mudimela * is_psr_su_specific_panel() - check if sink is AMD vendor-specific PSR-SU 511*b11107cbSLohita Mudimela * supported eDP device. 512*b11107cbSLohita Mudimela * 513*b11107cbSLohita Mudimela * @link: dc link pointer 514*b11107cbSLohita Mudimela * 515*b11107cbSLohita Mudimela * Return: true if AMDGPU vendor specific PSR-SU eDP panel 516*b11107cbSLohita Mudimela */ 517*b11107cbSLohita Mudimela bool is_psr_su_specific_panel(struct dc_link *link) 518*b11107cbSLohita Mudimela { 519*b11107cbSLohita Mudimela bool isPSRSUSupported = false; 520*b11107cbSLohita Mudimela struct dpcd_caps *dpcd_caps = &link->dpcd_caps; 521*b11107cbSLohita Mudimela 522*b11107cbSLohita Mudimela if (dpcd_caps->edp_rev >= DP_EDP_14) { 523*b11107cbSLohita Mudimela if (dpcd_caps->psr_info.psr_version >= DP_PSR2_WITH_Y_COORD_ET_SUPPORTED) 524*b11107cbSLohita Mudimela isPSRSUSupported = true; 525*b11107cbSLohita Mudimela /* 526*b11107cbSLohita Mudimela * Some panels will report PSR capabilities over additional DPCD bits. 527*b11107cbSLohita Mudimela * Such panels are approved despite reporting only PSR v3, as long as 528*b11107cbSLohita Mudimela * the additional bits are reported. 529*b11107cbSLohita Mudimela */ 530*b11107cbSLohita Mudimela if (dpcd_caps->sink_dev_id == DP_BRANCH_DEVICE_ID_001CF8) { 531*b11107cbSLohita Mudimela /* 532*b11107cbSLohita Mudimela * This is the temporary workaround to disable PSRSU when system turned on 533*b11107cbSLohita Mudimela * DSC function on the sepcific sink. 534*b11107cbSLohita Mudimela */ 535*b11107cbSLohita Mudimela if (dpcd_caps->psr_info.psr_version < DP_PSR2_WITH_Y_COORD_IS_SUPPORTED) 536*b11107cbSLohita Mudimela isPSRSUSupported = false; 537*b11107cbSLohita Mudimela else if (dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && 538*b11107cbSLohita Mudimela ((dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x08) || 539*b11107cbSLohita Mudimela (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x07))) 540*b11107cbSLohita Mudimela isPSRSUSupported = false; 541*b11107cbSLohita Mudimela else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x03) 542*b11107cbSLohita Mudimela isPSRSUSupported = false; 543*b11107cbSLohita Mudimela else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x01) 544*b11107cbSLohita Mudimela isPSRSUSupported = false; 545*b11107cbSLohita Mudimela else if (dpcd_caps->psr_info.force_psrsu_cap == 0x1) 546*b11107cbSLohita Mudimela isPSRSUSupported = true; 547*b11107cbSLohita Mudimela } 548*b11107cbSLohita Mudimela } 549*b11107cbSLohita Mudimela 550*b11107cbSLohita Mudimela return isPSRSUSupported; 551*b11107cbSLohita Mudimela } 552*b11107cbSLohita Mudimela 553*b11107cbSLohita Mudimela /** 554*b11107cbSLohita Mudimela * mod_power_calc_psr_configs() - calculate/update generic psr configuration fields. 555*b11107cbSLohita Mudimela * @psr_config: [output], psr configuration structure to be updated 556*b11107cbSLohita Mudimela * @link: [input] dc link pointer 557*b11107cbSLohita Mudimela * @stream: [input] dc stream state pointer 558*b11107cbSLohita Mudimela * 559*b11107cbSLohita Mudimela * calculate and update the psr configuration fields that are not DM specific, i.e. such 560*b11107cbSLohita Mudimela * fields which are based on DPCD caps or timing information. To setup PSR in DMUB FW, 561*b11107cbSLohita Mudimela * this helper is assumed to be called before the call of the DC helper dc_link_setup_psr(). 562*b11107cbSLohita Mudimela * 563*b11107cbSLohita Mudimela * PSR config fields to be updated within the helper: 564*b11107cbSLohita Mudimela * - psr_rfb_setup_time 565*b11107cbSLohita Mudimela * - psr_sdp_transmit_line_num_deadline 566*b11107cbSLohita Mudimela * - line_time_in_us 567*b11107cbSLohita Mudimela * - su_y_granularity 568*b11107cbSLohita Mudimela * - su_granularity_required 569*b11107cbSLohita Mudimela * - psr_frame_capture_indication_req 570*b11107cbSLohita Mudimela * - psr_exit_link_training_required 571*b11107cbSLohita Mudimela * 572*b11107cbSLohita Mudimela * PSR config fields that are DM specific and NOT updated within the helper: 573*b11107cbSLohita Mudimela * - allow_smu_optimizations 574*b11107cbSLohita Mudimela * - allow_multi_disp_optimizations 575*b11107cbSLohita Mudimela */ 576*b11107cbSLohita Mudimela void mod_power_calc_psr_configs(struct psr_config *psr_config, 577*b11107cbSLohita Mudimela struct dc_link *link, 578*b11107cbSLohita Mudimela const struct dc_stream_state *stream) 579*b11107cbSLohita Mudimela { 580*b11107cbSLohita Mudimela unsigned int num_vblank_lines = 0; 581*b11107cbSLohita Mudimela unsigned int vblank_time_in_us = 0; 582*b11107cbSLohita Mudimela unsigned int sdp_tx_deadline_in_us = 0; 583*b11107cbSLohita Mudimela unsigned int line_time_in_us = 0; 584*b11107cbSLohita Mudimela struct dpcd_caps *dpcd_caps = &link->dpcd_caps; 585*b11107cbSLohita Mudimela const int psr_setup_time_step_in_us = 55; /* refer to eDP spec DPCD 0x071h */ 586*b11107cbSLohita Mudimela 587*b11107cbSLohita Mudimela /* timing parameters */ 588*b11107cbSLohita Mudimela num_vblank_lines = stream->timing.v_total - 589*b11107cbSLohita Mudimela stream->timing.v_addressable - 590*b11107cbSLohita Mudimela stream->timing.v_border_top - 591*b11107cbSLohita Mudimela stream->timing.v_border_bottom; 592*b11107cbSLohita Mudimela 593*b11107cbSLohita Mudimela vblank_time_in_us = (stream->timing.h_total * num_vblank_lines * 1000) / (stream->timing.pix_clk_100hz / 10); 594*b11107cbSLohita Mudimela 595*b11107cbSLohita Mudimela line_time_in_us = ((stream->timing.h_total * 1000) / (stream->timing.pix_clk_100hz / 10)) + 1; 596*b11107cbSLohita Mudimela 597*b11107cbSLohita Mudimela /** 598*b11107cbSLohita Mudimela * psr configuration fields 599*b11107cbSLohita Mudimela * 600*b11107cbSLohita Mudimela * as per eDP 1.5 pg. 377 of 459, DPCD 0x071h bits [3:1], psr setup time bits interpreted as below 601*b11107cbSLohita Mudimela * 000b <--> 330 us (default) 602*b11107cbSLohita Mudimela * 001b <--> 275 us 603*b11107cbSLohita Mudimela * 010b <--> 220 us 604*b11107cbSLohita Mudimela * 011b <--> 165 us 605*b11107cbSLohita Mudimela * 100b <--> 110 us 606*b11107cbSLohita Mudimela * 101b <--> 055 us 607*b11107cbSLohita Mudimela * 110b <--> 000 us 608*b11107cbSLohita Mudimela */ 609*b11107cbSLohita Mudimela psr_config->psr_rfb_setup_time = 610*b11107cbSLohita Mudimela (6 - dpcd_caps->psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME) * psr_setup_time_step_in_us; 611*b11107cbSLohita Mudimela 612*b11107cbSLohita Mudimela if (psr_config->psr_rfb_setup_time > vblank_time_in_us) { 613*b11107cbSLohita Mudimela link->psr_settings.psr_frame_capture_indication_req = true; 614*b11107cbSLohita Mudimela link->psr_settings.psr_sdp_transmit_line_num_deadline = num_vblank_lines; 615*b11107cbSLohita Mudimela } else { 616*b11107cbSLohita Mudimela sdp_tx_deadline_in_us = vblank_time_in_us - psr_config->psr_rfb_setup_time; 617*b11107cbSLohita Mudimela 618*b11107cbSLohita Mudimela /* Set the last possible line SDP may be transmitted without violating the RFB setup time */ 619*b11107cbSLohita Mudimela link->psr_settings.psr_frame_capture_indication_req = false; 620*b11107cbSLohita Mudimela link->psr_settings.psr_sdp_transmit_line_num_deadline = sdp_tx_deadline_in_us / line_time_in_us; 621*b11107cbSLohita Mudimela } 622*b11107cbSLohita Mudimela 623*b11107cbSLohita Mudimela psr_config->psr_sdp_transmit_line_num_deadline = link->psr_settings.psr_sdp_transmit_line_num_deadline; 624*b11107cbSLohita Mudimela psr_config->line_time_in_us = line_time_in_us; 625*b11107cbSLohita Mudimela psr_config->su_y_granularity = dpcd_caps->psr_info.psr2_su_y_granularity_cap; 626*b11107cbSLohita Mudimela psr_config->su_granularity_required = dpcd_caps->psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED; 627*b11107cbSLohita Mudimela psr_config->psr_frame_capture_indication_req = link->psr_settings.psr_frame_capture_indication_req; 628*b11107cbSLohita Mudimela psr_config->psr_exit_link_training_required = 629*b11107cbSLohita Mudimela !link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED; 630*b11107cbSLohita Mudimela } 631*b11107cbSLohita Mudimela 632*b11107cbSLohita Mudimela bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link, 633*b11107cbSLohita Mudimela struct dc_stream_state *stream, 634*b11107cbSLohita Mudimela struct psr_config *config) 635*b11107cbSLohita Mudimela { 636*b11107cbSLohita Mudimela uint32_t pic_height; 637*b11107cbSLohita Mudimela uint32_t slice_height; 638*b11107cbSLohita Mudimela 639*b11107cbSLohita Mudimela config->dsc_slice_height = 0; 640*b11107cbSLohita Mudimela if (!(link->connector_signal & SIGNAL_TYPE_EDP) || 641*b11107cbSLohita Mudimela !dc->caps.edp_dsc_support || 642*b11107cbSLohita Mudimela link->panel_config.dsc.disable_dsc_edp || 643*b11107cbSLohita Mudimela !link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT || 644*b11107cbSLohita Mudimela !stream->timing.dsc_cfg.num_slices_v) 645*b11107cbSLohita Mudimela return true; 646*b11107cbSLohita Mudimela 647*b11107cbSLohita Mudimela pic_height = stream->timing.v_addressable + 648*b11107cbSLohita Mudimela stream->timing.v_border_top + stream->timing.v_border_bottom; 649*b11107cbSLohita Mudimela 650*b11107cbSLohita Mudimela if (stream->timing.dsc_cfg.num_slices_v == 0) 651*b11107cbSLohita Mudimela return false; 652*b11107cbSLohita Mudimela 653*b11107cbSLohita Mudimela slice_height = pic_height / stream->timing.dsc_cfg.num_slices_v; 654*b11107cbSLohita Mudimela config->dsc_slice_height = (uint16_t)slice_height; 655*b11107cbSLohita Mudimela 656*b11107cbSLohita Mudimela if (slice_height) { 657*b11107cbSLohita Mudimela if (config->su_y_granularity && 658*b11107cbSLohita Mudimela (slice_height % config->su_y_granularity)) { 659*b11107cbSLohita Mudimela ASSERT(0); 660*b11107cbSLohita Mudimela return false; 661*b11107cbSLohita Mudimela } 662*b11107cbSLohita Mudimela } 663*b11107cbSLohita Mudimela 664*b11107cbSLohita Mudimela return true; 665*b11107cbSLohita Mudimela } 666