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 */ dcn314_smu_wait_for_response(struct clk_mgr_internal * clk_mgr,unsigned int delay_us,unsigned int max_retries)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 dcn314_smu_send_msg_with_param(struct clk_mgr_internal * clk_mgr,unsigned int msg_id,unsigned int param)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_DEBUG("Watermarks table not configured properly by SMU"); 149 else if (msg_id == VBIOSSMC_MSG_SetHardMinDcfclkByFreq || 150 msg_id == VBIOSSMC_MSG_SetMinDeepSleepDcfclk) 151 DC_LOG_WARNING("DCFCLK_DPM is not enabled by BIOS"); 152 else 153 ASSERT(0); 154 REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Result_OK); 155 return -1; 156 } 157 158 if (IS_SMU_TIMEOUT(result)) { 159 ASSERT(0); 160 dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000); 161 } 162 163 return REG_READ(MP1_SMN_C2PMSG_83); 164 } 165 dcn314_smu_get_smu_version(struct clk_mgr_internal * clk_mgr)166 int dcn314_smu_get_smu_version(struct clk_mgr_internal *clk_mgr) 167 { 168 return dcn314_smu_send_msg_with_param( 169 clk_mgr, 170 VBIOSSMC_MSG_GetSmuVersion, 171 0); 172 } 173 174 dcn314_smu_set_dispclk(struct clk_mgr_internal * clk_mgr,int requested_dispclk_khz)175 int dcn314_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz) 176 { 177 int actual_dispclk_set_mhz = -1; 178 179 if (!clk_mgr->smu_present) 180 return requested_dispclk_khz; 181 182 /* Unit of SMU msg parameter is Mhz */ 183 actual_dispclk_set_mhz = dcn314_smu_send_msg_with_param( 184 clk_mgr, 185 VBIOSSMC_MSG_SetDispclkFreq, 186 khz_to_mhz_ceil(requested_dispclk_khz)); 187 188 return actual_dispclk_set_mhz * 1000; 189 } 190 dcn314_smu_set_dprefclk(struct clk_mgr_internal * clk_mgr)191 int dcn314_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr) 192 { 193 int actual_dprefclk_set_mhz = -1; 194 195 if (!clk_mgr->smu_present) 196 return clk_mgr->base.dprefclk_khz; 197 198 actual_dprefclk_set_mhz = dcn314_smu_send_msg_with_param( 199 clk_mgr, 200 VBIOSSMC_MSG_SetDprefclkFreq, 201 khz_to_mhz_ceil(clk_mgr->base.dprefclk_khz)); 202 203 /* TODO: add code for programing DP DTO, currently this is down by command table */ 204 205 return actual_dprefclk_set_mhz * 1000; 206 } 207 dcn314_smu_set_hard_min_dcfclk(struct clk_mgr_internal * clk_mgr,int requested_dcfclk_khz)208 int dcn314_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz) 209 { 210 int actual_dcfclk_set_mhz = -1; 211 212 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 213 return -1; 214 215 if (!clk_mgr->smu_present) 216 return requested_dcfclk_khz; 217 218 actual_dcfclk_set_mhz = dcn314_smu_send_msg_with_param( 219 clk_mgr, 220 VBIOSSMC_MSG_SetHardMinDcfclkByFreq, 221 khz_to_mhz_ceil(requested_dcfclk_khz)); 222 223 #ifdef DBG 224 smu_print("actual_dcfclk_set_mhz %d is set to : %d\n", 225 actual_dcfclk_set_mhz, 226 actual_dcfclk_set_mhz * 1000); 227 #endif 228 229 return actual_dcfclk_set_mhz * 1000; 230 } 231 dcn314_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal * clk_mgr,int requested_min_ds_dcfclk_khz)232 int dcn314_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz) 233 { 234 int actual_min_ds_dcfclk_mhz = -1; 235 236 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 237 return -1; 238 239 if (!clk_mgr->smu_present) 240 return requested_min_ds_dcfclk_khz; 241 242 actual_min_ds_dcfclk_mhz = dcn314_smu_send_msg_with_param( 243 clk_mgr, 244 VBIOSSMC_MSG_SetMinDeepSleepDcfclk, 245 khz_to_mhz_ceil(requested_min_ds_dcfclk_khz)); 246 247 return actual_min_ds_dcfclk_mhz * 1000; 248 } 249 dcn314_smu_set_dppclk(struct clk_mgr_internal * clk_mgr,int requested_dpp_khz)250 int dcn314_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz) 251 { 252 int actual_dppclk_set_mhz = -1; 253 254 if (!clk_mgr->smu_present) 255 return requested_dpp_khz; 256 257 actual_dppclk_set_mhz = dcn314_smu_send_msg_with_param( 258 clk_mgr, 259 VBIOSSMC_MSG_SetDppclkFreq, 260 khz_to_mhz_ceil(requested_dpp_khz)); 261 262 return actual_dppclk_set_mhz * 1000; 263 } 264 dcn314_smu_set_display_idle_optimization(struct clk_mgr_internal * clk_mgr,uint32_t idle_info)265 void dcn314_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info) 266 { 267 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled) 268 return; 269 270 if (!clk_mgr->smu_present) 271 return; 272 273 //TODO: Work with smu team to define optimization options. 274 dcn314_smu_send_msg_with_param( 275 clk_mgr, 276 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 277 idle_info); 278 } 279 dcn314_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal * clk_mgr,bool enable)280 void dcn314_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable) 281 { 282 union display_idle_optimization_u idle_info = { 0 }; 283 284 if (!clk_mgr->smu_present) 285 return; 286 287 if (enable) { 288 idle_info.idle_info.df_request_disabled = 1; 289 idle_info.idle_info.phy_ref_clk_off = 1; 290 } 291 292 dcn314_smu_send_msg_with_param( 293 clk_mgr, 294 VBIOSSMC_MSG_SetDisplayIdleOptimizations, 295 idle_info.data); 296 } 297 dcn314_smu_enable_pme_wa(struct clk_mgr_internal * clk_mgr)298 void dcn314_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr) 299 { 300 if (!clk_mgr->smu_present) 301 return; 302 303 dcn314_smu_send_msg_with_param( 304 clk_mgr, 305 VBIOSSMC_MSG_UpdatePmeRestore, 306 0); 307 } 308 dcn314_smu_set_dram_addr_high(struct clk_mgr_internal * clk_mgr,uint32_t addr_high)309 void dcn314_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high) 310 { 311 if (!clk_mgr->smu_present) 312 return; 313 314 dcn314_smu_send_msg_with_param(clk_mgr, 315 VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high); 316 } 317 dcn314_smu_set_dram_addr_low(struct clk_mgr_internal * clk_mgr,uint32_t addr_low)318 void dcn314_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low) 319 { 320 if (!clk_mgr->smu_present) 321 return; 322 323 dcn314_smu_send_msg_with_param(clk_mgr, 324 VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low); 325 } 326 dcn314_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal * clk_mgr)327 void dcn314_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr) 328 { 329 if (!clk_mgr->smu_present) 330 return; 331 332 dcn314_smu_send_msg_with_param(clk_mgr, 333 VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS); 334 } 335 dcn314_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal * clk_mgr)336 void dcn314_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) 337 { 338 if (!clk_mgr->smu_present) 339 return; 340 341 dcn314_smu_send_msg_with_param(clk_mgr, 342 VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS); 343 } 344 dcn314_smu_set_zstate_support(struct clk_mgr_internal * clk_mgr,enum dcn_zstate_support_state support)345 void dcn314_smu_set_zstate_support(struct clk_mgr_internal *clk_mgr, enum dcn_zstate_support_state support) 346 { 347 unsigned int msg_id, param; 348 349 if (!clk_mgr->smu_present) 350 return; 351 352 switch (support) { 353 354 case DCN_ZSTATE_SUPPORT_ALLOW: 355 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 356 param = (1 << 10) | (1 << 9) | (1 << 8); 357 break; 358 359 case DCN_ZSTATE_SUPPORT_DISALLOW: 360 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 361 param = 0; 362 break; 363 364 365 case DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY: 366 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 367 param = (1 << 10); 368 break; 369 370 case DCN_ZSTATE_SUPPORT_ALLOW_Z8_Z10_ONLY: 371 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 372 param = (1 << 10) | (1 << 8); 373 break; 374 375 case DCN_ZSTATE_SUPPORT_ALLOW_Z8_ONLY: 376 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 377 param = (1 << 8); 378 break; 379 380 default: //DCN_ZSTATE_SUPPORT_UNKNOWN 381 msg_id = VBIOSSMC_MSG_AllowZstatesEntry; 382 param = 0; 383 break; 384 } 385 386 387 dcn314_smu_send_msg_with_param( 388 clk_mgr, 389 msg_id, 390 param); 391 392 } 393 394 /* Arg = 1: Turn DTB on; 0: Turn DTB CLK OFF. when it is on, it is 600MHZ */ dcn314_smu_set_dtbclk(struct clk_mgr_internal * clk_mgr,bool enable)395 void dcn314_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable) 396 { 397 if (!clk_mgr->smu_present) 398 return; 399 400 dcn314_smu_send_msg_with_param( 401 clk_mgr, 402 VBIOSSMC_MSG_SetDtbClk, 403 enable); 404 } 405