1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright 2022 Advanced Micro Devices, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 * OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: AMD 24 * 25 */ 26 27 28 29 #include "core_types.h" 30 #include "clk_mgr_internal.h" 31 #include "reg_helper.h" 32 #include "dm_helpers.h" 33 #include "dcn314_smu.h" 34 35 #include "mp/mp_13_0_5_offset.h" 36 37 /* TODO: Use the real headers when they're correct */ 38 #define MP1_BASE__INST0_SEG0 0x00016000 39 #define MP1_BASE__INST0_SEG1 0x0243FC00 40 #define MP1_BASE__INST0_SEG2 0x00DC0000 41 #define MP1_BASE__INST0_SEG3 0x00E00000 42 #define MP1_BASE__INST0_SEG4 0x00E40000 43 #define MP1_BASE__INST0_SEG5 0 44 45 #ifdef BASE_INNER 46 #undef BASE_INNER 47 #endif 48 49 #define BASE_INNER(seg) MP1_BASE__INST0_SEG ## seg 50 51 #define BASE(seg) BASE_INNER(seg) 52 53 #define REG(reg_name) (BASE(reg##reg_name##_BASE_IDX) + reg##reg_name) 54 55 #define FN(reg_name, field) \ 56 FD(reg_name##__##field) 57 58 #include "logger_types.h" 59 #undef DC_LOGGER 60 #define DC_LOGGER \ 61 CTX->logger 62 #define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); } 63 64 #define VBIOSSMC_MSG_TestMessage 0x1 65 #define VBIOSSMC_MSG_GetSmuVersion 0x2 66 #define VBIOSSMC_MSG_PowerUpGfx 0x3 67 #define VBIOSSMC_MSG_SetDispclkFreq 0x4 68 #define VBIOSSMC_MSG_SetDprefclkFreq 0x5 //Not used. DPRef is constant 69 #define VBIOSSMC_MSG_SetDppclkFreq 0x6 70 #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq 0x7 71 #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk 0x8 72 #define VBIOSSMC_MSG_SetPhyclkVoltageByFreq 0x9 //Keep it in case VMIN dees not support phy clk 73 #define VBIOSSMC_MSG_GetFclkFrequency 0xA 74 #define VBIOSSMC_MSG_SetDisplayCount 0xB //Not used anymore 75 #define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0xC //Not used anymore 76 #define VBIOSSMC_MSG_UpdatePmeRestore 0xD 77 #define VBIOSSMC_MSG_SetVbiosDramAddrHigh 0xE //Used for WM table txfr 78 #define VBIOSSMC_MSG_SetVbiosDramAddrLow 0xF 79 #define VBIOSSMC_MSG_TransferTableSmu2Dram 0x10 80 #define VBIOSSMC_MSG_TransferTableDram2Smu 0x11 81 #define VBIOSSMC_MSG_SetDisplayIdleOptimizations 0x12 82 #define VBIOSSMC_MSG_GetDprefclkFreq 0x13 83 #define VBIOSSMC_MSG_GetDtbclkFreq 0x14 84 #define VBIOSSMC_MSG_AllowZstatesEntry 0x15 85 #define VBIOSSMC_MSG_DisallowZstatesEntry 0x16 86 #define VBIOSSMC_MSG_SetDtbClk 0x17 87 #define VBIOSSMC_Message_Count 0x18 88 89 #define VBIOSSMC_Status_BUSY 0x0 90 #define VBIOSSMC_Result_OK 0x1 91 #define VBIOSSMC_Result_Failed 0xFF 92 #define VBIOSSMC_Result_UnknownCmd 0xFE 93 #define VBIOSSMC_Result_CmdRejectedPrereq 0xFD 94 #define VBIOSSMC_Result_CmdRejectedBusy 0xFC 95 96 /* 97 * Function to be used instead of REG_WAIT macro because the wait ends when 98 * the register is NOT EQUAL to zero, and because the translation in msg_if.h 99 * won't work with REG_WAIT. 100 */ 101 static uint32_t dcn314_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) 102 { 103 uint32_t res_val = VBIOSSMC_Status_BUSY; 104 105 do { 106 res_val = REG_READ(MP1_SMN_C2PMSG_91); 107 if (res_val != VBIOSSMC_Status_BUSY) 108 break; 109 110 if (delay_us >= 1000) 111 msleep(delay_us/1000); 112 else if (delay_us > 0) 113 udelay(delay_us); 114 } while (max_retries--); 115 116 return res_val; 117 } 118 119 static int dcn314_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr, 120 unsigned int msg_id, 121 unsigned int param) 122 { 123 uint32_t result; 124 125 result = dcn314_smu_wait_for_response(clk_mgr, 10, 200000); 126 127 if (result != VBIOSSMC_Result_OK) 128 smu_print("SMU Response was not OK. SMU response after wait received is: %d\n", 129 result); 130 131 if (result == VBIOSSMC_Status_BUSY) 132 return -1; 133 134 /* First clear response register */ 135 REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY); 136 137 /* Set the parameter register for the SMU message, unit is Mhz */ 138 REG_WRITE(MP1_SMN_C2PMSG_83, param); 139 140 /* Trigger the message transaction by writing the message ID */ 141 REG_WRITE(MP1_SMN_C2PMSG_67, msg_id); 142 143 result = dcn314_smu_wait_for_response(clk_mgr, 10, 200000); 144 145 if (result == VBIOSSMC_Result_Failed) { 146 if (msg_id == VBIOSSMC_MSG_TransferTableDram2Smu && 147 param == TABLE_WATERMARKS) 148 DC_LOG_WARNING("Watermarks table not configured properly by SMU"); 149 else 150 ASSERT(0); 151 REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Result_OK); 152 return -1; 153 } 154 155 if (IS_SMU_TIMEOUT(result)) { 156 ASSERT(0); 157 dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000); 158 } 159 160 return REG_READ(MP1_SMN_C2PMSG_83); 161 } 162 163 int dcn314_smu_get_smu_version(struct clk_mgr_internal *clk_mgr) 164 { 165 return dcn314_smu_send_msg_with_param( 166 clk_mgr, 167 VBIOSSMC_MSG_GetSmuVersion, 168 0); 169 } 170 171 172 int dcn314_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz) 173 { 174 int actual_dispclk_set_mhz = -1; 175 176 if (!clk_mgr->smu_present) 177 return requested_dispclk_khz; 178 179 /* Unit of SMU msg parameter is Mhz */ 180 actual_dispclk_set_mhz = dcn314_smu_send_msg_with_param( 181 clk_mgr, 182 VBIOSSMC_MSG_SetDispclkFreq, 183 khz_to_mhz_ceil(requested_dispclk_khz)); 184 185 return actual_dispclk_set_mhz * 1000; 186 } 187 188 int dcn314_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr) 189 { 190 int actual_dprefclk_set_mhz = -1; 191 192 if (!clk_mgr->smu_present) 193 return clk_mgr->base.dprefclk_khz; 194 195 actual_dprefclk_set_mhz = dcn314_smu_send_msg_with_param( 196 clk_mgr, 197 VBIOSSMC_MSG_SetDprefclkFreq, 198 khz_to_mhz_ceil(clk_mgr->base.dprefclk_khz)); 199 200 /* TODO: add code for programing DP DTO, currently this is down by command table */ 201 202 return actual_dprefclk_set_mhz * 1000; 203 } 204 205 int dcn314_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz) 206 { 207 int actual_dcfclk_set_mhz = -1; 208 209 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 210 return -1; 211 212 if (!clk_mgr->smu_present) 213 return requested_dcfclk_khz; 214 215 actual_dcfclk_set_mhz = dcn314_smu_send_msg_with_param( 216 clk_mgr, 217 VBIOSSMC_MSG_SetHardMinDcfclkByFreq, 218 khz_to_mhz_ceil(requested_dcfclk_khz)); 219 220 #ifdef DBG 221 smu_print("actual_dcfclk_set_mhz %d is set to : %d\n", 222 actual_dcfclk_set_mhz, 223 actual_dcfclk_set_mhz * 1000); 224 #endif 225 226 return actual_dcfclk_set_mhz * 1000; 227 } 228 229 int dcn314_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz) 230 { 231 int actual_min_ds_dcfclk_mhz = -1; 232 233 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 234 return -1; 235 236 if (!clk_mgr->smu_present) 237 return requested_min_ds_dcfclk_khz; 238 239 actual_min_ds_dcfclk_mhz = dcn314_smu_send_msg_with_param( 240 clk_mgr, 241 VBIOSSMC_MSG_SetMinDeepSleepDcfclk, 242 khz_to_mhz_ceil(requested_min_ds_dcfclk_khz)); 243 244 return actual_min_ds_dcfclk_mhz * 1000; 245 } 246 247 int dcn314_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz) 248 { 249 int actual_dppclk_set_mhz = -1; 250 251 if (!clk_mgr->smu_present) 252 return requested_dpp_khz; 253 254 actual_dppclk_set_mhz = dcn314_smu_send_msg_with_param( 255 clk_mgr, 256 VBIOSSMC_MSG_SetDppclkFreq, 257 khz_to_mhz_ceil(requested_dpp_khz)); 258 259 return actual_dppclk_set_mhz * 1000; 260 } 261 262 void dcn314_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info) 263 { 264 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 265 return; 266 267 if (!clk_mgr->smu_present) 268 return; 269 270 //TODO: Work with smu team to define optimization options. 271 dcn314_smu_send_msg_with_param( 272 clk_mgr, 273 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 274 idle_info); 275 } 276 277 void dcn314_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable) 278 { 279 union display_idle_optimization_u idle_info = { 0 }; 280 281 if (!clk_mgr->smu_present) 282 return; 283 284 if (enable) { 285 idle_info.idle_info.df_request_disabled = 1; 286 idle_info.idle_info.phy_ref_clk_off = 1; 287 } 288 289 dcn314_smu_send_msg_with_param( 290 clk_mgr, 291 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 292 idle_info.data); 293 } 294 295 void dcn314_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr) 296 { 297 if (!clk_mgr->smu_present) 298 return; 299 300 dcn314_smu_send_msg_with_param( 301 clk_mgr, 302 VBIOSSMC_MSG_UpdatePmeRestore, 303 0); 304 } 305 306 void dcn314_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high) 307 { 308 if (!clk_mgr->smu_present) 309 return; 310 311 dcn314_smu_send_msg_with_param(clk_mgr, 312 VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high); 313 } 314 315 void dcn314_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low) 316 { 317 if (!clk_mgr->smu_present) 318 return; 319 320 dcn314_smu_send_msg_with_param(clk_mgr, 321 VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low); 322 } 323 324 void dcn314_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr) 325 { 326 if (!clk_mgr->smu_present) 327 return; 328 329 dcn314_smu_send_msg_with_param(clk_mgr, 330 VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS); 331 } 332 333 void dcn314_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) 334 { 335 if (!clk_mgr->smu_present) 336 return; 337 338 dcn314_smu_send_msg_with_param(clk_mgr, 339 VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS); 340 } 341 342 void dcn314_smu_set_zstate_support(struct clk_mgr_internal *clk_mgr, enum dcn_zstate_support_state support) 343 { 344 unsigned int msg_id, param; 345 346 if (!clk_mgr->smu_present) 347 return; 348 349 switch (support) { 350 351 case DCN_ZSTATE_SUPPORT_ALLOW: 352 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 353 param = (1 << 10) | (1 << 9) | (1 << 8); 354 break; 355 356 case DCN_ZSTATE_SUPPORT_DISALLOW: 357 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 358 param = 0; 359 break; 360 361 362 case DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY: 363 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 364 param = (1 << 10); 365 break; 366 367 case DCN_ZSTATE_SUPPORT_ALLOW_Z8_Z10_ONLY: 368 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 369 param = (1 << 10) | (1 << 8); 370 break; 371 372 case DCN_ZSTATE_SUPPORT_ALLOW_Z8_ONLY: 373 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 374 param = (1 << 8); 375 break; 376 377 default: //DCN_ZSTATE_SUPPORT_UNKNOWN 378 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 379 param = 0; 380 break; 381 } 382 383 384 dcn314_smu_send_msg_with_param( 385 clk_mgr, 386 msg_id, 387 param); 388 389 } 390 391 /* Arg = 1: Turn DTB on; 0: Turn DTB CLK OFF. when it is on, it is 600MHZ */ 392 void dcn314_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable) 393 { 394 if (!clk_mgr->smu_present) 395 return; 396 397 dcn314_smu_send_msg_with_param( 398 clk_mgr, 399 VBIOSSMC_MSG_SetDtbClk, 400 enable); 401 } 402