diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32')
3 files changed, 226 insertions, 3 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dalsmc.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dalsmc.h index c427be6ad..724a508b0 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dalsmc.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dalsmc.h @@ -55,7 +55,16 @@ #define DALSMC_MSG_SetFclkSwitchAllow 0x11 #define DALSMC_MSG_SetCabForUclkPstate 0x12 #define DALSMC_MSG_SetWorstCaseUclkLatency 0x13 -#define DALSMC_Message_Count 0x14 +#define DALSMC_MSG_SetAlwaysWaitDmcubResp 0x14 +#define DALSMC_MSG_ReturnHardMinStatus 0x15 +#define DALSMC_Message_Count 0x16 + +#define CHECK_HARD_MIN_CLK_DISPCLK 0x1 +#define CHECK_HARD_MIN_CLK_DPPCLK 0x2 +#define CHECK_HARD_MIN_CLK_DPREFCLK 0x4 +#define CHECK_HARD_MIN_CLK_DCFCLK 0x8 +#define CHECK_HARD_MIN_CLK_DTBCLK 0x10 +#define CHECK_HARD_MIN_CLK_UCLK 0x20 typedef enum { FCLK_SWITCH_DISALLOW, diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index e9345f655..a496930b1 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -53,6 +53,14 @@ #define mmCLK1_CLK3_DFS_CNTL 0x16E72 #define mmCLK1_CLK4_DFS_CNTL 0x16E75 +#define mmCLK1_CLK0_CURRENT_CNT 0x16EE7 +#define mmCLK1_CLK1_CURRENT_CNT 0x16EE8 +#define mmCLK1_CLK2_CURRENT_CNT 0x16EE9 +#define mmCLK1_CLK3_CURRENT_CNT 0x16EEA +#define mmCLK1_CLK4_CURRENT_CNT 0x16EEB + +#define mmCLK4_CLK0_CURRENT_CNT 0x1B0C9 + #define CLK1_CLK_PLL_REQ__FbMult_int_MASK 0x000001ffUL #define CLK1_CLK_PLL_REQ__PllSpineDiv_MASK 0x0000f000UL #define CLK1_CLK_PLL_REQ__FbMult_frac_MASK 0xffff0000UL @@ -450,6 +458,58 @@ static int dcn32_get_dispclk_from_dentist(struct clk_mgr *clk_mgr_base) return 0; } +static void dcn32_auto_dpm_test_log(struct dc_clocks *new_clocks, struct clk_mgr_internal *clk_mgr) +{ + unsigned int dispclk_khz_reg = REG_READ(CLK1_CLK0_CURRENT_CNT); // DISPCLK + unsigned int dppclk_khz_reg = REG_READ(CLK1_CLK1_CURRENT_CNT); // DPPCLK + unsigned int dprefclk_khz_reg = REG_READ(CLK1_CLK2_CURRENT_CNT); // DPREFCLK + unsigned int dcfclk_khz_reg = REG_READ(CLK1_CLK3_CURRENT_CNT); // DCFCLK + unsigned int dtbclk_khz_reg = REG_READ(CLK1_CLK4_CURRENT_CNT); // DTBCLK + unsigned int fclk_khz_reg = REG_READ(CLK4_CLK0_CURRENT_CNT); // FCLK + + // Overrides for these clocks in case there is no p_state change support + int dramclk_khz_override = new_clocks->dramclk_khz; + int fclk_khz_override = new_clocks->fclk_khz; + + int num_fclk_levels = clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_fclk_levels - 1; + + if (!new_clocks->p_state_change_support) { + dramclk_khz_override = clk_mgr->base.bw_params->max_memclk_mhz * 1000; + } + if (!new_clocks->fclk_p_state_change_support) { + fclk_khz_override = clk_mgr->base.bw_params->clk_table.entries[num_fclk_levels].fclk_mhz * 1000; + } + + //////////////////////////////////////////////////////////////////////////// + // IMPORTANT: When adding more clocks to these logs, do NOT put a newline + // anywhere other than at the very end of the string. + // + // Formatting example (make sure to have " - " between each entry): + // + // AutoDPMTest: clk1:%d - clk2:%d - clk3:%d - clk4:%d\n" + //////////////////////////////////////////////////////////////////////////// + if (new_clocks && + new_clocks->dramclk_khz > 0 && + new_clocks->fclk_khz > 0 && + new_clocks->dcfclk_khz > 0 && + new_clocks->dppclk_khz > 0) { + + DC_LOG_AUTO_DPM_TEST("AutoDPMTest: dramclk:%d - fclk:%d - " + "dcfclk:%d - dppclk:%d - dispclk_hw:%d - " + "dppclk_hw:%d - dprefclk_hw:%d - dcfclk_hw:%d - " + "dtbclk_hw:%d - fclk_hw:%d\n", + dramclk_khz_override, + fclk_khz_override, + new_clocks->dcfclk_khz, + new_clocks->dppclk_khz, + dispclk_khz_reg, + dppclk_khz_reg, + dprefclk_khz_reg, + dcfclk_khz_reg, + dtbclk_khz_reg, + fclk_khz_reg); + } +} static void dcn32_update_clocks(struct clk_mgr *clk_mgr_base, struct dc_state *context, @@ -646,6 +706,10 @@ static void dcn32_update_clocks(struct clk_mgr *clk_mgr_base, /*update dmcu for wait_loop count*/ dmcu->funcs->set_psr_wait_loop(dmcu, clk_mgr_base->clks.dispclk_khz / 1000 / 7); + + if (dc->config.enable_auto_dpm_test_logs) { + dcn32_auto_dpm_test_log(new_clocks, clk_mgr); + } } static uint32_t dcn32_get_vco_frequency_from_reg(struct clk_mgr_internal *clk_mgr) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr_smu_msg.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr_smu_msg.c index 700ce4203..df244b175 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr_smu_msg.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr_smu_msg.c @@ -90,6 +90,64 @@ static bool dcn32_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr, uint return false; } +/* + * Use these functions to return back delay information so we can aggregate the total + * delay when requesting hardmin clk + * + * dcn32_smu_wait_for_response_delay + * dcn32_smu_send_msg_with_param_delay + * + */ +static uint32_t dcn32_smu_wait_for_response_delay(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries, unsigned int *total_delay_us) +{ + uint32_t reg = 0; + *total_delay_us = 0; + + do { + reg = REG_READ(DAL_RESP_REG); + if (reg) + break; + + if (delay_us >= 1000) + msleep(delay_us/1000); + else if (delay_us > 0) + udelay(delay_us); + *total_delay_us += delay_us; + } while (max_retries--); + + return reg; +} + +static bool dcn32_smu_send_msg_with_param_delay(struct clk_mgr_internal *clk_mgr, uint32_t msg_id, uint32_t param_in, uint32_t *param_out, unsigned int *total_delay_us) +{ + unsigned int delay1_us, delay2_us; + *total_delay_us = 0; + + /* Wait for response register to be ready */ + dcn32_smu_wait_for_response_delay(clk_mgr, 10, 200000, &delay1_us); + + /* Clear response register */ + REG_WRITE(DAL_RESP_REG, 0); + + /* Set the parameter register for the SMU message */ + REG_WRITE(DAL_ARG_REG, param_in); + + /* Trigger the message transaction by writing the message ID */ + REG_WRITE(DAL_MSG_REG, msg_id); + + /* Wait for response */ + if (dcn32_smu_wait_for_response_delay(clk_mgr, 10, 200000, &delay2_us) == DALSMC_Result_OK) { + if (param_out) + *param_out = REG_READ(DAL_ARG_REG); + + *total_delay_us = delay1_us + delay2_us; + return true; + } + + *total_delay_us = delay1_us + 2000000; + return false; +} + void dcn32_smu_send_fclk_pstate_message(struct clk_mgr_internal *clk_mgr, bool enable) { smu_print("FCLK P-state support value is : %d\n", enable); @@ -122,10 +180,98 @@ void dcn32_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr) DALSMC_MSG_BacoAudioD3PME, 0, NULL); } +/* Check PMFW version if it supports ReturnHardMinStatus message */ +static bool dcn32_get_hard_min_status_supported(struct clk_mgr_internal *clk_mgr) +{ + if (ASICREV_IS_GC_11_0_0(clk_mgr->base.ctx->asic_id.hw_internal_rev)) { + if (clk_mgr->smu_ver >= 0x4e6a00) + return true; + } else if (ASICREV_IS_GC_11_0_2(clk_mgr->base.ctx->asic_id.hw_internal_rev)) { + if (clk_mgr->smu_ver >= 0x524e00) + return true; + } else { /* ASICREV_IS_GC_11_0_3 */ + if (clk_mgr->smu_ver >= 0x503900) + return true; + } + return false; +} + +/* Returns the clocks which were fulfilled by the DAL hard min arbiter in PMFW */ +static unsigned int dcn32_smu_get_hard_min_status(struct clk_mgr_internal *clk_mgr, bool *no_timeout, unsigned int *total_delay_us) +{ + uint32_t response = 0; + + /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ + uint32_t param = 0; + + *no_timeout = dcn32_smu_send_msg_with_param_delay(clk_mgr, + DALSMC_MSG_ReturnHardMinStatus, param, &response, total_delay_us); + + smu_print("SMU Get hard min status: no_timeout %d delay %d us clk bits %x\n", + *no_timeout, *total_delay_us, response); + + return response; +} + +static bool dcn32_smu_wait_get_hard_min_status(struct clk_mgr_internal *clk_mgr, + uint32_t clk) +{ + int readDalHardMinClkBits, checkDalHardMinClkBits; + unsigned int total_delay_us, read_total_delay_us; + bool no_timeout, hard_min_done; + + static unsigned int cur_wait_get_hard_min_max_us; + static unsigned int cur_wait_get_hard_min_max_timeouts; + + checkDalHardMinClkBits = CHECK_HARD_MIN_CLK_DPREFCLK; + if (clk == PPCLK_DISPCLK) + checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DISPCLK; + if (clk == PPCLK_DPPCLK) + checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DPPCLK; + if (clk == PPCLK_DCFCLK) + checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DCFCLK; + if (clk == PPCLK_DTBCLK) + checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_DTBCLK; + if (clk == PPCLK_UCLK) + checkDalHardMinClkBits |= CHECK_HARD_MIN_CLK_UCLK; + + if (checkDalHardMinClkBits == CHECK_HARD_MIN_CLK_DPREFCLK) + return 0; + + total_delay_us = 0; + hard_min_done = false; + while (1) { + readDalHardMinClkBits = dcn32_smu_get_hard_min_status(clk_mgr, &no_timeout, &read_total_delay_us); + total_delay_us += read_total_delay_us; + if (checkDalHardMinClkBits == (readDalHardMinClkBits & checkDalHardMinClkBits)) { + hard_min_done = true; + break; + } + + + if (total_delay_us >= 2000000) { + cur_wait_get_hard_min_max_timeouts++; + smu_print("SMU Wait get hard min status: %d timeouts\n", cur_wait_get_hard_min_max_timeouts); + break; + } + msleep(1); + total_delay_us += 1000; + } + + if (total_delay_us > cur_wait_get_hard_min_max_us) + cur_wait_get_hard_min_max_us = total_delay_us; + + smu_print("SMU Wait get hard min status: no_timeout %d, delay %d us, max %d us, read %x, check %x\n", + no_timeout, total_delay_us, cur_wait_get_hard_min_max_us, readDalHardMinClkBits, checkDalHardMinClkBits); + + return hard_min_done; +} + /* Returns the actual frequency that was set in MHz, 0 on failure */ unsigned int dcn32_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz) { uint32_t response = 0; + bool hard_min_done = false; /* bits 23:16 for clock type, lower 16 bits for frequency in MHz */ uint32_t param = (clk << 16) | freq_mhz; @@ -133,9 +279,13 @@ unsigned int dcn32_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, ui smu_print("SMU Set hard min by freq: clk = %d, freq_mhz = %d MHz\n", clk, freq_mhz); dcn32_smu_send_msg_with_param(clk_mgr, - DALSMC_MSG_SetHardMinByFreq, param, &response); + DALSMC_MSG_SetHardMinByFreq, param, &response); - smu_print("SMU Frequency set = %d KHz\n", response); + if (dcn32_get_hard_min_status_supported(clk_mgr)) { + hard_min_done = dcn32_smu_wait_get_hard_min_status(clk_mgr, clk); + smu_print("SMU Frequency set = %d KHz hard_min_done %d\n", response, hard_min_done); + } else + smu_print("SMU Frequency set = %d KHz\n", response); return response; } |