1 /*
2 * Copyright 2012-16 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 "dcn31_smu.h"
31
32 #include "yellow_carp_offset.h"
33 #include "mp/mp_13_0_2_offset.h"
34 #include "mp/mp_13_0_2_sh_mask.h"
35
36 #define REG(reg_name) \
37 (MP0_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name)
38
39 #define FN(reg_name, field) \
40 FD(reg_name##__##field)
41
42 #include "logger_types.h"
43 #undef DC_LOGGER
44 #define DC_LOGGER \
45 CTX->logger
46 #define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); }
47
48 #define VBIOSSMC_MSG_TestMessage 0x1
49 #define VBIOSSMC_MSG_GetSmuVersion 0x2
50 #define VBIOSSMC_MSG_PowerUpGfx 0x3
51 #define VBIOSSMC_MSG_SetDispclkFreq 0x4
52 #define VBIOSSMC_MSG_SetDprefclkFreq 0x5 //Not used. DPRef is constant
53 #define VBIOSSMC_MSG_SetDppclkFreq 0x6
54 #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq 0x7
55 #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk 0x8
56 #define VBIOSSMC_MSG_SetPhyclkVoltageByFreq 0x9 //Keep it in case VMIN dees not support phy clk
57 #define VBIOSSMC_MSG_GetFclkFrequency 0xA
58 #define VBIOSSMC_MSG_SetDisplayCount 0xB //Not used anymore
59 #define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0xC //Not used anymore
60 #define VBIOSSMC_MSG_UpdatePmeRestore 0xD
61 #define VBIOSSMC_MSG_SetVbiosDramAddrHigh 0xE //Used for WM table txfr
62 #define VBIOSSMC_MSG_SetVbiosDramAddrLow 0xF
63 #define VBIOSSMC_MSG_TransferTableSmu2Dram 0x10
64 #define VBIOSSMC_MSG_TransferTableDram2Smu 0x11
65 #define VBIOSSMC_MSG_SetDisplayIdleOptimizations 0x12
66 #define VBIOSSMC_MSG_GetDprefclkFreq 0x13
67 #define VBIOSSMC_MSG_GetDtbclkFreq 0x14
68 #define VBIOSSMC_MSG_AllowZstatesEntry 0x15
69 #define VBIOSSMC_MSG_DisallowZstatesEntry 0x16
70 #define VBIOSSMC_MSG_SetDtbClk 0x17
71 #define VBIOSSMC_Message_Count 0x18
72
73 #define VBIOSSMC_Status_BUSY 0x0
74 #define VBIOSSMC_Result_OK 0x1
75 #define VBIOSSMC_Result_Failed 0xFF
76 #define VBIOSSMC_Result_UnknownCmd 0xFE
77 #define VBIOSSMC_Result_CmdRejectedPrereq 0xFD
78 #define VBIOSSMC_Result_CmdRejectedBusy 0xFC
79
80 /*
81 * Function to be used instead of REG_WAIT macro because the wait ends when
82 * the register is NOT EQUAL to zero, and because the translation in msg_if.h
83 * won't work with REG_WAIT.
84 */
dcn31_smu_wait_for_response(struct clk_mgr_internal * clk_mgr,unsigned int delay_us,unsigned int max_retries)85 static uint32_t dcn31_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries)
86 {
87 uint32_t res_val = VBIOSSMC_Status_BUSY;
88
89 do {
90 res_val = REG_READ(MP1_SMN_C2PMSG_91);
91 if (res_val != VBIOSSMC_Status_BUSY)
92 break;
93
94 if (delay_us >= 1000)
95 msleep(delay_us/1000);
96 else if (delay_us > 0)
97 udelay(delay_us);
98 } while (max_retries--);
99
100 return res_val;
101 }
102
dcn31_smu_send_msg_with_param(struct clk_mgr_internal * clk_mgr,unsigned int msg_id,unsigned int param)103 static int dcn31_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr,
104 unsigned int msg_id,
105 unsigned int param)
106 {
107 uint32_t result;
108
109 result = dcn31_smu_wait_for_response(clk_mgr, 10, 200000);
110
111 if (result != VBIOSSMC_Result_OK)
112 smu_print("SMU Response was not OK. SMU response after wait received is: %d\n", result);
113
114 if (result == VBIOSSMC_Status_BUSY) {
115 return -1;
116 }
117
118 /* First clear response register */
119 REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY);
120
121 /* Set the parameter register for the SMU message, unit is Mhz */
122 REG_WRITE(MP1_SMN_C2PMSG_83, param);
123
124 /* Trigger the message transaction by writing the message ID */
125 REG_WRITE(MP1_SMN_C2PMSG_67, msg_id);
126
127 result = dcn31_smu_wait_for_response(clk_mgr, 10, 200000);
128
129 if (result == VBIOSSMC_Result_Failed) {
130 if (msg_id == VBIOSSMC_MSG_TransferTableDram2Smu &&
131 param == TABLE_WATERMARKS)
132 DC_LOG_DEBUG("Watermarks table not configured properly by SMU");
133 else
134 ASSERT(0);
135 REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Result_OK);
136 return -1;
137 }
138
139 if (IS_SMU_TIMEOUT(result)) {
140 ASSERT(0);
141 dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000);
142 }
143
144 return REG_READ(MP1_SMN_C2PMSG_83);
145 }
146
dcn31_smu_get_smu_version(struct clk_mgr_internal * clk_mgr)147 int dcn31_smu_get_smu_version(struct clk_mgr_internal *clk_mgr)
148 {
149 return dcn31_smu_send_msg_with_param(
150 clk_mgr,
151 VBIOSSMC_MSG_GetSmuVersion,
152 0);
153 }
154
155
dcn31_smu_set_dispclk(struct clk_mgr_internal * clk_mgr,int requested_dispclk_khz)156 int dcn31_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz)
157 {
158 int actual_dispclk_set_mhz = -1;
159
160 if (!clk_mgr->smu_present)
161 return requested_dispclk_khz;
162
163 /* Unit of SMU msg parameter is Mhz */
164 actual_dispclk_set_mhz = dcn31_smu_send_msg_with_param(
165 clk_mgr,
166 VBIOSSMC_MSG_SetDispclkFreq,
167 khz_to_mhz_ceil(requested_dispclk_khz));
168
169 return actual_dispclk_set_mhz * 1000;
170 }
171
dcn31_smu_set_dprefclk(struct clk_mgr_internal * clk_mgr)172 int dcn31_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr)
173 {
174 int actual_dprefclk_set_mhz = -1;
175
176 if (!clk_mgr->smu_present)
177 return clk_mgr->base.dprefclk_khz;
178
179 actual_dprefclk_set_mhz = dcn31_smu_send_msg_with_param(
180 clk_mgr,
181 VBIOSSMC_MSG_SetDprefclkFreq,
182 khz_to_mhz_ceil(clk_mgr->base.dprefclk_khz));
183
184 /* TODO: add code for programing DP DTO, currently this is down by command table */
185
186 return actual_dprefclk_set_mhz * 1000;
187 }
188
dcn31_smu_set_hard_min_dcfclk(struct clk_mgr_internal * clk_mgr,int requested_dcfclk_khz)189 int dcn31_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz)
190 {
191 int actual_dcfclk_set_mhz = -1;
192
193 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
194 return -1;
195
196 if (!clk_mgr->smu_present)
197 return requested_dcfclk_khz;
198
199 actual_dcfclk_set_mhz = dcn31_smu_send_msg_with_param(
200 clk_mgr,
201 VBIOSSMC_MSG_SetHardMinDcfclkByFreq,
202 khz_to_mhz_ceil(requested_dcfclk_khz));
203
204 return actual_dcfclk_set_mhz * 1000;
205 }
206
dcn31_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal * clk_mgr,int requested_min_ds_dcfclk_khz)207 int dcn31_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz)
208 {
209 int actual_min_ds_dcfclk_mhz = -1;
210
211 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
212 return -1;
213
214 if (!clk_mgr->smu_present)
215 return requested_min_ds_dcfclk_khz;
216
217 actual_min_ds_dcfclk_mhz = dcn31_smu_send_msg_with_param(
218 clk_mgr,
219 VBIOSSMC_MSG_SetMinDeepSleepDcfclk,
220 khz_to_mhz_ceil(requested_min_ds_dcfclk_khz));
221
222 return actual_min_ds_dcfclk_mhz * 1000;
223 }
224
dcn31_smu_set_dppclk(struct clk_mgr_internal * clk_mgr,int requested_dpp_khz)225 int dcn31_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz)
226 {
227 int actual_dppclk_set_mhz = -1;
228
229 if (!clk_mgr->smu_present)
230 return requested_dpp_khz;
231
232 actual_dppclk_set_mhz = dcn31_smu_send_msg_with_param(
233 clk_mgr,
234 VBIOSSMC_MSG_SetDppclkFreq,
235 khz_to_mhz_ceil(requested_dpp_khz));
236
237 return actual_dppclk_set_mhz * 1000;
238 }
239
dcn31_smu_set_display_idle_optimization(struct clk_mgr_internal * clk_mgr,uint32_t idle_info)240 void dcn31_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info)
241 {
242 if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
243 return;
244
245 if (!clk_mgr->smu_present)
246 return;
247
248 //TODO: Work with smu team to define optimization options.
249 dcn31_smu_send_msg_with_param(
250 clk_mgr,
251 VBIOSSMC_MSG_SetDisplayIdleOptimizations,
252 idle_info);
253 }
254
dcn31_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal * clk_mgr,bool enable)255 void dcn31_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable)
256 {
257 union display_idle_optimization_u idle_info = { 0 };
258
259 if (!clk_mgr->smu_present)
260 return;
261
262 if (enable) {
263 idle_info.idle_info.df_request_disabled = 1;
264 idle_info.idle_info.phy_ref_clk_off = 1;
265 }
266
267 dcn31_smu_send_msg_with_param(
268 clk_mgr,
269 VBIOSSMC_MSG_SetDisplayIdleOptimizations,
270 idle_info.data);
271 }
272
dcn31_smu_enable_pme_wa(struct clk_mgr_internal * clk_mgr)273 void dcn31_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr)
274 {
275 if (!clk_mgr->smu_present)
276 return;
277
278 dcn31_smu_send_msg_with_param(
279 clk_mgr,
280 VBIOSSMC_MSG_UpdatePmeRestore,
281 0);
282 }
283
dcn31_smu_set_dram_addr_high(struct clk_mgr_internal * clk_mgr,uint32_t addr_high)284 void dcn31_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high)
285 {
286 if (!clk_mgr->smu_present)
287 return;
288
289 dcn31_smu_send_msg_with_param(clk_mgr,
290 VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high);
291 }
292
dcn31_smu_set_dram_addr_low(struct clk_mgr_internal * clk_mgr,uint32_t addr_low)293 void dcn31_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low)
294 {
295 if (!clk_mgr->smu_present)
296 return;
297
298 dcn31_smu_send_msg_with_param(clk_mgr,
299 VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low);
300 }
301
dcn31_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal * clk_mgr)302 void dcn31_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr)
303 {
304 if (!clk_mgr->smu_present)
305 return;
306
307 dcn31_smu_send_msg_with_param(clk_mgr,
308 VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS);
309 }
310
dcn31_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal * clk_mgr)311 void dcn31_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr)
312 {
313 if (!clk_mgr->smu_present)
314 return;
315
316 dcn31_smu_send_msg_with_param(clk_mgr,
317 VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS);
318 }
319
dcn31_smu_set_zstate_support(struct clk_mgr_internal * clk_mgr,enum dcn_zstate_support_state support)320 void dcn31_smu_set_zstate_support(struct clk_mgr_internal *clk_mgr, enum dcn_zstate_support_state support)
321 {
322 unsigned int msg_id, param;
323
324 if (!clk_mgr->smu_present)
325 return;
326
327 if (!clk_mgr->base.ctx->dc->debug.enable_z9_disable_interface &&
328 (support == DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY))
329 support = DCN_ZSTATE_SUPPORT_DISALLOW;
330
331 if (support == DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY ||
332 support == DCN_ZSTATE_SUPPORT_ALLOW_Z8_Z10_ONLY)
333 param = 1;
334 else
335 param = 0;
336
337 if (support == DCN_ZSTATE_SUPPORT_DISALLOW)
338 msg_id = VBIOSSMC_MSG_DisallowZstatesEntry;
339 else
340 msg_id = VBIOSSMC_MSG_AllowZstatesEntry;
341
342 dcn31_smu_send_msg_with_param(
343 clk_mgr,
344 msg_id,
345 param);
346
347 }
348
349 /* Arg = 1: Turn DTB on; 0: Turn DTB CLK OFF. when it is on, it is 600MHZ */
dcn31_smu_set_dtbclk(struct clk_mgr_internal * clk_mgr,bool enable)350 void dcn31_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable)
351 {
352 if (!clk_mgr->smu_present)
353 return;
354
355 dcn31_smu_send_msg_with_param(
356 clk_mgr,
357 VBIOSSMC_MSG_SetDtbClk,
358 enable);
359 }
360