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