summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Deucher <alexander.deucher@amd.com>2013-07-02 18:38:02 -0400
committerAlex Deucher <alexander.deucher@amd.com>2013-07-05 18:09:19 -0400
commit70d01a5ee29fcb23a6b5948227b1aee212922ade (patch)
treef2087b3fa9b4c6942686367c4390b4a04b6904c4
parent67d5ced503db5b44cb82f378c9cb3f0e77a94e7f (diff)
downloadlinux-70d01a5ee29fcb23a6b5948227b1aee212922ade.tar.gz
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: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/radeon/radeon.h10
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c52
2 files changed, 62 insertions, 0 deletions
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 08cececb376d..b4681313646c 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -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 */
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index aaafb931922f..2557e8bab5cc 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -468,9 +468,57 @@ fail:
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,
@@ -1066,6 +1114,9 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev)
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 */
ret = device_create_file(rdev->dev, &dev_attr_power_profile);
if (ret)
@@ -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);