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