Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2017 Advanced Micro Devices, Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
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; /* assigned unique num for every power state id */
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              * for vega12/vega20 which does not support power state manager
0281              * DAL clock limits should also be honoured
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