File Index
Files and directories
Sign in
FILE

pp_psm.c

File preview with syntax highlighting.

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