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