Merge branches 'clk-range', 'clk-uniphier', 'clk-apple' and 'clk-qcom' into clk-next
[linux-2.6-microblaze.git] / drivers / gpu / drm / amd / display / dc / clk_mgr / dcn31 / dcn31_smu.c
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 <linux/delay.h>
27 #include "core_types.h"
28 #include "clk_mgr_internal.h"
29 #include "reg_helper.h"
30 #include "dm_helpers.h"
31 #include "dcn31_smu.h"
32
33 #include "yellow_carp_offset.h"
34 #include "mp/mp_13_0_2_offset.h"
35 #include "mp/mp_13_0_2_sh_mask.h"
36
37 #define REG(reg_name) \
38         (MP0_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name)
39
40 #define FN(reg_name, field) \
41         FD(reg_name##__##field)
42
43 #define VBIOSSMC_MSG_TestMessage                  0x1
44 #define VBIOSSMC_MSG_GetSmuVersion                0x2
45 #define VBIOSSMC_MSG_PowerUpGfx                   0x3
46 #define VBIOSSMC_MSG_SetDispclkFreq               0x4
47 #define VBIOSSMC_MSG_SetDprefclkFreq              0x5   //Not used. DPRef is constant
48 #define VBIOSSMC_MSG_SetDppclkFreq                0x6
49 #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq       0x7
50 #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk        0x8
51 #define VBIOSSMC_MSG_SetPhyclkVoltageByFreq       0x9   //Keep it in case VMIN dees not support phy clk
52 #define VBIOSSMC_MSG_GetFclkFrequency             0xA
53 #define VBIOSSMC_MSG_SetDisplayCount              0xB   //Not used anymore
54 #define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0xC   //Not used anymore
55 #define VBIOSSMC_MSG_UpdatePmeRestore             0xD
56 #define VBIOSSMC_MSG_SetVbiosDramAddrHigh         0xE   //Used for WM table txfr
57 #define VBIOSSMC_MSG_SetVbiosDramAddrLow          0xF
58 #define VBIOSSMC_MSG_TransferTableSmu2Dram        0x10
59 #define VBIOSSMC_MSG_TransferTableDram2Smu        0x11
60 #define VBIOSSMC_MSG_SetDisplayIdleOptimizations  0x12
61 #define VBIOSSMC_MSG_GetDprefclkFreq              0x13
62 #define VBIOSSMC_MSG_GetDtbclkFreq                0x14
63 #define VBIOSSMC_MSG_AllowZstatesEntry            0x15
64 #define VBIOSSMC_MSG_DisallowZstatesEntry         0x16
65 #define VBIOSSMC_MSG_SetDtbClk                    0x17
66 #define VBIOSSMC_Message_Count                    0x18
67
68 #define VBIOSSMC_Status_BUSY                      0x0
69 #define VBIOSSMC_Result_OK                        0x1
70 #define VBIOSSMC_Result_Failed                    0xFF
71 #define VBIOSSMC_Result_UnknownCmd                0xFE
72 #define VBIOSSMC_Result_CmdRejectedPrereq         0xFD
73 #define VBIOSSMC_Result_CmdRejectedBusy           0xFC
74
75 /*
76  * Function to be used instead of REG_WAIT macro because the wait ends when
77  * the register is NOT EQUAL to zero, and because the translation in msg_if.h
78  * won't work with REG_WAIT.
79  */
80 static uint32_t dcn31_smu_wait_for_response(struct clk_mgr_internal *clk_mgr, unsigned int delay_us, unsigned int max_retries)
81 {
82         uint32_t res_val = VBIOSSMC_Status_BUSY;
83
84         do {
85                 res_val = REG_READ(MP1_SMN_C2PMSG_91);
86                 if (res_val != VBIOSSMC_Status_BUSY)
87                         break;
88
89                 if (delay_us >= 1000)
90                         msleep(delay_us/1000);
91                 else if (delay_us > 0)
92                         udelay(delay_us);
93         } while (max_retries--);
94
95         return res_val;
96 }
97
98 static int dcn31_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr,
99                                          unsigned int msg_id,
100                                          unsigned int param)
101 {
102         uint32_t result;
103
104         result = dcn31_smu_wait_for_response(clk_mgr, 10, 200000);
105         ASSERT(result == VBIOSSMC_Result_OK);
106
107         if (result == VBIOSSMC_Status_BUSY) {
108                 return -1;
109         }
110
111         /* First clear response register */
112         REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Status_BUSY);
113
114         /* Set the parameter register for the SMU message, unit is Mhz */
115         REG_WRITE(MP1_SMN_C2PMSG_83, param);
116
117         /* Trigger the message transaction by writing the message ID */
118         REG_WRITE(MP1_SMN_C2PMSG_67, msg_id);
119
120         result = dcn31_smu_wait_for_response(clk_mgr, 10, 200000);
121
122         if (result == VBIOSSMC_Result_Failed) {
123                 if (msg_id == VBIOSSMC_MSG_TransferTableDram2Smu &&
124                     param == TABLE_WATERMARKS)
125                         DC_LOG_WARNING("Watermarks table not configured properly by SMU");
126                 else
127                         ASSERT(0);
128                 REG_WRITE(MP1_SMN_C2PMSG_91, VBIOSSMC_Result_OK);
129                 return -1;
130         }
131
132         if (IS_SMU_TIMEOUT(result)) {
133                 ASSERT(0);
134                 dm_helpers_smu_timeout(CTX, msg_id, param, 10 * 200000);
135         }
136
137         return REG_READ(MP1_SMN_C2PMSG_83);
138 }
139
140 int dcn31_smu_get_smu_version(struct clk_mgr_internal *clk_mgr)
141 {
142         return dcn31_smu_send_msg_with_param(
143                         clk_mgr,
144                         VBIOSSMC_MSG_GetSmuVersion,
145                         0);
146 }
147
148
149 int dcn31_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz)
150 {
151         int actual_dispclk_set_mhz = -1;
152
153         if (!clk_mgr->smu_present)
154                 return requested_dispclk_khz;
155
156         /*  Unit of SMU msg parameter is Mhz */
157         actual_dispclk_set_mhz = dcn31_smu_send_msg_with_param(
158                         clk_mgr,
159                         VBIOSSMC_MSG_SetDispclkFreq,
160                         khz_to_mhz_ceil(requested_dispclk_khz));
161
162         return actual_dispclk_set_mhz * 1000;
163 }
164
165 int dcn31_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr)
166 {
167         int actual_dprefclk_set_mhz = -1;
168
169         if (!clk_mgr->smu_present)
170                 return clk_mgr->base.dprefclk_khz;
171
172         actual_dprefclk_set_mhz = dcn31_smu_send_msg_with_param(
173                         clk_mgr,
174                         VBIOSSMC_MSG_SetDprefclkFreq,
175                         khz_to_mhz_ceil(clk_mgr->base.dprefclk_khz));
176
177         /* TODO: add code for programing DP DTO, currently this is down by command table */
178
179         return actual_dprefclk_set_mhz * 1000;
180 }
181
182 int dcn31_smu_set_hard_min_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_dcfclk_khz)
183 {
184         int actual_dcfclk_set_mhz = -1;
185
186         if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
187                 return -1;
188
189         if (!clk_mgr->smu_present)
190                 return requested_dcfclk_khz;
191
192         actual_dcfclk_set_mhz = dcn31_smu_send_msg_with_param(
193                         clk_mgr,
194                         VBIOSSMC_MSG_SetHardMinDcfclkByFreq,
195                         khz_to_mhz_ceil(requested_dcfclk_khz));
196
197         return actual_dcfclk_set_mhz * 1000;
198 }
199
200 int dcn31_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, int requested_min_ds_dcfclk_khz)
201 {
202         int actual_min_ds_dcfclk_mhz = -1;
203
204         if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
205                 return -1;
206
207         if (!clk_mgr->smu_present)
208                 return requested_min_ds_dcfclk_khz;
209
210         actual_min_ds_dcfclk_mhz = dcn31_smu_send_msg_with_param(
211                         clk_mgr,
212                         VBIOSSMC_MSG_SetMinDeepSleepDcfclk,
213                         khz_to_mhz_ceil(requested_min_ds_dcfclk_khz));
214
215         return actual_min_ds_dcfclk_mhz * 1000;
216 }
217
218 int dcn31_smu_set_dppclk(struct clk_mgr_internal *clk_mgr, int requested_dpp_khz)
219 {
220         int actual_dppclk_set_mhz = -1;
221
222         if (!clk_mgr->smu_present)
223                 return requested_dpp_khz;
224
225         actual_dppclk_set_mhz = dcn31_smu_send_msg_with_param(
226                         clk_mgr,
227                         VBIOSSMC_MSG_SetDppclkFreq,
228                         khz_to_mhz_ceil(requested_dpp_khz));
229
230         return actual_dppclk_set_mhz * 1000;
231 }
232
233 void dcn31_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, uint32_t idle_info)
234 {
235         if (!clk_mgr->base.ctx->dc->debug.pstate_enabled)
236                 return;
237
238         if (!clk_mgr->smu_present)
239                 return;
240
241         //TODO: Work with smu team to define optimization options.
242         dcn31_smu_send_msg_with_param(
243                 clk_mgr,
244                 VBIOSSMC_MSG_SetDisplayIdleOptimizations,
245                 idle_info);
246 }
247
248 void dcn31_smu_enable_phy_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable)
249 {
250         union display_idle_optimization_u idle_info = { 0 };
251
252         if (!clk_mgr->smu_present)
253                 return;
254
255         if (enable) {
256                 idle_info.idle_info.df_request_disabled = 1;
257                 idle_info.idle_info.phy_ref_clk_off = 1;
258         }
259
260         dcn31_smu_send_msg_with_param(
261                         clk_mgr,
262                         VBIOSSMC_MSG_SetDisplayIdleOptimizations,
263                         idle_info.data);
264 }
265
266 void dcn31_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr)
267 {
268         if (!clk_mgr->smu_present)
269                 return;
270
271         dcn31_smu_send_msg_with_param(
272                         clk_mgr,
273                         VBIOSSMC_MSG_UpdatePmeRestore,
274                         0);
275 }
276
277 void dcn31_smu_set_dram_addr_high(struct clk_mgr_internal *clk_mgr, uint32_t addr_high)
278 {
279         if (!clk_mgr->smu_present)
280                 return;
281
282         dcn31_smu_send_msg_with_param(clk_mgr,
283                         VBIOSSMC_MSG_SetVbiosDramAddrHigh, addr_high);
284 }
285
286 void dcn31_smu_set_dram_addr_low(struct clk_mgr_internal *clk_mgr, uint32_t addr_low)
287 {
288         if (!clk_mgr->smu_present)
289                 return;
290
291         dcn31_smu_send_msg_with_param(clk_mgr,
292                         VBIOSSMC_MSG_SetVbiosDramAddrLow, addr_low);
293 }
294
295 void dcn31_smu_transfer_dpm_table_smu_2_dram(struct clk_mgr_internal *clk_mgr)
296 {
297         if (!clk_mgr->smu_present)
298                 return;
299
300         dcn31_smu_send_msg_with_param(clk_mgr,
301                         VBIOSSMC_MSG_TransferTableSmu2Dram, TABLE_DPMCLOCKS);
302 }
303
304 void dcn31_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr)
305 {
306         if (!clk_mgr->smu_present)
307                 return;
308
309         dcn31_smu_send_msg_with_param(clk_mgr,
310                         VBIOSSMC_MSG_TransferTableDram2Smu, TABLE_WATERMARKS);
311 }
312
313 void dcn31_smu_set_Z9_support(struct clk_mgr_internal *clk_mgr, bool support)
314 {
315         //TODO: Work with smu team to define optimization options.
316         unsigned int msg_id;
317
318         if (!clk_mgr->smu_present)
319                 return;
320
321         if (support)
322                 msg_id = VBIOSSMC_MSG_AllowZstatesEntry;
323         else
324                 msg_id = VBIOSSMC_MSG_DisallowZstatesEntry;
325
326         dcn31_smu_send_msg_with_param(
327                 clk_mgr,
328                 msg_id,
329                 0);
330
331 }
332
333 /* Arg = 1: Turn DTB on; 0: Turn DTB CLK OFF. when it is on, it is 600MHZ */
334 void dcn31_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable)
335 {
336         if (!clk_mgr->smu_present)
337                 return;
338
339         dcn31_smu_send_msg_with_param(
340                         clk_mgr,
341                         VBIOSSMC_MSG_SetDtbClk,
342                         enable);
343 }