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 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 if (result) { 78 kfree(hwmgr->request_ps); 79 kfree(hwmgr->ps); 80 hwmgr->request_ps = NULL; 81 hwmgr->ps = NULL; 82 return -EINVAL; 83 } 84 85 if (state->classification.flags & PP_StateClassificationFlag_Boot) { 86 hwmgr->boot_ps = state; 87 memcpy(hwmgr->current_ps, state, size); 88 memcpy(hwmgr->request_ps, state, size); 89 } 90 91 state->id = i + 1; /* assigned unique num for every power state id */ 92 93 if (state->classification.flags & PP_StateClassificationFlag_Uvd) 94 hwmgr->uvd_ps = state; 95 state = (struct pp_power_state *)((unsigned long)state + size); 96 } 97 98 return 0; 99 } 100 101 int psm_fini_power_state_table(struct pp_hwmgr *hwmgr) 102 { 103 if (hwmgr == NULL) 104 return -EINVAL; 105 106 if (!hwmgr->ps) 107 return 0; 108 109 kfree(hwmgr->current_ps); 110 kfree(hwmgr->request_ps); 111 kfree(hwmgr->ps); 112 hwmgr->request_ps = NULL; 113 hwmgr->ps = NULL; 114 hwmgr->current_ps = NULL; 115 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 table_entries = hwmgr->num_ps; 127 state = hwmgr->ps; 128 129 for (i = 0; i < table_entries; i++) { 130 if (state->classification.ui_label & ui_label) { 131 *state_id = state->id; 132 return 0; 133 } 134 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 table_entries = hwmgr->num_ps; 148 state = hwmgr->ps; 149 150 for (i = 0; i < table_entries; i++) { 151 if (state->classification.flags & flag) { 152 *state_id = state->id; 153 return 0; 154 } 155 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size); 156 } 157 return -EINVAL; 158 } 159 160 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 table_entries = hwmgr->num_ps; 167 168 state = hwmgr->ps; 169 170 for (i = 0; i < table_entries; i++) { 171 if (state->id == state_id) { 172 memcpy(hwmgr->request_ps, state, hwmgr->ps_size); 173 return 0; 174 } 175 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size); 176 } 177 return -EINVAL; 178 } 179 180 int psm_set_boot_states(struct pp_hwmgr *hwmgr) 181 { 182 unsigned long state_id; 183 int ret = -EINVAL; 184 185 if (!hwmgr->ps) 186 return 0; 187 188 if (!psm_get_state_by_classification(hwmgr, PP_StateClassificationFlag_Boot, 189 &state_id)) 190 ret = psm_set_states(hwmgr, state_id); 191 192 return ret; 193 } 194 195 int psm_set_performance_states(struct pp_hwmgr *hwmgr) 196 { 197 unsigned long state_id; 198 int ret = -EINVAL; 199 200 if (!hwmgr->ps) 201 return 0; 202 203 if (!psm_get_ui_state(hwmgr, PP_StateUILabel_Performance, 204 &state_id)) 205 ret = psm_set_states(hwmgr, state_id); 206 207 return ret; 208 } 209 210 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 if (!hwmgr->ps) 218 return 0; 219 220 table_entries = hwmgr->num_ps; 221 *state = hwmgr->ps; 222 223 restart_search: 224 for (i = 0; i < table_entries; i++) { 225 if ((*state)->classification.ui_label & label_id) 226 return 0; 227 *state = (struct pp_power_state *)((uintptr_t)*state + hwmgr->ps_size); 228 } 229 230 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 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 if (new_ps != NULL) 249 requested = new_ps; 250 else 251 requested = hwmgr->request_ps; 252 253 pcurrent = hwmgr->current_ps; 254 255 phm_apply_state_adjust_rules(hwmgr, requested, pcurrent); 256 if (pcurrent == NULL || (0 != phm_check_states_equal(hwmgr, 257 &pcurrent->hardware, &requested->hardware, &equal))) 258 equal = false; 259 260 if (!equal || phm_check_smc_update_required_for_display_configuration(hwmgr)) { 261 phm_set_power_state(hwmgr, &pcurrent->hardware, &requested->hardware); 262 memcpy(hwmgr->current_ps, hwmgr->request_ps, hwmgr->ps_size); 263 } 264 } 265 266 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 if (hwmgr->not_vf) { 273 if (!skip_display_settings) 274 phm_display_configuration_changed(hwmgr); 275 276 if (hwmgr->ps) 277 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 phm_apply_clock_adjust_rules(hwmgr); 284 285 if (!skip_display_settings) 286 phm_notify_smc_display_config_after_ps_adjustment(hwmgr); 287 } 288 289 if (!phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level)) 290 hwmgr->dpm_level = hwmgr->request_dpm_level; 291 292 if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) { 293 index = fls(hwmgr->workload_mask); 294 index = index > 0 && index <= Workload_Policy_Max ? index - 1 : 0; 295 workload = hwmgr->workload_setting[index]; 296 297 if (hwmgr->power_profile_mode != workload && hwmgr->hwmgr_func->set_power_profile_mode) 298 hwmgr->hwmgr_func->set_power_profile_mode(hwmgr, &workload, 0); 299 } 300 301 return 0; 302 } 303 304