1 /*
2  * Copyright 2021 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 "dcn316_smu.h"
31 #include "mp/mp_13_0_8_offset.h"
32 #include "mp/mp_13_0_8_sh_mask.h"
33 
34 #define MAX_INSTANCE                                        7
35 #define MAX_SEGMENT                                         6
36 
37 struct IP_BASE_INSTANCE
38 {
39     unsigned int segment[MAX_SEGMENT];
40 };
41 
42 struct IP_BASE
43 {
44     struct IP_BASE_INSTANCE instance[MAX_INSTANCE];
45 };
46 
47 static const struct IP_BASE MP0_BASE = { { { { 0x00016000, 0x00DC0000, 0x00E00000, 0x00E40000, 0x0243FC00, 0 } },
48                                         { { 0, 0, 0, 0, 0, 0 } },
49                                         { { 0, 0, 0, 0, 0, 0 } },
50                                         { { 0, 0, 0, 0, 0, 0 } },
51                                         { { 0, 0, 0, 0, 0, 0 } },
52                                         { { 0, 0, 0, 0, 0, 0 } },
53                                         { { 0, 0, 0, 0, 0, 0 } } } };
54 
55 #define REG(reg_name) \
56 	(MP0_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name)
57 
58 #define FN(reg_name, field) \
59 	FD(reg_name##__##field)
60 
61 #include "logger_types.h"
62 #undef DC_LOGGER
63 #define DC_LOGGER \
64 	CTX->logger
65 #define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); }
66 
67 #define VBIOSSMC_MSG_TestMessage                  0x01 ///< To check if PMFW is alive and responding. Requirement specified by PMFW team
68 #define VBIOSSMC_MSG_GetPmfwVersion               0x02 ///< Get PMFW version
69 #define VBIOSSMC_MSG_Spare0                       0x03 ///< Spare0
70 #define VBIOSSMC_MSG_SetDispclkFreq               0x04 ///< Set display clock frequency in MHZ
71 #define VBIOSSMC_MSG_Spare1                       0x05 ///< Spare1
72 #define VBIOSSMC_MSG_SetDppclkFreq                0x06 ///< Set DPP clock frequency in MHZ
73 #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq       0x07 ///< Set DCF clock frequency hard min in MHZ
74 #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk        0x08 ///< Set DCF clock minimum frequency in deep sleep in MHZ
75 #define VBIOSSMC_MSG_SetPhyclkVoltageByFreq       0x09 ///< Set display phy clock frequency in MHZ in case VMIN does not support phy frequency
76 #define VBIOSSMC_MSG_GetFclkFrequency             0x0A ///< Get FCLK frequency, return frequemcy in MHZ
77 #define VBIOSSMC_MSG_SetDisplayCount              0x0B ///< Inform PMFW of number of display connected
78 #define VBIOSSMC_MSG_SPARE						  0x0C ///< SPARE
79 #define VBIOSSMC_MSG_UpdatePmeRestore             0x0D ///< To ask PMFW to write into Azalia for PME wake up event
80 #define VBIOSSMC_MSG_SetVbiosDramAddrHigh         0x0E ///< Set DRAM address high 32 bits for WM table transfer
81 #define VBIOSSMC_MSG_SetVbiosDramAddrLow          0x0F ///< Set DRAM address low 32 bits for WM table transfer
82 #define VBIOSSMC_MSG_TransferTableSmu2Dram        0x10 ///< Transfer table from PMFW SRAM to system DRAM
83 #define VBIOSSMC_MSG_TransferTableDram2Smu        0x11 ///< Transfer table from system DRAM to PMFW
84 #define VBIOSSMC_MSG_SetDisplayIdleOptimizations  0x12 ///< Set Idle state optimization for display off
85 #define VBIOSSMC_MSG_GetDprefclkFreq              0x13 ///< Get DPREF clock frequency. Return in MHZ
86 #define VBIOSSMC_MSG_GetDtbclkFreq                0x14 ///< Get DPREF clock frequency. Return in MHZ
87 #define VBIOSSMC_MSG_SetDtbclkFreq                0x15 ///< Inform PMFW to turn on/off DTB clock arg = 1, turn DTB clock on 600MHz/ arg = 0 turn DTB clock off
88 #define VBIOSSMC_Message_Count                    0x16 ///< Total number of VBIS and DAL messages
89 
90 #define VBIOSSMC_Status_BUSY                      0x0
91 #define VBIOSSMC_Result_OK                        0x01 ///< Message Response OK
92 #define VBIOSSMC_Result_Failed                    0xFF ///< Message Response Failed
93 #define VBIOSSMC_Result_UnknownCmd                0xFE ///< Message Response Unknown Command
94 #define VBIOSSMC_Result_CmdRejectedPrereq         0xFD ///< Message Response Command Failed Prerequisite
95 #define VBIOSSMC_Result_CmdRejectedBusy           0xFC ///< Message Response Command Rejected due to PMFW is busy. Sender should retry sending this message
96 
97 /*
98  * Function to be used instead of REG_WAIT macro because the wait ends when
99  * the register is NOT EQUAL to zero, and because the translation in msg_if.h
100  * won't work with REG_WAIT.
101  */
102 static uint32_t dcn316_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries)
103 {
104 	uint32_t res_val = VBIOSSMC_Status_BUSY;
105 
106 	do {
107 		res_val = REG_READ(MP1_SMN_C2PMSG_91);
108 		if (res_val != VBIOSSMC_Status_BUSY)
109 			break;
110 
111 		if (delay_us >= 1000)
112 			msleep(delay_us/1000);
113 		else if (delay_us > 0)
114 			udelay(delay_us);
115 	} while (max_retries--);
116 
117 	return res_val;
118 }
119 
120 static int dcn316_smu_send_msg_with_param(
121 		struct clk_mgr_internal *clk_mgr,
122 		unsigned int msg_id, unsigned int param)
123 {
124 	uint32_t result;
125 
126 	result = dcn316_smu_wait_for_response(clk_mgr, 10, 200000);
127 
128 	if (result != VBIOSSMC_Result_OK)
129 		smu_print("SMU Response was not OK. SMU response after wait received is: %d\n", result);
130 
131 	if (result == VBIOSSMC_Status_BUSY) {
132 		return -1;
133 	}
134 
135 	/* First clear response register */
136 	REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY);
137 
138 	/* Set the parameter register for the SMU message, unit is Mhz */
139 	REG_WRITE(MP1_SMN_C2PMSG_83, param);
140 
141 	/* Trigger the message transaction by writing the message ID */
142 	REG_WRITE(MP1_SMN_C2PMSG_67, msg_id);
143 
144 	result = dcn316_smu_wait_for_response(clk_mgr, 10, 200000);
145 
146 	if (result == VBIOSSMC_Status_BUSY) {
147 		ASSERT(0);
148 		dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000);
149 	}
150 
151 	return REG_READ(MP1_SMN_C2PMSG_83);
152 }
153 
154 int dcn316_smu_get_smu_version(struct clk_mgr_internal *clk_mgr)
155 {
156 	return dcn316_smu_send_msg_with_param(
157 			clk_mgr,
158 			VBIOSSMC_MSG_GetPmfwVersion,
159 			0);
160 }
161 
162 
163 int dcn316_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz)
164 {
165 	int actual_dispclk_set_mhz = -1;
166 
167 	if (!clk_mgr->smu_present)
168 		return requested_dispclk_khz;
169 
170 	/*  Unit of SMU msg parameter is Mhz */
171 	actual_dispclk_set_mhz = dcn316_smu_send_msg_with_param(
172 			clk_mgr,
173 			VBIOSSMC_MSG_SetDispclkFreq,
174 			khz_to_mhz_ceil(requested_dispclk_khz));
175 
176 	return actual_dispclk_set_mhz * 1000;
177 }
178 
179 int dcn316_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz)
180 {
181 	int actual_dcfclk_set_mhz = -1;
182 
183 	if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
184 		return -1;
185 
186 	if (!clk_mgr->smu_present)
187 		return requested_dcfclk_khz;
188 
189 	actual_dcfclk_set_mhz = dcn316_smu_send_msg_with_param(
190 			clk_mgr,
191 			VBIOSSMC_MSG_SetHardMinDcfclkByFreq,
192 			khz_to_mhz_ceil(requested_dcfclk_khz));
193 
194 #ifdef DBG
195 	smu_print("actual_dcfclk_set_mhz %d is set to : %d\n", actual_dcfclk_set_mhz, actual_dcfclk_set_mhz * 1000);
196 #endif
197 
198 	return actual_dcfclk_set_mhz * 1000;
199 }
200 
201 int dcn316_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz)
202 {
203 	int actual_min_ds_dcfclk_mhz = -1;
204 
205 	if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
206 		return -1;
207 
208 	if (!clk_mgr->smu_present)
209 		return requested_min_ds_dcfclk_khz;
210 
211 	actual_min_ds_dcfclk_mhz = dcn316_smu_send_msg_with_param(
212 			clk_mgr,
213 			VBIOSSMC_MSG_SetMinDeepSleepDcfclk,
214 			khz_to_mhz_ceil(requested_min_ds_dcfclk_khz));
215 
216 	return actual_min_ds_dcfclk_mhz * 1000;
217 }
218 
219 int dcn316_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz)
220 {
221 	int actual_dppclk_set_mhz = -1;
222 
223 	if (!clk_mgr->smu_present)
224 		return requested_dpp_khz;
225 
226 	actual_dppclk_set_mhz = dcn316_smu_send_msg_with_param(
227 			clk_mgr,
228 			VBIOSSMC_MSG_SetDppclkFreq,
229 			khz_to_mhz_ceil(requested_dpp_khz));
230 
231 	return actual_dppclk_set_mhz * 1000;
232 }
233 
234 void dcn316_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info)
235 {
236 	if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
237 		return;
238 
239 	if (!clk_mgr->smu_present)
240 		return;
241 
242 	//TODO: Work with smu team to define optimization options.
243 	dcn316_smu_send_msg_with_param(
244 		clk_mgr,
245 		VBIOSSMC_MSG_SetDisplayIdleOptimizations,
246 		idle_info);
247 }
248 
249 void dcn316_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable)
250 {
251 	union display_idle_optimization_u idle_info = { 0 };
252 
253 	if (!clk_mgr->smu_present)
254 		return;
255 
256 	if (enable) {
257 		idle_info.idle_info.df_request_disabled = 1;
258 		idle_info.idle_info.phy_ref_clk_off = 1;
259 	}
260 
261 	dcn316_smu_send_msg_with_param(
262 			clk_mgr,
263 			VBIOSSMC_MSG_SetDisplayIdleOptimizations,
264 			idle_info.data);
265 }
266 
267 void dcn316_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high)
268 {
269 	if (!clk_mgr->smu_present)
270 		return;
271 
272 	dcn316_smu_send_msg_with_param(clk_mgr,
273 			VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high);
274 }
275 
276 void dcn316_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low)
277 {
278 	if (!clk_mgr->smu_present)
279 		return;
280 
281 	dcn316_smu_send_msg_with_param(clk_mgr,
282 			VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low);
283 }
284 
285 void dcn316_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr)
286 {
287 	if (!clk_mgr->smu_present)
288 		return;
289 
290 	dcn316_smu_send_msg_with_param(clk_mgr,
291 			VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS);
292 }
293 
294 void dcn316_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr)
295 {
296 	if (!clk_mgr->smu_present)
297 		return;
298 
299 	dcn316_smu_send_msg_with_param(clk_mgr,
300 			VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS);
301 }
302 
303 void dcn316_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr)
304 {
305 	if (!clk_mgr->smu_present)
306 		return;
307 
308 	dcn316_smu_send_msg_with_param(
309 			clk_mgr,
310 			VBIOSSMC_MSG_UpdatePmeRestore,
311 			0);
312 }
313 
314 /* Arg = 1: Turn DTB on; 0: Turn DTB CLK OFF. when it is on, it is 600MHZ */
315 void dcn316_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable)
316 {
317 	if (!clk_mgr->smu_present)
318 		return;
319 
320 	dcn316_smu_send_msg_with_param(
321 			clk_mgr,
322 			VBIOSSMC_MSG_SetDtbclkFreq,
323 			enable);
324 }
325 
326 int dcn316_smu_get_dpref_clk(struct clk_mgr_internal *clk_mgr)
327 {
328 	int dprefclk_get_mhz = -1;
329 
330 	if (clk_mgr->smu_present) {
331 		dprefclk_get_mhz = dcn316_smu_send_msg_with_param(
332 			clk_mgr,
333 			VBIOSSMC_MSG_GetDprefclkFreq,
334 			0);
335 	}
336 	return (dprefclk_get_mhz * 1000);
337 }
338 
339 int dcn316_smu_get_smu_fclk(struct clk_mgr_internal *clk_mgr)
340 {
341 	int fclk_get_mhz = -1;
342 
343 	if (clk_mgr->smu_present) {
344 		fclk_get_mhz = dcn316_smu_send_msg_with_param(
345 			clk_mgr,
346 			VBIOSSMC_MSG_GetFclkFrequency,
347 			0);
348 	}
349 	return (fclk_get_mhz * 1000);
350 }
351