Line data Source code
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 : */
86 0 : static uint32_t dcn31_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries)
87 : {
88 0 : uint32_t res_val = VBIOSSMC_Status_BUSY;
89 :
90 : do {
91 0 : res_val = REG_READ(MP1_SMN_C2PMSG_91);
92 0 : if (res_val != VBIOSSMC_Status_BUSY)
93 : break;
94 :
95 0 : if (delay_us >= 1000)
96 0 : msleep(delay_us/1000);
97 0 : else if (delay_us > 0)
98 0 : udelay(delay_us);
99 0 : } while (max_retries--);
100 :
101 0 : return res_val;
102 : }
103 :
104 0 : 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 0 : 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 0 : if (result == VBIOSSMC_Status_BUSY) {
116 : return -1;
117 : }
118 :
119 : /* First clear response register */
120 0 : REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY);
121 :
122 : /* Set the parameter register for the SMU message, unit is Mhz */
123 0 : REG_WRITE(MP1_SMN_C2PMSG_83, param);
124 :
125 : /* Trigger the message transaction by writing the message ID */
126 0 : REG_WRITE(MP1_SMN_C2PMSG_67, msg_id);
127 :
128 0 : result = dcn31_smu_wait_for_response(clk_mgr, 10, 200000);
129 :
130 0 : if (result == VBIOSSMC_Result_Failed) {
131 0 : if (msg_id == VBIOSSMC_MSG_TransferTableDram2Smu &&
132 0 : param == TABLE_WATERMARKS)
133 0 : DC_LOG_WARNING("Watermarks table not configured properly by SMU");
134 : else
135 0 : ASSERT(0);
136 0 : REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Result_OK);
137 0 : return -1;
138 : }
139 :
140 0 : if (IS_SMU_TIMEOUT(result)) {
141 0 : ASSERT(0);
142 0 : dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000);
143 : }
144 :
145 0 : return REG_READ(MP1_SMN_C2PMSG_83);
146 : }
147 :
148 0 : int dcn31_smu_get_smu_version(struct clk_mgr_internal *clk_mgr)
149 : {
150 0 : return dcn31_smu_send_msg_with_param(
151 : clk_mgr,
152 : VBIOSSMC_MSG_GetSmuVersion,
153 : 0);
154 : }
155 :
156 :
157 0 : int dcn31_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz)
158 : {
159 0 : int actual_dispclk_set_mhz = -1;
160 :
161 0 : if (!clk_mgr->smu_present)
162 : return requested_dispclk_khz;
163 :
164 : /* Unit of SMU msg parameter is Mhz */
165 0 : actual_dispclk_set_mhz = dcn31_smu_send_msg_with_param(
166 : clk_mgr,
167 : VBIOSSMC_MSG_SetDispclkFreq,
168 0 : khz_to_mhz_ceil(requested_dispclk_khz));
169 :
170 0 : return actual_dispclk_set_mhz * 1000;
171 : }
172 :
173 0 : int dcn31_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr)
174 : {
175 0 : int actual_dprefclk_set_mhz = -1;
176 :
177 0 : if (!clk_mgr->smu_present)
178 0 : return clk_mgr->base.dprefclk_khz;
179 :
180 0 : actual_dprefclk_set_mhz = dcn31_smu_send_msg_with_param(
181 : clk_mgr,
182 : VBIOSSMC_MSG_SetDprefclkFreq,
183 0 : 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 0 : return actual_dprefclk_set_mhz * 1000;
188 : }
189 :
190 0 : int dcn31_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz)
191 : {
192 0 : int actual_dcfclk_set_mhz = -1;
193 :
194 0 : if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
195 : return -1;
196 :
197 0 : if (!clk_mgr->smu_present)
198 : return requested_dcfclk_khz;
199 :
200 0 : actual_dcfclk_set_mhz = dcn31_smu_send_msg_with_param(
201 : clk_mgr,
202 : VBIOSSMC_MSG_SetHardMinDcfclkByFreq,
203 0 : 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 0 : return actual_dcfclk_set_mhz * 1000;
210 : }
211 :
212 0 : int dcn31_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz)
213 : {
214 0 : int actual_min_ds_dcfclk_mhz = -1;
215 :
216 0 : if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
217 : return -1;
218 :
219 0 : if (!clk_mgr->smu_present)
220 : return requested_min_ds_dcfclk_khz;
221 :
222 0 : actual_min_ds_dcfclk_mhz = dcn31_smu_send_msg_with_param(
223 : clk_mgr,
224 : VBIOSSMC_MSG_SetMinDeepSleepDcfclk,
225 0 : khz_to_mhz_ceil(requested_min_ds_dcfclk_khz));
226 :
227 0 : return actual_min_ds_dcfclk_mhz * 1000;
228 : }
229 :
230 0 : int dcn31_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz)
231 : {
232 0 : int actual_dppclk_set_mhz = -1;
233 :
234 0 : if (!clk_mgr->smu_present)
235 : return requested_dpp_khz;
236 :
237 0 : actual_dppclk_set_mhz = dcn31_smu_send_msg_with_param(
238 : clk_mgr,
239 : VBIOSSMC_MSG_SetDppclkFreq,
240 0 : khz_to_mhz_ceil(requested_dpp_khz));
241 :
242 0 : return actual_dppclk_set_mhz * 1000;
243 : }
244 :
245 0 : void dcn31_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info)
246 : {
247 0 : if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
248 : return;
249 :
250 0 : if (!clk_mgr->smu_present)
251 : return;
252 :
253 : //TODO: Work with smu team to define optimization options.
254 0 : dcn31_smu_send_msg_with_param(
255 : clk_mgr,
256 : VBIOSSMC_MSG_SetDisplayIdleOptimizations,
257 : idle_info);
258 : }
259 :
260 0 : void dcn31_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable)
261 : {
262 0 : union display_idle_optimization_u idle_info = { 0 };
263 :
264 0 : if (!clk_mgr->smu_present)
265 0 : return;
266 :
267 0 : if (enable) {
268 0 : idle_info.idle_info.df_request_disabled = 1;
269 0 : idle_info.idle_info.phy_ref_clk_off = 1;
270 : }
271 :
272 0 : dcn31_smu_send_msg_with_param(
273 : clk_mgr,
274 : VBIOSSMC_MSG_SetDisplayIdleOptimizations,
275 : idle_info.data);
276 : }
277 :
278 0 : void dcn31_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr)
279 : {
280 0 : if (!clk_mgr->smu_present)
281 : return;
282 :
283 0 : dcn31_smu_send_msg_with_param(
284 : clk_mgr,
285 : VBIOSSMC_MSG_UpdatePmeRestore,
286 : 0);
287 : }
288 :
289 0 : void dcn31_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high)
290 : {
291 0 : if (!clk_mgr->smu_present)
292 : return;
293 :
294 0 : dcn31_smu_send_msg_with_param(clk_mgr,
295 : VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high);
296 : }
297 :
298 0 : void dcn31_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low)
299 : {
300 0 : if (!clk_mgr->smu_present)
301 : return;
302 :
303 0 : dcn31_smu_send_msg_with_param(clk_mgr,
304 : VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low);
305 : }
306 :
307 0 : void dcn31_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr)
308 : {
309 0 : if (!clk_mgr->smu_present)
310 : return;
311 :
312 0 : dcn31_smu_send_msg_with_param(clk_mgr,
313 : VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS);
314 : }
315 :
316 0 : void dcn31_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr)
317 : {
318 0 : if (!clk_mgr->smu_present)
319 : return;
320 :
321 0 : dcn31_smu_send_msg_with_param(clk_mgr,
322 : VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS);
323 : }
324 :
325 0 : 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 0 : if (!clk_mgr->smu_present)
330 : return;
331 :
332 0 : if (!clk_mgr->base.ctx->dc->debug.enable_z9_disable_interface &&
333 : (support == DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY))
334 0 : support = DCN_ZSTATE_SUPPORT_DISALLOW;
335 :
336 :
337 0 : if (support == DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY)
338 : param = 1;
339 : else
340 0 : param = 0;
341 :
342 0 : if (support == DCN_ZSTATE_SUPPORT_DISALLOW)
343 : msg_id = VBIOSSMC_MSG_DisallowZstatesEntry;
344 : else
345 0 : msg_id = VBIOSSMC_MSG_AllowZstatesEntry;
346 :
347 0 : 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 */
355 0 : void dcn31_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable)
356 : {
357 0 : if (!clk_mgr->smu_present)
358 : return;
359 :
360 0 : dcn31_smu_send_msg_with_param(
361 : clk_mgr,
362 : VBIOSSMC_MSG_SetDtbClk,
363 : enable);
364 : }
|