dcn315_smu.c (11870B)
1/* 2 * Copyright 2021 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26#include "core_types.h" 27#include "clk_mgr_internal.h" 28#include "reg_helper.h" 29#include "dm_helpers.h" 30#include "dcn315_smu.h" 31#include "mp/mp_13_0_5_offset.h" 32 33#define MAX_INSTANCE 6 34#define MAX_SEGMENT 6 35 36struct IP_BASE_INSTANCE 37{ 38 unsigned int segment[MAX_SEGMENT]; 39}; 40 41struct IP_BASE 42{ 43 struct IP_BASE_INSTANCE instance[MAX_INSTANCE]; 44}; 45 46static const struct IP_BASE MP0_BASE = { { { { 0x00016000, 0x00DC0000, 0x00E00000, 0x00E40000, 0x0243FC00, 0 } }, 47 { { 0, 0, 0, 0, 0, 0 } }, 48 { { 0, 0, 0, 0, 0, 0 } }, 49 { { 0, 0, 0, 0, 0, 0 } }, 50 { { 0, 0, 0, 0, 0, 0 } }, 51 { { 0, 0, 0, 0, 0, 0 } } } }; 52static const struct IP_BASE NBIO_BASE = { { { { 0x00000000, 0x00000014, 0x00000D20, 0x00010400, 0x0241B000, 0x04040000 } }, 53 { { 0, 0, 0, 0, 0, 0 } }, 54 { { 0, 0, 0, 0, 0, 0 } }, 55 { { 0, 0, 0, 0, 0, 0 } }, 56 { { 0, 0, 0, 0, 0, 0 } }, 57 { { 0, 0, 0, 0, 0, 0 } } } }; 58 59#define regBIF_BX_PF2_RSMU_INDEX 0x0000 60#define regBIF_BX_PF2_RSMU_INDEX_BASE_IDX 1 61#define regBIF_BX_PF2_RSMU_DATA 0x0001 62#define regBIF_BX_PF2_RSMU_DATA_BASE_IDX 1 63 64#define REG(reg_name) \ 65 (MP0_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name) 66 67#define FN(reg_name, field) \ 68 FD(reg_name##__##field) 69 70#define REG_NBIO(reg_name) \ 71 (NBIO_BASE.instance[0].segment[regBIF_BX_PF2_ ## reg_name ## _BASE_IDX] + regBIF_BX_PF2_ ## reg_name) 72 73#define mmMP1_C2PMSG_3 0x3B1050C 74 75#define VBIOSSMC_MSG_TestMessage 0x01 ///< To check if PMFW is alive and responding. Requirement specified by PMFW team 76#define VBIOSSMC_MSG_GetPmfwVersion 0x02 ///< Get PMFW version 77#define VBIOSSMC_MSG_Spare0 0x03 ///< Spare0 78#define VBIOSSMC_MSG_SetDispclkFreq 0x04 ///< Set display clock frequency in MHZ 79#define VBIOSSMC_MSG_Spare1 0x05 ///< Spare1 80#define VBIOSSMC_MSG_SetDppclkFreq 0x06 ///< Set DPP clock frequency in MHZ 81#define VBIOSSMC_MSG_SetHardMinDcfclkByFreq 0x07 ///< Set DCF clock frequency hard min in MHZ 82#define VBIOSSMC_MSG_SetMinDeepSleepDcfclk 0x08 ///< Set DCF clock minimum frequency in deep sleep in MHZ 83#define VBIOSSMC_MSG_GetDtbclkFreq 0x09 ///< Get display dtb clock frequency in MHZ in case VMIN does not support phy frequency 84#define VBIOSSMC_MSG_SetDtbClk 0x0A ///< Set dtb clock frequency, return frequemcy in MHZ 85#define VBIOSSMC_MSG_SetDisplayCount 0x0B ///< Inform PMFW of number of display connected 86#define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0x0C ///< To ask PMFW turn off TMDP 48MHz refclk during display off to save power 87#define VBIOSSMC_MSG_UpdatePmeRestore 0x0D ///< To ask PMFW to write into Azalia for PME wake up event 88#define VBIOSSMC_MSG_SetVbiosDramAddrHigh 0x0E ///< Set DRAM address high 32 bits for WM table transfer 89#define VBIOSSMC_MSG_SetVbiosDramAddrLow 0x0F ///< Set DRAM address low 32 bits for WM table transfer 90#define VBIOSSMC_MSG_TransferTableSmu2Dram 0x10 ///< Transfer table from PMFW SRAM to system DRAM 91#define VBIOSSMC_MSG_TransferTableDram2Smu 0x11 ///< Transfer table from system DRAM to PMFW 92#define VBIOSSMC_MSG_SetDisplayIdleOptimizations 0x12 ///< Set Idle state optimization for display off 93#define VBIOSSMC_MSG_GetDprefclkFreq 0x13 ///< Get DPREF clock frequency. Return in MHZ 94#define VBIOSSMC_Message_Count 0x14 ///< Total number of VBIS and DAL messages 95 96#define VBIOSSMC_Status_BUSY 0x0 97#define VBIOSSMC_Result_OK 0x01 ///< Message Response OK 98#define VBIOSSMC_Result_Failed 0xFF ///< Message Response Failed 99#define VBIOSSMC_Result_UnknownCmd 0xFE ///< Message Response Unknown Command 100#define VBIOSSMC_Result_CmdRejectedPrereq 0xFD ///< Message Response Command Failed Prerequisite 101#define VBIOSSMC_Result_CmdRejectedBusy 0xFC ///< Message Response Command Rejected due to PMFW is busy. Sender should retry sending this message 102 103/* 104 * Function to be used instead of REG_WAIT macro because the wait ends when 105 * the register is NOT EQUAL to zero, and because the translation in msg_if.h 106 * won't work with REG_WAIT. 107 */ 108static uint32_t dcn315_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) 109{ 110 uint32_t res_val = VBIOSSMC_Status_BUSY; 111 112 do { 113 res_val = REG_READ(MP1_SMN_C2PMSG_38); 114 if (res_val != VBIOSSMC_Status_BUSY) 115 break; 116 117 if (delay_us >= 1000) 118 msleep(delay_us/1000); 119 else if (delay_us > 0) 120 udelay(delay_us); 121 } while (max_retries--); 122 123 return res_val; 124} 125 126static int dcn315_smu_send_msg_with_param( 127 struct clk_mgr_internal *clk_mgr, 128 unsigned int msg_id, unsigned int param) 129{ 130 uint32_t result; 131 132 result = dcn315_smu_wait_for_response(clk_mgr, 10, 200000); 133 ASSERT(result == VBIOSSMC_Result_OK); 134 135 if (result == VBIOSSMC_Status_BUSY) { 136 return -1; 137 } 138 139 /* First clear response register */ 140 REG_WRITE(MP1_SMN_C2PMSG_38, VBIOSSMC_Status_BUSY); 141 142 /* Set the parameter register for the SMU message, unit is Mhz */ 143 REG_WRITE(MP1_SMN_C2PMSG_37, param); 144 145 /* Trigger the message transaction by writing the message ID */ 146 generic_write_indirect_reg(CTX, 147 REG_NBIO(RSMU_INDEX), REG_NBIO(RSMU_DATA), 148 mmMP1_C2PMSG_3, msg_id); 149 150 result = dcn315_smu_wait_for_response(clk_mgr, 10, 200000); 151 152 if (result == VBIOSSMC_Status_BUSY) { 153 ASSERT(0); 154 dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000); 155 } 156 157 return REG_READ(MP1_SMN_C2PMSG_37); 158} 159 160int dcn315_smu_get_smu_version(struct clk_mgr_internal *clk_mgr) 161{ 162 return dcn315_smu_send_msg_with_param( 163 clk_mgr, 164 VBIOSSMC_MSG_GetPmfwVersion, 165 0); 166} 167 168 169int dcn315_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz) 170{ 171 int actual_dispclk_set_mhz = -1; 172 173 if (!clk_mgr->smu_present) 174 return requested_dispclk_khz; 175 176 /* Unit of SMU msg parameter is Mhz */ 177 actual_dispclk_set_mhz = dcn315_smu_send_msg_with_param( 178 clk_mgr, 179 VBIOSSMC_MSG_SetDispclkFreq, 180 khz_to_mhz_ceil(requested_dispclk_khz)); 181 182 return actual_dispclk_set_mhz * 1000; 183} 184 185int dcn315_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz) 186{ 187 int actual_dcfclk_set_mhz = -1; 188 189 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 190 return -1; 191 192 if (!clk_mgr->smu_present) 193 return requested_dcfclk_khz; 194 195 actual_dcfclk_set_mhz = dcn315_smu_send_msg_with_param( 196 clk_mgr, 197 VBIOSSMC_MSG_SetHardMinDcfclkByFreq, 198 khz_to_mhz_ceil(requested_dcfclk_khz)); 199 200 return actual_dcfclk_set_mhz * 1000; 201} 202 203int dcn315_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz) 204{ 205 int actual_min_ds_dcfclk_mhz = -1; 206 207 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 208 return -1; 209 210 if (!clk_mgr->smu_present) 211 return requested_min_ds_dcfclk_khz; 212 213 actual_min_ds_dcfclk_mhz = dcn315_smu_send_msg_with_param( 214 clk_mgr, 215 VBIOSSMC_MSG_SetMinDeepSleepDcfclk, 216 khz_to_mhz_ceil(requested_min_ds_dcfclk_khz)); 217 218 return actual_min_ds_dcfclk_mhz * 1000; 219} 220 221int dcn315_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz) 222{ 223 int actual_dppclk_set_mhz = -1; 224 225 if (!clk_mgr->smu_present) 226 return requested_dpp_khz; 227 228 actual_dppclk_set_mhz = dcn315_smu_send_msg_with_param( 229 clk_mgr, 230 VBIOSSMC_MSG_SetDppclkFreq, 231 khz_to_mhz_ceil(requested_dpp_khz)); 232 233 return actual_dppclk_set_mhz * 1000; 234} 235 236void dcn315_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info) 237{ 238 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 239 return; 240 241 if (!clk_mgr->smu_present) 242 return; 243 244 //TODO: Work with smu team to define optimization options. 245 dcn315_smu_send_msg_with_param( 246 clk_mgr, 247 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 248 idle_info); 249} 250 251void dcn315_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable) 252{ 253 union display_idle_optimization_u idle_info = { 0 }; 254 255 if (!clk_mgr->smu_present) 256 return; 257 258 if (enable) { 259 idle_info.idle_info.df_request_disabled = 1; 260 idle_info.idle_info.phy_ref_clk_off = 1; 261 } 262 263 dcn315_smu_send_msg_with_param( 264 clk_mgr, 265 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 266 idle_info.data); 267} 268 269void dcn315_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr) 270{ 271 if (!clk_mgr->smu_present) 272 return; 273 274 dcn315_smu_send_msg_with_param( 275 clk_mgr, 276 VBIOSSMC_MSG_UpdatePmeRestore, 277 0); 278} 279void dcn315_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high) 280{ 281 if (!clk_mgr->smu_present) 282 return; 283 284 dcn315_smu_send_msg_with_param(clk_mgr, 285 VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high); 286} 287 288void dcn315_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low) 289{ 290 if (!clk_mgr->smu_present) 291 return; 292 293 dcn315_smu_send_msg_with_param(clk_mgr, 294 VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low); 295} 296 297void dcn315_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr) 298{ 299 if (!clk_mgr->smu_present) 300 return; 301 302 dcn315_smu_send_msg_with_param(clk_mgr, 303 VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS); 304} 305 306void dcn315_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) 307{ 308 if (!clk_mgr->smu_present) 309 return; 310 311 dcn315_smu_send_msg_with_param(clk_mgr, 312 VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS); 313} 314 315int dcn315_smu_get_dpref_clk(struct clk_mgr_internal *clk_mgr) 316{ 317 int dprefclk_get_mhz = -1; 318 if (clk_mgr->smu_present) { 319 dprefclk_get_mhz = dcn315_smu_send_msg_with_param( 320 clk_mgr, 321 VBIOSSMC_MSG_GetDprefclkFreq, 322 0); 323 } 324 return (dprefclk_get_mhz * 1000); 325} 326 327int dcn315_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr) 328{ 329 int fclk_get_mhz = -1; 330 331 if (clk_mgr->smu_present) { 332 fclk_get_mhz = dcn315_smu_send_msg_with_param( 333 clk_mgr, 334 VBIOSSMC_MSG_GetDtbclkFreq, 335 0); 336 } 337 return (fclk_get_mhz * 1000); 338} 339 340void dcn315_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable) 341{ 342 if (!clk_mgr->smu_present) 343 return; 344 345 dcn315_smu_send_msg_with_param( 346 clk_mgr, 347 VBIOSSMC_MSG_SetDtbClk, 348 enable); 349}