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