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