1 /*
2 * Copyright 2017 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 */
23
24 #include <linux/types.h>
25 #include <linux/kernel.h>
26 #include <linux/slab.h>
27 #include "pp_psm.h"
28
psm_init_power_state_table(struct pp_hwmgr * hwmgr)29 int psm_init_power_state_table(struct pp_hwmgr *hwmgr)
30 {
31 int result;
32 unsigned int i;
33 struct pp_power_state *state;
34 int size, table_entries;
35
36 if (hwmgr->hwmgr_func->get_num_of_pp_table_entries == NULL)
37 return 0;
38
39 if (hwmgr->hwmgr_func->get_power_state_size == NULL)
40 return 0;
41
42 table_entries = hwmgr->hwmgr_func->get_num_of_pp_table_entries(hwmgr);
43
44 size = hwmgr->hwmgr_func->get_power_state_size(hwmgr) +
45 sizeof(struct pp_power_state);
46
47 if (table_entries <= 0 || size == 0) {
48 pr_warn("Please check whether power state management is supported on this asic\n");
49 hwmgr->num_ps = 0;
50 hwmgr->ps_size = 0;
51 return 0;
52 }
53 hwmgr->num_ps = table_entries;
54 hwmgr->ps_size = size;
55
56 hwmgr->ps = kcalloc(table_entries, size, GFP_KERNEL);
57 if (hwmgr->ps == NULL)
58 return -ENOMEM;
59
60 hwmgr->request_ps = kzalloc(size, GFP_KERNEL);
61 if (hwmgr->request_ps == NULL) {
62 kfree(hwmgr->ps);
63 hwmgr->ps = NULL;
64 return -ENOMEM;
65 }
66
67 hwmgr->current_ps = kzalloc(size, GFP_KERNEL);
68 if (hwmgr->current_ps == NULL) {
69 kfree(hwmgr->request_ps);
70 kfree(hwmgr->ps);
71 hwmgr->request_ps = NULL;
72 hwmgr->ps = NULL;
73 return -ENOMEM;
74 }
75
76 state = hwmgr->ps;
77
78 for (i = 0; i < table_entries; i++) {
79 result = hwmgr->hwmgr_func->get_pp_table_entry(hwmgr, i, state);
80 if (result) {
81 kfree(hwmgr->current_ps);
82 kfree(hwmgr->request_ps);
83 kfree(hwmgr->ps);
84 hwmgr->current_ps = NULL;
85 hwmgr->request_ps = NULL;
86 hwmgr->ps = NULL;
87 return -EINVAL;
88 }
89
90 if (state->classification.flags & PP_StateClassificationFlag_Boot) {
91 hwmgr->boot_ps = state;
92 memcpy(hwmgr->current_ps, state, size);
93 memcpy(hwmgr->request_ps, state, size);
94 }
95
96 state->id = i + 1; /* assigned unique num for every power state id */
97
98 if (state->classification.flags & PP_StateClassificationFlag_Uvd)
99 hwmgr->uvd_ps = state;
100 state = (struct pp_power_state *)((unsigned long)state + size);
101 }
102
103 return 0;
104 }
105
psm_fini_power_state_table(struct pp_hwmgr * hwmgr)106 int psm_fini_power_state_table(struct pp_hwmgr *hwmgr)
107 {
108 if (hwmgr == NULL)
109 return -EINVAL;
110
111 if (!hwmgr->ps)
112 return 0;
113
114 kfree(hwmgr->current_ps);
115 kfree(hwmgr->request_ps);
116 kfree(hwmgr->ps);
117 hwmgr->request_ps = NULL;
118 hwmgr->ps = NULL;
119 hwmgr->current_ps = NULL;
120 return 0;
121 }
122
psm_get_ui_state(struct pp_hwmgr * hwmgr,enum PP_StateUILabel ui_label,unsigned long * state_id)123 static int psm_get_ui_state(struct pp_hwmgr *hwmgr,
124 enum PP_StateUILabel ui_label,
125 unsigned long *state_id)
126 {
127 struct pp_power_state *state;
128 int table_entries;
129 int i;
130
131 table_entries = hwmgr->num_ps;
132 state = hwmgr->ps;
133
134 for (i = 0; i < table_entries; i++) {
135 if (state->classification.ui_label & ui_label) {
136 *state_id = state->id;
137 return 0;
138 }
139 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
140 }
141 return -EINVAL;
142 }
143
psm_get_state_by_classification(struct pp_hwmgr * hwmgr,enum PP_StateClassificationFlag flag,unsigned long * state_id)144 static int psm_get_state_by_classification(struct pp_hwmgr *hwmgr,
145 enum PP_StateClassificationFlag flag,
146 unsigned long *state_id)
147 {
148 struct pp_power_state *state;
149 int table_entries;
150 int i;
151
152 table_entries = hwmgr->num_ps;
153 state = hwmgr->ps;
154
155 for (i = 0; i < table_entries; i++) {
156 if (state->classification.flags & flag) {
157 *state_id = state->id;
158 return 0;
159 }
160 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
161 }
162 return -EINVAL;
163 }
164
psm_set_states(struct pp_hwmgr * hwmgr,unsigned long state_id)165 static int psm_set_states(struct pp_hwmgr *hwmgr, unsigned long state_id)
166 {
167 struct pp_power_state *state;
168 int table_entries;
169 int i;
170
171 table_entries = hwmgr->num_ps;
172
173 state = hwmgr->ps;
174
175 for (i = 0; i < table_entries; i++) {
176 if (state->id == state_id) {
177 memcpy(hwmgr->request_ps, state, hwmgr->ps_size);
178 return 0;
179 }
180 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
181 }
182 return -EINVAL;
183 }
184
psm_set_boot_states(struct pp_hwmgr * hwmgr)185 int psm_set_boot_states(struct pp_hwmgr *hwmgr)
186 {
187 unsigned long state_id;
188 int ret = -EINVAL;
189
190 if (!hwmgr->ps)
191 return 0;
192
193 if (!psm_get_state_by_classification(hwmgr, PP_StateClassificationFlag_Boot,
194 &state_id))
195 ret = psm_set_states(hwmgr, state_id);
196
197 return ret;
198 }
199
psm_set_performance_states(struct pp_hwmgr * hwmgr)200 int psm_set_performance_states(struct pp_hwmgr *hwmgr)
201 {
202 unsigned long state_id;
203 int ret = -EINVAL;
204
205 if (!hwmgr->ps)
206 return 0;
207
208 if (!psm_get_ui_state(hwmgr, PP_StateUILabel_Performance,
209 &state_id))
210 ret = psm_set_states(hwmgr, state_id);
211
212 return ret;
213 }
214
psm_set_user_performance_state(struct pp_hwmgr * hwmgr,enum PP_StateUILabel label_id,struct pp_power_state ** state)215 int psm_set_user_performance_state(struct pp_hwmgr *hwmgr,
216 enum PP_StateUILabel label_id,
217 struct pp_power_state **state)
218 {
219 int table_entries;
220 int i;
221
222 if (!hwmgr->ps)
223 return 0;
224
225 table_entries = hwmgr->num_ps;
226 *state = hwmgr->ps;
227
228 restart_search:
229 for (i = 0; i < table_entries; i++) {
230 if ((*state)->classification.ui_label & label_id)
231 return 0;
232 *state = (struct pp_power_state *)((uintptr_t)*state + hwmgr->ps_size);
233 }
234
235 switch (label_id) {
236 case PP_StateUILabel_Battery:
237 case PP_StateUILabel_Balanced:
238 label_id = PP_StateUILabel_Performance;
239 goto restart_search;
240 default:
241 break;
242 }
243 return -EINVAL;
244 }
245
power_state_management(struct pp_hwmgr * hwmgr,struct pp_power_state * new_ps)246 static void power_state_management(struct pp_hwmgr *hwmgr,
247 struct pp_power_state *new_ps)
248 {
249 struct pp_power_state *pcurrent;
250 struct pp_power_state *requested;
251 bool equal;
252
253 if (new_ps != NULL)
254 requested = new_ps;
255 else
256 requested = hwmgr->request_ps;
257
258 pcurrent = hwmgr->current_ps;
259
260 phm_apply_state_adjust_rules(hwmgr, requested, pcurrent);
261 if (pcurrent == NULL || (0 != phm_check_states_equal(hwmgr,
262 &pcurrent->hardware, &requested->hardware, &equal)))
263 equal = false;
264
265 if (!equal || phm_check_smc_update_required_for_display_configuration(hwmgr)) {
266 phm_set_power_state(hwmgr, &pcurrent->hardware, &requested->hardware);
267 memcpy(hwmgr->current_ps, hwmgr->request_ps, hwmgr->ps_size);
268 }
269 }
270
psm_adjust_power_state_dynamic(struct pp_hwmgr * hwmgr,bool skip_display_settings,struct pp_power_state * new_ps)271 int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr, bool skip_display_settings,
272 struct pp_power_state *new_ps)
273 {
274 uint32_t index;
275 long workload[1];
276
277 if (hwmgr->not_vf) {
278 if (!skip_display_settings)
279 phm_display_configuration_changed(hwmgr);
280
281 if (hwmgr->ps)
282 power_state_management(hwmgr, new_ps);
283 else
284 /*
285 * for vega12/vega20 which does not support power state manager
286 * DAL clock limits should also be honoured
287 */
288 phm_apply_clock_adjust_rules(hwmgr);
289
290 if (!skip_display_settings)
291 phm_notify_smc_display_config_after_ps_adjustment(hwmgr);
292 }
293
294 if (!phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level))
295 hwmgr->dpm_level = hwmgr->request_dpm_level;
296
297 if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
298 index = fls(hwmgr->workload_mask);
299 index = index > 0 && index <= Workload_Policy_Max ? index - 1 : 0;
300 workload[0] = hwmgr->workload_setting[index];
301
302 if (hwmgr->power_profile_mode != workload[0] && hwmgr->hwmgr_func->set_power_profile_mode)
303 hwmgr->hwmgr_func->set_power_profile_mode(hwmgr, workload, 0);
304 }
305
306 return 0;
307 }
308
309