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