1 /* $NetBSD: amdgpu_pp_psm.c,v 1.3 2021/12/19 12:21:29 riastradh Exp $ */ 2 3 /* 4 * Copyright 2017 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 */ 25 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: amdgpu_pp_psm.c,v 1.3 2021/12/19 12:21:29 riastradh Exp $"); 28 29 #include <linux/types.h> 30 #include <linux/kernel.h> 31 #include <linux/slab.h> 32 #include "pp_psm.h" 33 34 int psm_init_power_state_table(struct pp_hwmgr *hwmgr) 35 { 36 int result __unused; 37 unsigned int i; 38 unsigned int table_entries; 39 struct pp_power_state *state; 40 int size; 41 42 if (hwmgr->hwmgr_func->get_num_of_pp_table_entries == NULL) 43 return 0; 44 45 if (hwmgr->hwmgr_func->get_power_state_size == NULL) 46 return 0; 47 48 hwmgr->num_ps = table_entries = hwmgr->hwmgr_func->get_num_of_pp_table_entries(hwmgr); 49 50 hwmgr->ps_size = size = hwmgr->hwmgr_func->get_power_state_size(hwmgr) + 51 sizeof(struct pp_power_state); 52 53 if (table_entries == 0 || size == 0) { 54 pr_warn("Please check whether power state management is supported on this asic\n"); 55 return 0; 56 } 57 58 hwmgr->ps = kcalloc(table_entries, size, GFP_KERNEL); 59 if (hwmgr->ps == NULL) 60 return -ENOMEM; 61 62 hwmgr->request_ps = kzalloc(size, GFP_KERNEL); 63 if (hwmgr->request_ps == NULL) { 64 kfree(hwmgr->ps); 65 hwmgr->ps = NULL; 66 return -ENOMEM; 67 } 68 69 hwmgr->current_ps = kzalloc(size, GFP_KERNEL); 70 if (hwmgr->current_ps == NULL) { 71 kfree(hwmgr->request_ps); 72 kfree(hwmgr->ps); 73 hwmgr->request_ps = NULL; 74 hwmgr->ps = NULL; 75 return -ENOMEM; 76 } 77 78 state = hwmgr->ps; 79 80 for (i = 0; i < table_entries; i++) { 81 result = hwmgr->hwmgr_func->get_pp_table_entry(hwmgr, i, state); 82 83 if (state->classification.flags & PP_StateClassificationFlag_Boot) { 84 hwmgr->boot_ps = state; 85 memcpy(hwmgr->current_ps, state, size); 86 memcpy(hwmgr->request_ps, state, size); 87 } 88 89 state->id = i + 1; /* assigned unique num for every power state id */ 90 91 if (state->classification.flags & PP_StateClassificationFlag_Uvd) 92 hwmgr->uvd_ps = state; 93 state = (struct pp_power_state *)((unsigned long)state + size); 94 } 95 96 return 0; 97 } 98 99 int psm_fini_power_state_table(struct pp_hwmgr *hwmgr) 100 { 101 if (hwmgr == NULL) 102 return -EINVAL; 103 104 if (!hwmgr->ps) 105 return 0; 106 107 kfree(hwmgr->current_ps); 108 kfree(hwmgr->request_ps); 109 kfree(hwmgr->ps); 110 hwmgr->request_ps = NULL; 111 hwmgr->ps = NULL; 112 hwmgr->current_ps = NULL; 113 return 0; 114 } 115 116 static int psm_get_ui_state(struct pp_hwmgr *hwmgr, 117 enum PP_StateUILabel ui_label, 118 unsigned long *state_id) 119 { 120 struct pp_power_state *state; 121 int table_entries; 122 int i; 123 124 table_entries = hwmgr->num_ps; 125 state = hwmgr->ps; 126 127 for (i = 0; i < table_entries; i++) { 128 if (state->classification.ui_label & ui_label) { 129 *state_id = state->id; 130 return 0; 131 } 132 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size); 133 } 134 return -EINVAL; 135 } 136 137 static int psm_get_state_by_classification(struct pp_hwmgr *hwmgr, 138 enum PP_StateClassificationFlag flag, 139 unsigned long *state_id) 140 { 141 struct pp_power_state *state; 142 int table_entries; 143 int i; 144 145 table_entries = hwmgr->num_ps; 146 state = hwmgr->ps; 147 148 for (i = 0; i < table_entries; i++) { 149 if (state->classification.flags & flag) { 150 *state_id = state->id; 151 return 0; 152 } 153 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size); 154 } 155 return -EINVAL; 156 } 157 158 static int psm_set_states(struct pp_hwmgr *hwmgr, unsigned long state_id) 159 { 160 struct pp_power_state *state; 161 int table_entries; 162 int i; 163 164 table_entries = hwmgr->num_ps; 165 166 state = hwmgr->ps; 167 168 for (i = 0; i < table_entries; i++) { 169 if (state->id == state_id) { 170 memcpy(hwmgr->request_ps, state, hwmgr->ps_size); 171 return 0; 172 } 173 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size); 174 } 175 return -EINVAL; 176 } 177 178 int psm_set_boot_states(struct pp_hwmgr *hwmgr) 179 { 180 unsigned long state_id; 181 int ret = -EINVAL; 182 183 if (!hwmgr->ps) 184 return 0; 185 186 if (!psm_get_state_by_classification(hwmgr, PP_StateClassificationFlag_Boot, 187 &state_id)) 188 ret = psm_set_states(hwmgr, state_id); 189 190 return ret; 191 } 192 193 int psm_set_performance_states(struct pp_hwmgr *hwmgr) 194 { 195 unsigned long state_id; 196 int ret = -EINVAL; 197 198 if (!hwmgr->ps) 199 return 0; 200 201 if (!psm_get_ui_state(hwmgr, PP_StateUILabel_Performance, 202 &state_id)) 203 ret = psm_set_states(hwmgr, state_id); 204 205 return ret; 206 } 207 208 int psm_set_user_performance_state(struct pp_hwmgr *hwmgr, 209 enum PP_StateUILabel label_id, 210 struct pp_power_state **state) 211 { 212 int table_entries; 213 int i; 214 215 if (!hwmgr->ps) 216 return 0; 217 218 table_entries = hwmgr->num_ps; 219 *state = hwmgr->ps; 220 221 restart_search: 222 for (i = 0; i < table_entries; i++) { 223 if ((*state)->classification.ui_label & label_id) 224 return 0; 225 *state = (struct pp_power_state *)((uintptr_t)*state + hwmgr->ps_size); 226 } 227 228 switch (label_id) { 229 case PP_StateUILabel_Battery: 230 case PP_StateUILabel_Balanced: 231 label_id = PP_StateUILabel_Performance; 232 goto restart_search; 233 default: 234 break; 235 } 236 return -EINVAL; 237 } 238 239 static void power_state_management(struct pp_hwmgr *hwmgr, 240 struct pp_power_state *new_ps) 241 { 242 struct pp_power_state *pcurrent; 243 struct pp_power_state *requested; 244 bool equal; 245 246 if (new_ps != NULL) 247 requested = new_ps; 248 else 249 requested = hwmgr->request_ps; 250 251 pcurrent = hwmgr->current_ps; 252 253 phm_apply_state_adjust_rules(hwmgr, requested, pcurrent); 254 if (pcurrent == NULL || (0 != phm_check_states_equal(hwmgr, 255 &pcurrent->hardware, &requested->hardware, &equal))) 256 equal = false; 257 258 if (!equal || phm_check_smc_update_required_for_display_configuration(hwmgr)) { 259 phm_set_power_state(hwmgr, &pcurrent->hardware, &requested->hardware); 260 memcpy(hwmgr->current_ps, hwmgr->request_ps, hwmgr->ps_size); 261 } 262 } 263 264 int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr, bool skip_display_settings, 265 struct pp_power_state *new_ps) 266 { 267 uint32_t index; 268 long workload; 269 270 if (hwmgr->not_vf) { 271 if (!skip_display_settings) 272 phm_display_configuration_changed(hwmgr); 273 274 if (hwmgr->ps) 275 power_state_management(hwmgr, new_ps); 276 else 277 /* 278 * for vega12/vega20 which does not support power state manager 279 * DAL clock limits should also be honoured 280 */ 281 phm_apply_clock_adjust_rules(hwmgr); 282 283 if (!skip_display_settings) 284 phm_notify_smc_display_config_after_ps_adjustment(hwmgr); 285 } 286 287 if (!phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level)) 288 hwmgr->dpm_level = hwmgr->request_dpm_level; 289 290 if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) { 291 index = fls(hwmgr->workload_mask); 292 index = index > 0 && index <= Workload_Policy_Max ? index - 1 : 0; 293 workload = hwmgr->workload_setting[index]; 294 295 if (hwmgr->power_profile_mode != workload && hwmgr->hwmgr_func->set_power_profile_mode) 296 hwmgr->hwmgr_func->set_power_profile_mode(hwmgr, &workload, 0); 297 } 298 299 return 0; 300 } 301 302