0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include <linux/types.h>
0025 #include <linux/kernel.h>
0026 #include <linux/slab.h>
0027 #include "pp_psm.h"
0028
0029 int psm_init_power_state_table(struct pp_hwmgr *hwmgr)
0030 {
0031 int result;
0032 unsigned int i;
0033 unsigned int table_entries;
0034 struct pp_power_state *state;
0035 int size;
0036
0037 if (hwmgr->hwmgr_func->get_num_of_pp_table_entries == NULL)
0038 return 0;
0039
0040 if (hwmgr->hwmgr_func->get_power_state_size == NULL)
0041 return 0;
0042
0043 hwmgr->num_ps = table_entries = hwmgr->hwmgr_func->get_num_of_pp_table_entries(hwmgr);
0044
0045 hwmgr->ps_size = size = hwmgr->hwmgr_func->get_power_state_size(hwmgr) +
0046 sizeof(struct pp_power_state);
0047
0048 if (table_entries == 0 || size == 0) {
0049 pr_warn("Please check whether power state management is supported on this asic\n");
0050 return 0;
0051 }
0052
0053 hwmgr->ps = kcalloc(table_entries, size, GFP_KERNEL);
0054 if (hwmgr->ps == NULL)
0055 return -ENOMEM;
0056
0057 hwmgr->request_ps = kzalloc(size, GFP_KERNEL);
0058 if (hwmgr->request_ps == NULL) {
0059 kfree(hwmgr->ps);
0060 hwmgr->ps = NULL;
0061 return -ENOMEM;
0062 }
0063
0064 hwmgr->current_ps = kzalloc(size, GFP_KERNEL);
0065 if (hwmgr->current_ps == NULL) {
0066 kfree(hwmgr->request_ps);
0067 kfree(hwmgr->ps);
0068 hwmgr->request_ps = NULL;
0069 hwmgr->ps = NULL;
0070 return -ENOMEM;
0071 }
0072
0073 state = hwmgr->ps;
0074
0075 for (i = 0; i < table_entries; i++) {
0076 result = hwmgr->hwmgr_func->get_pp_table_entry(hwmgr, i, state);
0077 if (result) {
0078 kfree(hwmgr->request_ps);
0079 kfree(hwmgr->ps);
0080 hwmgr->request_ps = NULL;
0081 hwmgr->ps = NULL;
0082 return -EINVAL;
0083 }
0084
0085 if (state->classification.flags & PP_StateClassificationFlag_Boot) {
0086 hwmgr->boot_ps = state;
0087 memcpy(hwmgr->current_ps, state, size);
0088 memcpy(hwmgr->request_ps, state, size);
0089 }
0090
0091 state->id = i + 1;
0092
0093 if (state->classification.flags & PP_StateClassificationFlag_Uvd)
0094 hwmgr->uvd_ps = state;
0095 state = (struct pp_power_state *)((unsigned long)state + size);
0096 }
0097
0098 return 0;
0099 }
0100
0101 int psm_fini_power_state_table(struct pp_hwmgr *hwmgr)
0102 {
0103 if (hwmgr == NULL)
0104 return -EINVAL;
0105
0106 if (!hwmgr->ps)
0107 return 0;
0108
0109 kfree(hwmgr->current_ps);
0110 kfree(hwmgr->request_ps);
0111 kfree(hwmgr->ps);
0112 hwmgr->request_ps = NULL;
0113 hwmgr->ps = NULL;
0114 hwmgr->current_ps = NULL;
0115 return 0;
0116 }
0117
0118 static int psm_get_ui_state(struct pp_hwmgr *hwmgr,
0119 enum PP_StateUILabel ui_label,
0120 unsigned long *state_id)
0121 {
0122 struct pp_power_state *state;
0123 int table_entries;
0124 int i;
0125
0126 table_entries = hwmgr->num_ps;
0127 state = hwmgr->ps;
0128
0129 for (i = 0; i < table_entries; i++) {
0130 if (state->classification.ui_label & ui_label) {
0131 *state_id = state->id;
0132 return 0;
0133 }
0134 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
0135 }
0136 return -EINVAL;
0137 }
0138
0139 static int psm_get_state_by_classification(struct pp_hwmgr *hwmgr,
0140 enum PP_StateClassificationFlag flag,
0141 unsigned long *state_id)
0142 {
0143 struct pp_power_state *state;
0144 int table_entries;
0145 int i;
0146
0147 table_entries = hwmgr->num_ps;
0148 state = hwmgr->ps;
0149
0150 for (i = 0; i < table_entries; i++) {
0151 if (state->classification.flags & flag) {
0152 *state_id = state->id;
0153 return 0;
0154 }
0155 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
0156 }
0157 return -EINVAL;
0158 }
0159
0160 static int psm_set_states(struct pp_hwmgr *hwmgr, unsigned long state_id)
0161 {
0162 struct pp_power_state *state;
0163 int table_entries;
0164 int i;
0165
0166 table_entries = hwmgr->num_ps;
0167
0168 state = hwmgr->ps;
0169
0170 for (i = 0; i < table_entries; i++) {
0171 if (state->id == state_id) {
0172 memcpy(hwmgr->request_ps, state, hwmgr->ps_size);
0173 return 0;
0174 }
0175 state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
0176 }
0177 return -EINVAL;
0178 }
0179
0180 int psm_set_boot_states(struct pp_hwmgr *hwmgr)
0181 {
0182 unsigned long state_id;
0183 int ret = -EINVAL;
0184
0185 if (!hwmgr->ps)
0186 return 0;
0187
0188 if (!psm_get_state_by_classification(hwmgr, PP_StateClassificationFlag_Boot,
0189 &state_id))
0190 ret = psm_set_states(hwmgr, state_id);
0191
0192 return ret;
0193 }
0194
0195 int psm_set_performance_states(struct pp_hwmgr *hwmgr)
0196 {
0197 unsigned long state_id;
0198 int ret = -EINVAL;
0199
0200 if (!hwmgr->ps)
0201 return 0;
0202
0203 if (!psm_get_ui_state(hwmgr, PP_StateUILabel_Performance,
0204 &state_id))
0205 ret = psm_set_states(hwmgr, state_id);
0206
0207 return ret;
0208 }
0209
0210 int psm_set_user_performance_state(struct pp_hwmgr *hwmgr,
0211 enum PP_StateUILabel label_id,
0212 struct pp_power_state **state)
0213 {
0214 int table_entries;
0215 int i;
0216
0217 if (!hwmgr->ps)
0218 return 0;
0219
0220 table_entries = hwmgr->num_ps;
0221 *state = hwmgr->ps;
0222
0223 restart_search:
0224 for (i = 0; i < table_entries; i++) {
0225 if ((*state)->classification.ui_label & label_id)
0226 return 0;
0227 *state = (struct pp_power_state *)((uintptr_t)*state + hwmgr->ps_size);
0228 }
0229
0230 switch (label_id) {
0231 case PP_StateUILabel_Battery:
0232 case PP_StateUILabel_Balanced:
0233 label_id = PP_StateUILabel_Performance;
0234 goto restart_search;
0235 default:
0236 break;
0237 }
0238 return -EINVAL;
0239 }
0240
0241 static void power_state_management(struct pp_hwmgr *hwmgr,
0242 struct pp_power_state *new_ps)
0243 {
0244 struct pp_power_state *pcurrent;
0245 struct pp_power_state *requested;
0246 bool equal;
0247
0248 if (new_ps != NULL)
0249 requested = new_ps;
0250 else
0251 requested = hwmgr->request_ps;
0252
0253 pcurrent = hwmgr->current_ps;
0254
0255 phm_apply_state_adjust_rules(hwmgr, requested, pcurrent);
0256 if (pcurrent == NULL || (0 != phm_check_states_equal(hwmgr,
0257 &pcurrent->hardware, &requested->hardware, &equal)))
0258 equal = false;
0259
0260 if (!equal || phm_check_smc_update_required_for_display_configuration(hwmgr)) {
0261 phm_set_power_state(hwmgr, &pcurrent->hardware, &requested->hardware);
0262 memcpy(hwmgr->current_ps, hwmgr->request_ps, hwmgr->ps_size);
0263 }
0264 }
0265
0266 int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr, bool skip_display_settings,
0267 struct pp_power_state *new_ps)
0268 {
0269 uint32_t index;
0270 long workload;
0271
0272 if (hwmgr->not_vf) {
0273 if (!skip_display_settings)
0274 phm_display_configuration_changed(hwmgr);
0275
0276 if (hwmgr->ps)
0277 power_state_management(hwmgr, new_ps);
0278 else
0279
0280
0281
0282
0283 phm_apply_clock_adjust_rules(hwmgr);
0284
0285 if (!skip_display_settings)
0286 phm_notify_smc_display_config_after_ps_adjustment(hwmgr);
0287 }
0288
0289 if (!phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level))
0290 hwmgr->dpm_level = hwmgr->request_dpm_level;
0291
0292 if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
0293 index = fls(hwmgr->workload_mask);
0294 index = index > 0 && index <= Workload_Policy_Max ? index - 1 : 0;
0295 workload = hwmgr->workload_setting[index];
0296
0297 if (hwmgr->power_profile_mode != workload && hwmgr->hwmgr_func->set_power_profile_mode)
0298 hwmgr->hwmgr_func->set_power_profile_mode(hwmgr, &workload, 0);
0299 }
0300
0301 return 0;
0302 }
0303