Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3898ea1 authored by Eric Huang's avatar Eric Huang Committed by Alex Deucher
Browse files

drm/amd/powerplay: add some sysfs interfaces for powerplay.



The new sysfs interfaces:
pp_num_states: Read-only, return the number of all pp states, 0 if powerplay is not available.
pp_cur_state: Read-only, return the index number of current pp state.
pp_force_state: Read-write, to write a power state index will switch to selected state forcedly and
		enable forced state mode, disable forced state mode. such as "echo >...".
pp_table: Read-write, binary output, to be used to read or write the dpm table, the maximum
		file size is 4KB of page size.
pp_dpm_sclk: Read-write, reading will return a dpm levels list, to write an index number will force
		powerplay to set the corresponding dpm level.
pp_dpm_mclk: same as sclk.
pp_dpm_pcie: same as sclk.

And add new setting "manual" to the existing interface power_dpm_force_performance_level.

Reviewed-by: default avatarAlex Deucher <alexander.deucher@amd.com>
Signed-off-by: default avatarEric Huang <JinHuiEric.Huang@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent c7e6be23
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -1503,6 +1503,7 @@ enum amdgpu_dpm_forced_level {
	AMDGPU_DPM_FORCED_LEVEL_AUTO = 0,
	AMDGPU_DPM_FORCED_LEVEL_LOW = 1,
	AMDGPU_DPM_FORCED_LEVEL_HIGH = 2,
	AMDGPU_DPM_FORCED_LEVEL_MANUAL = 3,
};

struct amdgpu_vce_state {
@@ -2014,6 +2015,7 @@ struct amdgpu_device {
	/* powerplay */
	struct amd_powerplay		powerplay;
	bool				pp_enabled;
	bool				pp_force_state_enabled;

	/* dpm */
	struct amdgpu_pm		pm;
@@ -2301,6 +2303,21 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring)
#define amdgpu_dpm_get_performance_level(adev) \
	(adev)->powerplay.pp_funcs->get_performance_level((adev)->powerplay.pp_handle)

#define amdgpu_dpm_get_pp_num_states(adev, data) \
	(adev)->powerplay.pp_funcs->get_pp_num_states((adev)->powerplay.pp_handle, data)

#define amdgpu_dpm_get_pp_table(adev, table) \
	(adev)->powerplay.pp_funcs->get_pp_table((adev)->powerplay.pp_handle, table)

#define amdgpu_dpm_set_pp_table(adev, buf, size) \
	(adev)->powerplay.pp_funcs->set_pp_table((adev)->powerplay.pp_handle, buf, size)

#define amdgpu_dpm_print_clock_levels(adev, type, buf) \
	(adev)->powerplay.pp_funcs->print_clock_levels((adev)->powerplay.pp_handle, type, buf)

#define amdgpu_dpm_force_clock_level(adev, type, level) \
		(adev)->powerplay.pp_funcs->force_clock_level((adev)->powerplay.pp_handle, type, level)

#define amdgpu_dpm_dispatch_task(adev, event_id, input, output)		\
	(adev)->powerplay.pp_funcs->dispatch_tasks((adev)->powerplay.pp_handle, (event_id), (input), (output))

+335 −1
Original line number Diff line number Diff line
@@ -119,7 +119,9 @@ static ssize_t amdgpu_get_dpm_forced_performance_level(struct device *dev,
		level = amdgpu_dpm_get_performance_level(adev);
		return snprintf(buf, PAGE_SIZE, "%s\n",
				(level == AMD_DPM_FORCED_LEVEL_AUTO) ? "auto" :
				(level == AMD_DPM_FORCED_LEVEL_LOW) ? "low" : "high");
				(level == AMD_DPM_FORCED_LEVEL_LOW) ? "low" :
				(level == AMD_DPM_FORCED_LEVEL_HIGH) ? "high" :
				(level == AMD_DPM_FORCED_LEVEL_MANUAL) ? "manual" : "unknown");
	} else {
		enum amdgpu_dpm_forced_level level;

@@ -146,6 +148,8 @@ static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev,
		level = AMDGPU_DPM_FORCED_LEVEL_HIGH;
	} else if (strncmp("auto", buf, strlen("auto")) == 0) {
		level = AMDGPU_DPM_FORCED_LEVEL_AUTO;
	} else if (strncmp("manual", buf, strlen("manual")) == 0) {
		level = AMDGPU_DPM_FORCED_LEVEL_MANUAL;
	} else {
		count = -EINVAL;
		goto fail;
@@ -172,10 +176,293 @@ static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev,
	return count;
}

static ssize_t amdgpu_get_pp_num_states(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	struct pp_states_info data;
	int i, buf_len;

	if (adev->pp_enabled)
		amdgpu_dpm_get_pp_num_states(adev, &data);

	buf_len = snprintf(buf, PAGE_SIZE, "states: %d\n", data.nums);
	for (i = 0; i < data.nums; i++)
		buf_len += snprintf(buf + buf_len, PAGE_SIZE, "%d %s\n", i,
				(data.states[i] == POWER_STATE_TYPE_INTERNAL_BOOT) ? "boot" :
				(data.states[i] == POWER_STATE_TYPE_BATTERY) ? "battery" :
				(data.states[i] == POWER_STATE_TYPE_BALANCED) ? "balanced" :
				(data.states[i] == POWER_STATE_TYPE_PERFORMANCE) ? "performance" : "default");

	return buf_len;
}

static ssize_t amdgpu_get_pp_cur_state(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	struct pp_states_info data;
	enum amd_pm_state_type pm = 0;
	int i = 0;

	if (adev->pp_enabled) {

		pm = amdgpu_dpm_get_current_power_state(adev);
		amdgpu_dpm_get_pp_num_states(adev, &data);

		for (i = 0; i < data.nums; i++) {
			if (pm == data.states[i])
				break;
		}

		if (i == data.nums)
			i = -EINVAL;
	}

	return snprintf(buf, PAGE_SIZE, "%d\n", i);
}

static ssize_t amdgpu_get_pp_force_state(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	struct pp_states_info data;
	enum amd_pm_state_type pm = 0;
	int i;

	if (adev->pp_force_state_enabled && adev->pp_enabled) {
		pm = amdgpu_dpm_get_current_power_state(adev);
		amdgpu_dpm_get_pp_num_states(adev, &data);

		for (i = 0; i < data.nums; i++) {
			if (pm == data.states[i])
				break;
		}

		if (i == data.nums)
			i = -EINVAL;

		return snprintf(buf, PAGE_SIZE, "%d\n", i);

	} else
		return snprintf(buf, PAGE_SIZE, "\n");
}

static ssize_t amdgpu_set_pp_force_state(struct device *dev,
		struct device_attribute *attr,
		const char *buf,
		size_t count)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	enum amd_pm_state_type state = 0;
	long idx;
	int ret;

	if (strlen(buf) == 1)
		adev->pp_force_state_enabled = false;
	else {
		ret = kstrtol(buf, 0, &idx);

		if (ret) {
			count = -EINVAL;
			goto fail;
		}

		if (adev->pp_enabled) {
			struct pp_states_info data;
			amdgpu_dpm_get_pp_num_states(adev, &data);
			state = data.states[idx];
			/* only set user selected power states */
			if (state != POWER_STATE_TYPE_INTERNAL_BOOT &&
				state != POWER_STATE_TYPE_DEFAULT) {
				amdgpu_dpm_dispatch_task(adev,
						AMD_PP_EVENT_ENABLE_USER_STATE, &state, NULL);
				adev->pp_force_state_enabled = true;
			}
		}
	}
fail:
	return count;
}

static ssize_t amdgpu_get_pp_table(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	char *table = NULL;
	int size, i;

	if (adev->pp_enabled)
		size = amdgpu_dpm_get_pp_table(adev, &table);
	else
		return 0;

	if (size >= PAGE_SIZE)
		size = PAGE_SIZE - 1;

	for (i = 0; i < size; i++) {
		sprintf(buf + i, "%02x", table[i]);
	}
	sprintf(buf + i, "\n");

	return size;
}

static ssize_t amdgpu_set_pp_table(struct device *dev,
		struct device_attribute *attr,
		const char *buf,
		size_t count)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;

	if (adev->pp_enabled)
		amdgpu_dpm_set_pp_table(adev, buf, count);

	return count;
}

static ssize_t amdgpu_get_pp_dpm_sclk(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	ssize_t size = 0;

	if (adev->pp_enabled)
		size = amdgpu_dpm_print_clock_levels(adev, PP_SCLK, buf);

	return size;
}

static ssize_t amdgpu_set_pp_dpm_sclk(struct device *dev,
		struct device_attribute *attr,
		const char *buf,
		size_t count)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	int ret;
	long level;

	ret = kstrtol(buf, 0, &level);

	if (ret) {
		count = -EINVAL;
		goto fail;
	}

	if (adev->pp_enabled)
		amdgpu_dpm_force_clock_level(adev, PP_SCLK, level);
fail:
	return count;
}

static ssize_t amdgpu_get_pp_dpm_mclk(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	ssize_t size = 0;

	if (adev->pp_enabled)
		size = amdgpu_dpm_print_clock_levels(adev, PP_MCLK, buf);

	return size;
}

static ssize_t amdgpu_set_pp_dpm_mclk(struct device *dev,
		struct device_attribute *attr,
		const char *buf,
		size_t count)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	int ret;
	long level;

	ret = kstrtol(buf, 0, &level);

	if (ret) {
		count = -EINVAL;
		goto fail;
	}

	if (adev->pp_enabled)
		amdgpu_dpm_force_clock_level(adev, PP_MCLK, level);
fail:
	return count;
}

static ssize_t amdgpu_get_pp_dpm_pcie(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	ssize_t size = 0;

	if (adev->pp_enabled)
		size = amdgpu_dpm_print_clock_levels(adev, PP_PCIE, buf);

	return size;
}

static ssize_t amdgpu_set_pp_dpm_pcie(struct device *dev,
		struct device_attribute *attr,
		const char *buf,
		size_t count)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct amdgpu_device *adev = ddev->dev_private;
	int ret;
	long level;

	ret = kstrtol(buf, 0, &level);

	if (ret) {
		count = -EINVAL;
		goto fail;
	}

	if (adev->pp_enabled)
		amdgpu_dpm_force_clock_level(adev, PP_PCIE, level);
fail:
	return count;
}

static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, amdgpu_get_dpm_state, amdgpu_set_dpm_state);
static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
		   amdgpu_get_dpm_forced_performance_level,
		   amdgpu_set_dpm_forced_performance_level);
static DEVICE_ATTR(pp_num_states, S_IRUGO, amdgpu_get_pp_num_states, NULL);
static DEVICE_ATTR(pp_cur_state, S_IRUGO, amdgpu_get_pp_cur_state, NULL);
static DEVICE_ATTR(pp_force_state, S_IRUGO | S_IWUSR,
		amdgpu_get_pp_force_state,
		amdgpu_set_pp_force_state);
static DEVICE_ATTR(pp_table, S_IRUGO | S_IWUSR,
		amdgpu_get_pp_table,
		amdgpu_set_pp_table);
static DEVICE_ATTR(pp_dpm_sclk, S_IRUGO | S_IWUSR,
		amdgpu_get_pp_dpm_sclk,
		amdgpu_set_pp_dpm_sclk);
static DEVICE_ATTR(pp_dpm_mclk, S_IRUGO | S_IWUSR,
		amdgpu_get_pp_dpm_mclk,
		amdgpu_set_pp_dpm_mclk);
static DEVICE_ATTR(pp_dpm_pcie, S_IRUGO | S_IWUSR,
		amdgpu_get_pp_dpm_pcie,
		amdgpu_set_pp_dpm_pcie);

static ssize_t amdgpu_hwmon_show_temp(struct device *dev,
				      struct device_attribute *attr,
@@ -765,6 +1052,44 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
		DRM_ERROR("failed to create device file for dpm state\n");
		return ret;
	}

	if (adev->pp_enabled) {
		ret = device_create_file(adev->dev, &dev_attr_pp_num_states);
		if (ret) {
			DRM_ERROR("failed to create device file pp_num_states\n");
			return ret;
		}
		ret = device_create_file(adev->dev, &dev_attr_pp_cur_state);
		if (ret) {
			DRM_ERROR("failed to create device file pp_cur_state\n");
			return ret;
		}
		ret = device_create_file(adev->dev, &dev_attr_pp_force_state);
		if (ret) {
			DRM_ERROR("failed to create device file pp_force_state\n");
			return ret;
		}
		ret = device_create_file(adev->dev, &dev_attr_pp_table);
		if (ret) {
			DRM_ERROR("failed to create device file pp_table\n");
			return ret;
		}
		ret = device_create_file(adev->dev, &dev_attr_pp_dpm_sclk);
		if (ret) {
			DRM_ERROR("failed to create device file pp_dpm_sclk\n");
			return ret;
		}
		ret = device_create_file(adev->dev, &dev_attr_pp_dpm_mclk);
		if (ret) {
			DRM_ERROR("failed to create device file pp_dpm_mclk\n");
			return ret;
		}
		ret = device_create_file(adev->dev, &dev_attr_pp_dpm_pcie);
		if (ret) {
			DRM_ERROR("failed to create device file pp_dpm_pcie\n");
			return ret;
		}
	}
	ret = amdgpu_debugfs_pm_init(adev);
	if (ret) {
		DRM_ERROR("Failed to register debugfs file for dpm!\n");
@@ -782,6 +1107,15 @@ void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev)
		hwmon_device_unregister(adev->pm.int_hwmon_dev);
	device_remove_file(adev->dev, &dev_attr_power_dpm_state);
	device_remove_file(adev->dev, &dev_attr_power_dpm_force_performance_level);
	if (adev->pp_enabled) {
		device_remove_file(adev->dev, &dev_attr_pp_num_states);
		device_remove_file(adev->dev, &dev_attr_pp_cur_state);
		device_remove_file(adev->dev, &dev_attr_pp_force_state);
		device_remove_file(adev->dev, &dev_attr_pp_table);
		device_remove_file(adev->dev, &dev_attr_pp_dpm_sclk);
		device_remove_file(adev->dev, &dev_attr_pp_dpm_mclk);
		device_remove_file(adev->dev, &dev_attr_pp_dpm_pcie);
	}
}

void amdgpu_pm_compute_clocks(struct amdgpu_device *adev)
+115 −1
Original line number Diff line number Diff line
@@ -433,6 +433,9 @@ enum amd_pm_state_type pp_dpm_get_current_power_state(void *handle)
	case PP_StateUILabel_Performance:
		return POWER_STATE_TYPE_PERFORMANCE;
	default:
		if (state->classification.flags & PP_StateClassificationFlag_Boot)
			return  POWER_STATE_TYPE_INTERNAL_BOOT;
		else
			return POWER_STATE_TYPE_DEFAULT;
	}
}
@@ -535,6 +538,112 @@ static int pp_dpm_get_temperature(void *handle)
	return hwmgr->hwmgr_func->get_temperature(hwmgr);
}

static int pp_dpm_get_pp_num_states(void *handle,
		struct pp_states_info *data)
{
	struct pp_hwmgr *hwmgr;
	int i;

	if (!handle)
		return -EINVAL;

	hwmgr = ((struct pp_instance *)handle)->hwmgr;

	if (hwmgr == NULL || hwmgr->ps == NULL)
		return -EINVAL;

	data->nums = hwmgr->num_ps;

	for (i = 0; i < hwmgr->num_ps; i++) {
		struct pp_power_state *state = (struct pp_power_state *)
				((unsigned long)hwmgr->ps + i * hwmgr->ps_size);
		switch (state->classification.ui_label) {
		case PP_StateUILabel_Battery:
			data->states[i] = POWER_STATE_TYPE_BATTERY;
			break;
		case PP_StateUILabel_Balanced:
			data->states[i] = POWER_STATE_TYPE_BALANCED;
			break;
		case PP_StateUILabel_Performance:
			data->states[i] = POWER_STATE_TYPE_PERFORMANCE;
			break;
		default:
			if (state->classification.flags & PP_StateClassificationFlag_Boot)
				data->states[i] = POWER_STATE_TYPE_INTERNAL_BOOT;
			else
				data->states[i] = POWER_STATE_TYPE_DEFAULT;
		}
	}

	return 0;
}

static int pp_dpm_get_pp_table(void *handle, char **table)
{
	struct pp_hwmgr *hwmgr;

	if (!handle)
		return -EINVAL;

	hwmgr = ((struct pp_instance *)handle)->hwmgr;

	if (hwmgr == NULL || hwmgr->hwmgr_func == NULL ||
		hwmgr->hwmgr_func->get_pp_table == NULL)
		return -EINVAL;

	return hwmgr->hwmgr_func->get_pp_table(hwmgr, table);
}

static int pp_dpm_set_pp_table(void *handle, const char *buf, size_t size)
{
	struct pp_hwmgr *hwmgr;

	if (!handle)
		return -EINVAL;

	hwmgr = ((struct pp_instance *)handle)->hwmgr;

	if (hwmgr == NULL || hwmgr->hwmgr_func == NULL ||
		hwmgr->hwmgr_func->set_pp_table == NULL)
			return -EINVAL;

	return hwmgr->hwmgr_func->set_pp_table(hwmgr, buf, size);
}

static int pp_dpm_force_clock_level(void *handle,
		enum pp_clock_type type, int level)
{
	struct pp_hwmgr *hwmgr;

	if (!handle)
		return -EINVAL;

	hwmgr = ((struct pp_instance *)handle)->hwmgr;

	if (hwmgr == NULL || hwmgr->hwmgr_func == NULL ||
			hwmgr->hwmgr_func->force_clock_level == NULL)
				return -EINVAL;

	return hwmgr->hwmgr_func->force_clock_level(hwmgr, type, level);
}

static int pp_dpm_print_clock_levels(void *handle,
		enum pp_clock_type type, char *buf)
{
	struct pp_hwmgr *hwmgr;

	if (!handle)
		return -EINVAL;

	hwmgr = ((struct pp_instance *)handle)->hwmgr;

	if (hwmgr == NULL || hwmgr->hwmgr_func == NULL ||
			hwmgr->hwmgr_func->print_clock_levels == NULL)
		return -EINVAL;

	return hwmgr->hwmgr_func->print_clock_levels(hwmgr, type, buf);
}

const struct amd_powerplay_funcs pp_dpm_funcs = {
	.get_temperature = pp_dpm_get_temperature,
	.load_firmware = pp_dpm_load_fw,
@@ -552,6 +661,11 @@ const struct amd_powerplay_funcs pp_dpm_funcs = {
	.get_fan_control_mode = pp_dpm_get_fan_control_mode,
	.set_fan_speed_percent = pp_dpm_set_fan_speed_percent,
	.get_fan_speed_percent = pp_dpm_get_fan_speed_percent,
	.get_pp_num_states = pp_dpm_get_pp_num_states,
	.get_pp_table = pp_dpm_get_pp_table,
	.set_pp_table = pp_dpm_set_pp_table,
	.force_clock_level = pp_dpm_force_clock_level,
	.print_clock_levels = pp_dpm_print_clock_levels,
};

static int amd_pp_instance_init(struct amd_pp_init *pp_init,
+17 −0
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ enum amd_dpm_forced_level {
	AMD_DPM_FORCED_LEVEL_AUTO = 0,
	AMD_DPM_FORCED_LEVEL_LOW = 1,
	AMD_DPM_FORCED_LEVEL_HIGH = 2,
	AMD_DPM_FORCED_LEVEL_MANUAL = 3,
};

struct amd_pp_init {
@@ -225,6 +226,17 @@ enum {
	PP_GROUP_MAX
};

enum pp_clock_type {
	PP_SCLK,
	PP_MCLK,
	PP_PCIE,
};

struct pp_states_info {
	uint32_t nums;
	uint32_t states[16];
};

#define PP_GROUP_MASK        0xF0000000
#define PP_GROUP_SHIFT       28

@@ -278,6 +290,11 @@ struct amd_powerplay_funcs {
	int (*get_fan_control_mode)(void *handle);
	int (*set_fan_speed_percent)(void *handle, uint32_t percent);
	int (*get_fan_speed_percent)(void *handle, uint32_t *speed);
	int (*get_pp_num_states)(void *handle, struct pp_states_info *data);
	int (*get_pp_table)(void *handle, char **table);
	int (*set_pp_table)(void *handle, const char *buf, size_t size);
	int (*force_clock_level)(void *handle, enum pp_clock_type type, int level);
	int (*print_clock_levels)(void *handle, enum pp_clock_type type, char *buf);
};

struct amd_powerplay {
+4 −0
Original line number Diff line number Diff line
@@ -327,6 +327,10 @@ struct pp_hwmgr_func {
	int (*get_dal_power_level)(struct pp_hwmgr *hwmgr,
				   struct amd_pp_dal_clock_info *info);
	int (*power_off_asic)(struct pp_hwmgr *hwmgr);
	int (*get_pp_table)(struct pp_hwmgr *hwmgr, char **table);
	int (*set_pp_table)(struct pp_hwmgr *hwmgr, const char *buf, size_t size);
	int (*force_clock_level)(struct pp_hwmgr *hwmgr, enum pp_clock_type type, int level);
	int (*print_clock_levels)(struct pp_hwmgr *hwmgr, enum pp_clock_type type, char *buf);
};

struct pp_table_func {