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

Commit 70d01a5e authored by Alex Deucher's avatar Alex Deucher
Browse files

drm/radeon/dpm: add infrastructure to force performance levels



This allows you to force specific power levels within a power
state.  Due to hardware restrictions between generations, the
interface is limited to the following 3 selections:

auto: all levels enabled
low: forced to the lowest power level
high: forced to the highest power level

Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 67d5ced5
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1335,6 +1335,12 @@ enum radeon_pcie_gen {
	RADEON_PCIE_GEN_INVALID = 0xffff
};

enum radeon_dpm_forced_level {
	RADEON_DPM_FORCED_LEVEL_AUTO = 0,
	RADEON_DPM_FORCED_LEVEL_LOW = 1,
	RADEON_DPM_FORCED_LEVEL_HIGH = 2,
};

struct radeon_dpm {
	struct radeon_ps        *ps;
	/* number of valid power states */
@@ -1374,6 +1380,8 @@ struct radeon_dpm {
	bool                    uvd_active;
	/* thermal handling */
	struct radeon_dpm_thermal thermal;
	/* forced levels */
	enum radeon_dpm_forced_level forced_level;
};

void radeon_dpm_enable_power_state(struct radeon_device *rdev,
@@ -1669,6 +1677,7 @@ struct radeon_asic {
		u32 (*get_mclk)(struct radeon_device *rdev, bool low);
		void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps);
		void (*debugfs_print_current_performance_level)(struct radeon_device *rdev, struct seq_file *m);
		int (*force_performance_level)(struct radeon_device *rdev, enum radeon_dpm_forced_level level);
	} dpm;
	/* pageflipping */
	struct {
@@ -2436,6 +2445,7 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_dpm_get_mclk(rdev, l) rdev->asic->dpm.get_mclk((rdev), (l))
#define radeon_dpm_print_power_state(rdev, ps) rdev->asic->dpm.print_power_state((rdev), (ps))
#define radeon_dpm_debugfs_print_current_performance_level(rdev, m) rdev->asic->dpm.debugfs_print_current_performance_level((rdev), (m))
#define radeon_dpm_force_performance_level(rdev, l) rdev->asic->dpm.force_performance_level((rdev), (l))

/* Common functions */
/* AGP */
+52 −0
Original line number Diff line number Diff line
@@ -468,9 +468,57 @@ static ssize_t radeon_set_dpm_state(struct device *dev,
	return count;
}

static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev,
						       struct device_attribute *attr,
						       char *buf)
{
	struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
	struct radeon_device *rdev = ddev->dev_private;
	enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;

	return snprintf(buf, PAGE_SIZE, "%s\n",
			(level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" :
			(level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high");
}

static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
						       struct device_attribute *attr,
						       const char *buf,
						       size_t count)
{
	struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
	struct radeon_device *rdev = ddev->dev_private;
	enum radeon_dpm_forced_level level;
	int ret = 0;

	mutex_lock(&rdev->pm.mutex);
	if (strncmp("low", buf, strlen("low")) == 0) {
		level = RADEON_DPM_FORCED_LEVEL_LOW;
	} else if (strncmp("high", buf, strlen("high")) == 0) {
		level = RADEON_DPM_FORCED_LEVEL_HIGH;
	} else if (strncmp("auto", buf, strlen("auto")) == 0) {
		level = RADEON_DPM_FORCED_LEVEL_AUTO;
	} else {
		mutex_unlock(&rdev->pm.mutex);
		count = -EINVAL;
		goto fail;
	}
	if (rdev->asic->dpm.force_performance_level) {
		ret = radeon_dpm_force_performance_level(rdev, level);
		if (ret)
			count = -EINVAL;
	}
	mutex_unlock(&rdev->pm.mutex);
fail:
	return count;
}

static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile);
static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method);
static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
		   radeon_get_dpm_forced_performance_level,
		   radeon_set_dpm_forced_performance_level);

static ssize_t radeon_hwmon_show_temp(struct device *dev,
				      struct device_attribute *attr,
@@ -1064,6 +1112,9 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev)

	if (rdev->pm.num_power_states > 1) {
		ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
		if (ret)
			DRM_ERROR("failed to create device file for dpm state\n");
		ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
		if (ret)
			DRM_ERROR("failed to create device file for dpm state\n");
		/* XXX: these are noops for dpm but are here for backwards compat */
@@ -1170,6 +1221,7 @@ static void radeon_pm_fini_dpm(struct radeon_device *rdev)
		mutex_unlock(&rdev->pm.mutex);

		device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
		device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
		/* XXX backwards compat */
		device_remove_file(rdev->dev, &dev_attr_power_profile);
		device_remove_file(rdev->dev, &dev_attr_power_method);