Commit | Line | Data |
---|---|---|
3a83e4e6 RL |
1 | /* |
2 | * Copyright 2020 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 <linux/delay.h> | |
30 | ||
31 | #include "dcn301_smu.h" | |
32 | ||
33 | #include "vangogh_ip_offset.h" | |
34 | ||
35 | #include "mp/mp_11_5_0_offset.h" | |
36 | #include "mp/mp_11_5_0_sh_mask.h" | |
37 | ||
38 | #define REG(reg_name) \ | |
39 | (MP0_BASE.instance[0].segment[mm ## reg_name ## _BASE_IDX] + mm ## reg_name) | |
40 | ||
41 | #define FN(reg_name, field) \ | |
42 | FD(reg_name##__##field) | |
43 | ||
44 | #define VBIOSSMC_MSG_GetSmuVersion 0x2 | |
45 | #define VBIOSSMC_MSG_SetDispclkFreq 0x4 | |
46 | #define VBIOSSMC_MSG_SetDprefclkFreq 0x5 | |
47 | #define VBIOSSMC_MSG_SetDppclkFreq 0x6 | |
48 | #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq 0x7 | |
49 | #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk 0x8 | |
50 | //#define VBIOSSMC_MSG_SetPhyclkVoltageByFreq 0xA | |
51 | #define VBIOSSMC_MSG_GetFclkFrequency 0xA | |
52 | //#define VBIOSSMC_MSG_SetDisplayCount 0xC | |
53 | //#define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0xD | |
54 | #define VBIOSSMC_MSG_UpdatePmeRestore 0xD | |
55 | #define VBIOSSMC_MSG_SetVbiosDramAddrHigh 0xE //Used for WM table txfr | |
56 | #define VBIOSSMC_MSG_SetVbiosDramAddrLow 0xF | |
57 | #define VBIOSSMC_MSG_TransferTableSmu2Dram 0x10 | |
58 | #define VBIOSSMC_MSG_TransferTableDram2Smu 0x11 | |
59 | #define VBIOSSMC_MSG_SetDisplayIdleOptimizations 0x12 | |
60 | ||
61 | #define VBIOSSMC_Status_BUSY 0x0 | |
62 | #define VBIOSSMC_Result_OK 0x1 | |
63 | #define VBIOSSMC_Result_Failed 0xFF | |
64 | #define VBIOSSMC_Result_UnknownCmd 0xFE | |
65 | #define VBIOSSMC_Result_CmdRejectedPrereq 0xFD | |
66 | #define VBIOSSMC_Result_CmdRejectedBusy 0xFC | |
67 | ||
68 | /* | |
69 | * Function to be used instead of REG_WAIT macro because the wait ends when | |
70 | * the register is NOT EQUAL to zero, and because the translation in msg_if.h | |
71 | * won't work with REG_WAIT. | |
72 | */ | |
73 | static uint32_t dcn301_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries) | |
74 | { | |
75 | uint32_t res_val = VBIOSSMC_Status_BUSY; | |
76 | ||
77 | do { | |
78 | res_val = REG_READ(MP1_SMN_C2PMSG_91); | |
79 | if (res_val != VBIOSSMC_Status_BUSY) | |
80 | break; | |
81 | ||
82 | if (delay_us >= 1000) | |
83 | msleep(delay_us/1000); | |
84 | else if (delay_us > 0) | |
85 | udelay(delay_us); | |
86 | } while (max_retries--); | |
87 | ||
88 | return res_val; | |
89 | } | |
90 | ||
91 | int dcn301_smu_send_msg_with_param( | |
92 | struct clk_mgr_internal *clk_mgr, | |
93 | unsigned int msg_id, unsigned int param) | |
94 | { | |
95 | uint32_t result; | |
96 | ||
97 | /* First clear response register */ | |
98 | REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY); | |
99 | ||
100 | /* Set the parameter register for the SMU message, unit is Mhz */ | |
101 | REG_WRITE(MP1_SMN_C2PMSG_83, param); | |
102 | ||
103 | /* Trigger the message transaction by writing the message ID */ | |
104 | REG_WRITE(MP1_SMN_C2PMSG_67, msg_id); | |
105 | ||
8d5fdaa6 | 106 | result = dcn301_smu_wait_for_response(clk_mgr, 10, 200000); |
3a83e4e6 RL |
107 | |
108 | ASSERT(result == VBIOSSMC_Result_OK); | |
109 | ||
110 | /* Actual dispclk set is returned in the parameter register */ | |
111 | return REG_READ(MP1_SMN_C2PMSG_83); | |
112 | } | |
113 | ||
114 | int dcn301_smu_get_smu_version(struct clk_mgr_internal *clk_mgr) | |
115 | { | |
73affd30 HW |
116 | int smu_version = dcn301_smu_send_msg_with_param(clk_mgr, |
117 | VBIOSSMC_MSG_GetSmuVersion, | |
118 | 0); | |
119 | ||
120 | DC_LOG_DEBUG("%s %x\n", __func__, smu_version); | |
121 | ||
122 | return smu_version; | |
3a83e4e6 RL |
123 | } |
124 | ||
125 | ||
126 | int dcn301_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz) | |
127 | { | |
128 | int actual_dispclk_set_mhz = -1; | |
129 | ||
73affd30 HW |
130 | DC_LOG_DEBUG("%s(%d)\n", __func__, requested_dispclk_khz); |
131 | ||
3a83e4e6 RL |
132 | /* Unit of SMU msg parameter is Mhz */ |
133 | actual_dispclk_set_mhz = dcn301_smu_send_msg_with_param( | |
134 | clk_mgr, | |
135 | VBIOSSMC_MSG_SetDispclkFreq, | |
136 | requested_dispclk_khz / 1000); | |
137 | ||
138 | return actual_dispclk_set_mhz * 1000; | |
139 | } | |
140 | ||
141 | int dcn301_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr) | |
142 | { | |
143 | int actual_dprefclk_set_mhz = -1; | |
144 | ||
73affd30 HW |
145 | DC_LOG_DEBUG("%s %d\n", __func__, clk_mgr->base.dprefclk_khz / 1000); |
146 | ||
3a83e4e6 RL |
147 | actual_dprefclk_set_mhz = dcn301_smu_send_msg_with_param( |
148 | clk_mgr, | |
149 | VBIOSSMC_MSG_SetDprefclkFreq, | |
150 | clk_mgr->base.dprefclk_khz / 1000); | |
151 | ||
152 | /* TODO: add code for programing DP DTO, currently this is down by command table */ | |
153 | ||
154 | return actual_dprefclk_set_mhz * 1000; | |
155 | } | |
156 | ||
157 | int dcn301_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz) | |
158 | { | |
159 | int actual_dcfclk_set_mhz = -1; | |
160 | ||
73affd30 HW |
161 | DC_LOG_DEBUG("%s(%d)\n", __func__, requested_dcfclk_khz); |
162 | ||
3a83e4e6 RL |
163 | actual_dcfclk_set_mhz = dcn301_smu_send_msg_with_param( |
164 | clk_mgr, | |
165 | VBIOSSMC_MSG_SetHardMinDcfclkByFreq, | |
166 | requested_dcfclk_khz / 1000); | |
167 | ||
168 | return actual_dcfclk_set_mhz * 1000; | |
169 | } | |
170 | ||
171 | int dcn301_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz) | |
172 | { | |
173 | int actual_min_ds_dcfclk_mhz = -1; | |
174 | ||
73affd30 HW |
175 | DC_LOG_DEBUG("%s(%d)\n", __func__, requested_min_ds_dcfclk_khz); |
176 | ||
3a83e4e6 RL |
177 | actual_min_ds_dcfclk_mhz = dcn301_smu_send_msg_with_param( |
178 | clk_mgr, | |
179 | VBIOSSMC_MSG_SetMinDeepSleepDcfclk, | |
180 | requested_min_ds_dcfclk_khz / 1000); | |
181 | ||
182 | return actual_min_ds_dcfclk_mhz * 1000; | |
183 | } | |
184 | ||
185 | int dcn301_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz) | |
186 | { | |
187 | int actual_dppclk_set_mhz = -1; | |
188 | ||
73affd30 HW |
189 | DC_LOG_DEBUG("%s(%d)\n", __func__, requested_dpp_khz); |
190 | ||
3a83e4e6 RL |
191 | actual_dppclk_set_mhz = dcn301_smu_send_msg_with_param( |
192 | clk_mgr, | |
193 | VBIOSSMC_MSG_SetDppclkFreq, | |
194 | requested_dpp_khz / 1000); | |
195 | ||
196 | return actual_dppclk_set_mhz * 1000; | |
197 | } | |
198 | ||
199 | void dcn301_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info) | |
200 | { | |
201 | //TODO: Work with smu team to define optimization options. | |
202 | ||
73affd30 HW |
203 | DC_LOG_DEBUG("%s(%x)\n", __func__, idle_info); |
204 | ||
3a83e4e6 RL |
205 | dcn301_smu_send_msg_with_param( |
206 | clk_mgr, | |
207 | VBIOSSMC_MSG_SetDisplayIdleOptimizations, | |
208 | idle_info); | |
209 | } | |
210 | ||
211 | void dcn301_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable) | |
212 | { | |
213 | union display_idle_optimization_u idle_info = { 0 }; | |
214 | ||
215 | if (enable) { | |
216 | idle_info.idle_info.df_request_disabled = 1; | |
217 | idle_info.idle_info.phy_ref_clk_off = 1; | |
218 | } | |
219 | ||
73affd30 HW |
220 | DC_LOG_DEBUG("%s(%d)\n", __func__, enable); |
221 | ||
3a83e4e6 RL |
222 | dcn301_smu_send_msg_with_param( |
223 | clk_mgr, | |
224 | VBIOSSMC_MSG_SetDisplayIdleOptimizations, | |
225 | idle_info.data); | |
226 | } | |
227 | ||
228 | void dcn301_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr) | |
229 | { | |
230 | dcn301_smu_send_msg_with_param( | |
231 | clk_mgr, | |
232 | VBIOSSMC_MSG_UpdatePmeRestore, | |
233 | 0); | |
234 | } | |
235 | ||
236 | void dcn301_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high) | |
237 | { | |
73affd30 HW |
238 | DC_LOG_DEBUG("%s(%x)\n", __func__, addr_high); |
239 | ||
3a83e4e6 RL |
240 | dcn301_smu_send_msg_with_param(clk_mgr, |
241 | VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high); | |
242 | } | |
243 | ||
244 | void dcn301_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low) | |
245 | { | |
73affd30 HW |
246 | DC_LOG_DEBUG("%s(%x)\n", __func__, addr_low); |
247 | ||
3a83e4e6 RL |
248 | dcn301_smu_send_msg_with_param(clk_mgr, |
249 | VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low); | |
250 | } | |
251 | ||
252 | void dcn301_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr) | |
253 | { | |
254 | dcn301_smu_send_msg_with_param(clk_mgr, | |
255 | VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS); | |
256 | } | |
257 | ||
258 | void dcn301_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr) | |
259 | { | |
260 | dcn301_smu_send_msg_with_param(clk_mgr, | |
261 | VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS); | |
262 | } |