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 36 struct IP_BASE_INSTANCE 37 { 38 unsigned int segment[MAX_SEGMENT]; 39 }; 40 41 struct IP_BASE 42 { 43 struct IP_BASE_INSTANCE instance[MAX_INSTANCE]; 44 }; 45 46 static 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 } } } }; 52 static 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 #include "logger_types.h" 74 #undef DC_LOGGER 75 #define DC_LOGGER \ 76 CTX->logger 77 #define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); } 78 79 #define mmMP1_C2PMSG_3 0x3B1050C 80 81 #define VBIOSSMC_MSG_TestMessage 0x01 ///< To check if PMFW is alive and responding. Requirement specified by PMFW team 82 #define VBIOSSMC_MSG_GetPmfwVersion 0x02 ///< Get PMFW version 83 #define VBIOSSMC_MSG_Spare0 0x03 ///< Spare0 84 #define VBIOSSMC_MSG_SetDispclkFreq 0x04 ///< Set display clock frequency in MHZ 85 #define VBIOSSMC_MSG_Spare1 0x05 ///< Spare1 86 #define VBIOSSMC_MSG_SetDppclkFreq 0x06 ///< Set DPP clock frequency in MHZ 87 #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq 0x07 ///< Set DCF clock frequency hard min in MHZ 88 #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk 0x08 ///< Set DCF clock minimum frequency in deep sleep in MHZ 89 #define VBIOSSMC_MSG_GetDtbclkFreq 0x09 ///< Get display dtb clock frequency in MHZ in case VMIN does not support phy frequency 90 #define VBIOSSMC_MSG_SetDtbClk 0x0A ///< Set dtb clock frequency, return frequemcy in MHZ 91 #define VBIOSSMC_MSG_SetDisplayCount 0x0B ///< Inform PMFW of number of display connected 92 #define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0x0C ///< To ask PMFW turn off TMDP 48MHz refclk during display off to save power 93 #define VBIOSSMC_MSG_UpdatePmeRestore 0x0D ///< To ask PMFW to write into Azalia for PME wake up event 94 #define VBIOSSMC_MSG_SetVbiosDramAddrHigh 0x0E ///< Set DRAM address high 32 bits for WM table transfer 95 #define VBIOSSMC_MSG_SetVbiosDramAddrLow 0x0F ///< Set DRAM address low 32 bits for WM table transfer 96 #define VBIOSSMC_MSG_TransferTableSmu2Dram 0x10 ///< Transfer table from PMFW SRAM to system DRAM 97 #define VBIOSSMC_MSG_TransferTableDram2Smu 0x11 ///< Transfer table from system DRAM to PMFW 98 #define VBIOSSMC_MSG_SetDisplayIdleOptimizations 0x12 ///< Set Idle state optimization for display off 99 #define VBIOSSMC_MSG_GetDprefclkFreq 0x13 ///< Get DPREF clock frequency. Return in MHZ 100 #define VBIOSSMC_Message_Count 0x14 ///< Total number of VBIS and DAL messages 101 102 #define VBIOSSMC_Status_BUSY 0x0 103 #define VBIOSSMC_Result_OK 0x01 ///< Message Response OK 104 #define VBIOSSMC_Result_Failed 0xFF ///< Message Response Failed 105 #define VBIOSSMC_Result_UnknownCmd 0xFE ///< Message Response Unknown Command 106 #define VBIOSSMC_Result_CmdRejectedPrereq 0xFD ///< Message Response Command Failed Prerequisite 107 #define VBIOSSMC_Result_CmdRejectedBusy 0xFC ///< Message Response Command Rejected due to PMFW is busy. Sender should retry sending this message 108 109 /* 110 * Function to be used instead of REG_WAIT macro because the wait ends when 111 * the register is NOT EQUAL to zero, and because the translation in msg_if.h 112 * won't work with REG_WAIT. 113 */ 114 static uint32_t dcn315_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) 115 { 116 uint32_t res_val = VBIOSSMC_Status_BUSY; 117 118 do { 119 res_val = REG_READ(MP1_SMN_C2PMSG_38); 120 if (res_val != VBIOSSMC_Status_BUSY) 121 break; 122 123 if (delay_us >= 1000) 124 msleep(delay_us/1000); 125 else if (delay_us > 0) 126 udelay(delay_us); 127 } while (max_retries--); 128 129 return res_val; 130 } 131 132 static int dcn315_smu_send_msg_with_param( 133 struct clk_mgr_internal *clk_mgr, 134 unsigned int msg_id, unsigned int param) 135 { 136 uint32_t result; 137 138 result = dcn315_smu_wait_for_response(clk_mgr, 10, 200000); 139 140 if (result != VBIOSSMC_Result_OK) 141 smu_print("SMU Response was not OK. SMU response after wait received is: %d\n", result); 142 143 if (result == VBIOSSMC_Status_BUSY) { 144 return -1; 145 } 146 147 /* First clear response register */ 148 REG_WRITE(MP1_SMN_C2PMSG_38, VBIOSSMC_Status_BUSY); 149 150 /* Set the parameter register for the SMU message, unit is Mhz */ 151 REG_WRITE(MP1_SMN_C2PMSG_37, param); 152 153 /* Trigger the message transaction by writing the message ID */ 154 generic_write_indirect_reg(CTX, 155 REG_NBIO(RSMU_INDEX), REG_NBIO(RSMU_DATA), 156 mmMP1_C2PMSG_3, msg_id); 157 158 result = dcn315_smu_wait_for_response(clk_mgr, 10, 200000); 159 160 if (result == VBIOSSMC_Status_BUSY) { 161 ASSERT(0); 162 dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000); 163 } 164 165 return REG_READ(MP1_SMN_C2PMSG_37); 166 } 167 168 int dcn315_smu_get_smu_version(struct clk_mgr_internal *clk_mgr) 169 { 170 return dcn315_smu_send_msg_with_param( 171 clk_mgr, 172 VBIOSSMC_MSG_GetPmfwVersion, 173 0); 174 } 175 176 177 int dcn315_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz) 178 { 179 int actual_dispclk_set_mhz = -1; 180 181 if (!clk_mgr->smu_present) 182 return requested_dispclk_khz; 183 184 /* Unit of SMU msg parameter is Mhz */ 185 actual_dispclk_set_mhz = dcn315_smu_send_msg_with_param( 186 clk_mgr, 187 VBIOSSMC_MSG_SetDispclkFreq, 188 khz_to_mhz_ceil(requested_dispclk_khz)); 189 190 return actual_dispclk_set_mhz * 1000; 191 } 192 193 int dcn315_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz) 194 { 195 int actual_dcfclk_set_mhz = -1; 196 197 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 198 return -1; 199 200 if (!clk_mgr->smu_present) 201 return requested_dcfclk_khz; 202 203 actual_dcfclk_set_mhz = dcn315_smu_send_msg_with_param( 204 clk_mgr, 205 VBIOSSMC_MSG_SetHardMinDcfclkByFreq, 206 khz_to_mhz_ceil(requested_dcfclk_khz)); 207 208 #ifdef DBG 209 smu_print("actual_dcfclk_set_mhz %d is set to : %d\n", actual_dcfclk_set_mhz, actual_dcfclk_set_mhz * 1000); 210 #endif 211 212 return actual_dcfclk_set_mhz * 1000; 213 } 214 215 int dcn315_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz) 216 { 217 int actual_min_ds_dcfclk_mhz = -1; 218 219 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 220 return -1; 221 222 if (!clk_mgr->smu_present) 223 return requested_min_ds_dcfclk_khz; 224 225 actual_min_ds_dcfclk_mhz = dcn315_smu_send_msg_with_param( 226 clk_mgr, 227 VBIOSSMC_MSG_SetMinDeepSleepDcfclk, 228 khz_to_mhz_ceil(requested_min_ds_dcfclk_khz)); 229 230 return actual_min_ds_dcfclk_mhz * 1000; 231 } 232 233 int dcn315_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz) 234 { 235 int actual_dppclk_set_mhz = -1; 236 237 if (!clk_mgr->smu_present) 238 return requested_dpp_khz; 239 240 actual_dppclk_set_mhz = dcn315_smu_send_msg_with_param( 241 clk_mgr, 242 VBIOSSMC_MSG_SetDppclkFreq, 243 khz_to_mhz_ceil(requested_dpp_khz)); 244 245 return actual_dppclk_set_mhz * 1000; 246 } 247 248 void dcn315_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info) 249 { 250 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 251 return; 252 253 if (!clk_mgr->smu_present) 254 return; 255 256 //TODO: Work with smu team to define optimization options. 257 dcn315_smu_send_msg_with_param( 258 clk_mgr, 259 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 260 idle_info); 261 } 262 263 void dcn315_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable) 264 { 265 union display_idle_optimization_u idle_info = { 0 }; 266 267 if (!clk_mgr->smu_present) 268 return; 269 270 if (enable) { 271 idle_info.idle_info.df_request_disabled = 1; 272 idle_info.idle_info.phy_ref_clk_off = 1; 273 } 274 275 dcn315_smu_send_msg_with_param( 276 clk_mgr, 277 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 278 idle_info.data); 279 } 280 281 void dcn315_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr) 282 { 283 if (!clk_mgr->smu_present) 284 return; 285 286 dcn315_smu_send_msg_with_param( 287 clk_mgr, 288 VBIOSSMC_MSG_UpdatePmeRestore, 289 0); 290 } 291 void dcn315_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high) 292 { 293 if (!clk_mgr->smu_present) 294 return; 295 296 dcn315_smu_send_msg_with_param(clk_mgr, 297 VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high); 298 } 299 300 void dcn315_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low) 301 { 302 if (!clk_mgr->smu_present) 303 return; 304 305 dcn315_smu_send_msg_with_param(clk_mgr, 306 VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low); 307 } 308 309 void dcn315_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr) 310 { 311 if (!clk_mgr->smu_present) 312 return; 313 314 dcn315_smu_send_msg_with_param(clk_mgr, 315 VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS); 316 } 317 318 void dcn315_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) 319 { 320 if (!clk_mgr->smu_present) 321 return; 322 323 dcn315_smu_send_msg_with_param(clk_mgr, 324 VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS); 325 } 326 327 int dcn315_smu_get_dpref_clk(struct clk_mgr_internal *clk_mgr) 328 { 329 int dprefclk_get_mhz = -1; 330 if (clk_mgr->smu_present) { 331 dprefclk_get_mhz = dcn315_smu_send_msg_with_param( 332 clk_mgr, 333 VBIOSSMC_MSG_GetDprefclkFreq, 334 0); 335 } 336 return (dprefclk_get_mhz * 1000); 337 } 338 339 int dcn315_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr) 340 { 341 int fclk_get_mhz = -1; 342 343 if (clk_mgr->smu_present) { 344 fclk_get_mhz = dcn315_smu_send_msg_with_param( 345 clk_mgr, 346 VBIOSSMC_MSG_GetDtbclkFreq, 347 0); 348 } 349 return (fclk_get_mhz * 1000); 350 } 351 352 void dcn315_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable) 353 { 354 if (!clk_mgr->smu_present) 355 return; 356 357 dcn315_smu_send_msg_with_param( 358 clk_mgr, 359 VBIOSSMC_MSG_SetDtbClk, 360 enable); 361 } 362