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