Home | History | Annotate | Line # | Download | only in hwmgr
      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