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