From: Alex Deucher Date: Sat, 24 Apr 2010 18:50:23 +0000 (-0400) Subject: drm/radeon/kms/pm: rework power management X-Git-Tag: v2.6.35-rc1~18^2~44 X-Git-Url: http://ftp.safe.ca/?p=safe%2Fjmp%2Flinux-2.6;a=commitdiff_plain;h=a424816fb37f894a37585cf86dfdd6b8b1dc681f drm/radeon/kms/pm: rework power management Add two new sysfs attributes: - dynpm - power_state Echoing 0/1 to dynpm disables/enables dynamic power management. The driver scales the sclk dynamically based on the number of queued fences. dynpm only scales sclk dynamically in single head mode. Echoing x.y to power_state selects a static power state (x) and clock mode (y). This allows you to statically select a power state and clock mode. Selecting a static clock mode will disable dynpm. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 2106ac6..7675827 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -145,7 +145,7 @@ void r100_get_power_state(struct radeon_device *rdev, pcie_lanes); } -void r100_set_power_state(struct radeon_device *rdev) +void r100_set_power_state(struct radeon_device *rdev, bool static_switch) { u32 sclk, mclk; @@ -167,33 +167,51 @@ void r100_set_power_state(struct radeon_device *rdev) if (rdev->pm.active_crtc_count > 1) mclk = rdev->clock.default_mclk; - /* set pcie lanes */ - /* TODO */ + /* voltage, pcie lanes, etc.*/ + radeon_pm_misc(rdev); - /* set voltage */ - /* TODO */ - - /* set engine clock */ - if (sclk != rdev->pm.current_sclk) { - radeon_sync_with_vblank(rdev); - radeon_pm_debug_check_in_vbl(rdev, false); - radeon_set_engine_clock(rdev, sclk); - radeon_pm_debug_check_in_vbl(rdev, true); - rdev->pm.current_sclk = sclk; - DRM_INFO("Setting: e: %d\n", sclk); - } + if (static_switch) { + radeon_pm_prepare(rdev); + /* set engine clock */ + if (sclk != rdev->pm.current_sclk) { + radeon_set_engine_clock(rdev, sclk); + rdev->pm.current_sclk = sclk; + DRM_INFO("Setting: e: %d\n", sclk); + } +#if 0 + /* set memory clock */ + if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) { + radeon_set_memory_clock(rdev, mclk); + rdev->pm.current_mclk = mclk; + DRM_INFO("Setting: m: %d\n", mclk); + } +#endif + radeon_pm_finish(rdev); + } else { + /* set engine clock */ + if (sclk != rdev->pm.current_sclk) { + radeon_sync_with_vblank(rdev); + radeon_pm_debug_check_in_vbl(rdev, false); + radeon_set_engine_clock(rdev, sclk); + radeon_pm_debug_check_in_vbl(rdev, true); + rdev->pm.current_sclk = sclk; + DRM_INFO("Setting: e: %d\n", sclk); + } #if 0 - /* set memory clock */ - if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) { - radeon_sync_with_vblank(rdev); - radeon_pm_debug_check_in_vbl(rdev, false); - radeon_set_memory_clock(rdev, mclk); - radeon_pm_debug_check_in_vbl(rdev, true); - rdev->pm.current_mclk = mclk; - DRM_INFO("Setting: m: %d\n", mclk); - } + /* set memory clock */ + if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) { + radeon_sync_with_vblank(rdev); + radeon_pm_debug_check_in_vbl(rdev, false); + radeon_pm_prepare(rdev); + radeon_set_memory_clock(rdev, mclk); + radeon_pm_finish(rdev); + radeon_pm_debug_check_in_vbl(rdev, true); + rdev->pm.current_mclk = mclk; + DRM_INFO("Setting: m: %d\n", mclk); + } #endif + } rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index; rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index; diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 38f75f5..4691309 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -247,7 +247,7 @@ void r600_get_power_state(struct radeon_device *rdev, pcie_lanes); } -void r600_set_power_state(struct radeon_device *rdev) +void r600_set_power_state(struct radeon_device *rdev, bool static_switch) { u32 sclk, mclk; @@ -266,37 +266,52 @@ void r600_set_power_state(struct radeon_device *rdev) clock_info[rdev->pm.requested_clock_mode_index].mclk; if (mclk > rdev->clock.default_mclk) mclk = rdev->clock.default_mclk; - /* don't change the mclk with multiple crtcs */ - if (rdev->pm.active_crtc_count > 1) - mclk = rdev->clock.default_mclk; - - /* set pcie lanes */ - /* TODO */ - /* set voltage */ - /* TODO */ + /* voltage, pcie lanes, etc.*/ + radeon_pm_misc(rdev); - /* set engine clock */ - if (sclk != rdev->pm.current_sclk) { - radeon_sync_with_vblank(rdev); - radeon_pm_debug_check_in_vbl(rdev, false); - radeon_set_engine_clock(rdev, sclk); - radeon_pm_debug_check_in_vbl(rdev, true); - rdev->pm.current_sclk = sclk; - DRM_INFO("Setting: e: %d\n", sclk); - } + if (static_switch) { + radeon_pm_prepare(rdev); + /* set engine clock */ + if (sclk != rdev->pm.current_sclk) { + radeon_set_engine_clock(rdev, sclk); + rdev->pm.current_sclk = sclk; + DRM_INFO("Setting: e: %d\n", sclk); + } +#if 0 + /* set memory clock */ + if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) { + radeon_set_memory_clock(rdev, mclk); + rdev->pm.current_mclk = mclk; + DRM_INFO("Setting: m: %d\n", mclk); + } +#endif + radeon_pm_finish(rdev); + } else { + /* set engine clock */ + if (sclk != rdev->pm.current_sclk) { + radeon_sync_with_vblank(rdev); + radeon_pm_debug_check_in_vbl(rdev, false); + radeon_set_engine_clock(rdev, sclk); + radeon_pm_debug_check_in_vbl(rdev, true); + rdev->pm.current_sclk = sclk; + DRM_INFO("Setting: e: %d\n", sclk); + } #if 0 - /* set memory clock */ - if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) { - radeon_sync_with_vblank(rdev); - radeon_pm_debug_check_in_vbl(rdev, false); - radeon_set_memory_clock(rdev, mclk); - radeon_pm_debug_check_in_vbl(rdev, true); - rdev->pm.current_mclk = mclk; - DRM_INFO("Setting: m: %d\n", mclk); - } + /* set memory clock */ + if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) { + radeon_sync_with_vblank(rdev); + radeon_pm_debug_check_in_vbl(rdev, false); + radeon_pm_prepare(rdev); + radeon_set_memory_clock(rdev, mclk); + radeon_pm_finish(rdev); + radeon_pm_debug_check_in_vbl(rdev, true); + rdev->pm.current_mclk = mclk; + DRM_INFO("Setting: m: %d\n", mclk); + } #endif + } rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index; rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index; diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 16f0ea7..cdcf5ea 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -814,7 +814,7 @@ struct radeon_asic { void (*ioctl_wait_idle)(struct radeon_device *rdev, struct radeon_bo *bo); bool (*gui_idle)(struct radeon_device *rdev); void (*get_power_state)(struct radeon_device *rdev, enum radeon_pm_action action); - void (*set_power_state)(struct radeon_device *rdev); + void (*set_power_state)(struct radeon_device *rdev, bool static_switch); void (*pm_misc)(struct radeon_device *rdev); void (*pm_prepare)(struct radeon_device *rdev); void (*pm_finish)(struct radeon_device *rdev); @@ -1226,7 +1226,10 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) #define radeon_hpd_set_polarity(rdev, hpd) (rdev)->asic->hpd_set_polarity((rdev), (hpd)) #define radeon_gui_idle(rdev) (rdev)->asic->gui_idle((rdev)) #define radeon_get_power_state(rdev, a) (rdev)->asic->get_power_state((rdev), (a)) -#define radeon_set_power_state(rdev) (rdev)->asic->set_power_state((rdev)) +#define radeon_set_power_state(rdev, s) (rdev)->asic->set_power_state((rdev), (s)) +#define radeon_pm_misc(rdev) (rdev)->asic->pm_misc((rdev)) +#define radeon_pm_prepare(rdev) (rdev)->asic->pm_prepare((rdev)) +#define radeon_pm_finish(rdev) (rdev)->asic->pm_finish((rdev)) /* Common functions */ /* AGP */ diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 378db67..8a12786 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -127,7 +127,7 @@ void r100_enable_bm(struct radeon_device *rdev); void r100_set_common_regs(struct radeon_device *rdev); void r100_bm_disable(struct radeon_device *rdev); extern bool r100_gui_idle(struct radeon_device *rdev); -extern void r100_set_power_state(struct radeon_device *rdev); +extern void r100_set_power_state(struct radeon_device *rdev, bool static_switch); extern void r100_get_power_state(struct radeon_device *rdev, enum radeon_pm_action action); extern void r100_pm_misc(struct radeon_device *rdev); @@ -281,7 +281,7 @@ void r600_hpd_set_polarity(struct radeon_device *rdev, enum radeon_hpd_id hpd); extern void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo); extern bool r600_gui_idle(struct radeon_device *rdev); -extern void r600_set_power_state(struct radeon_device *rdev); +extern void r600_set_power_state(struct radeon_device *rdev, bool static_switch); extern void r600_get_power_state(struct radeon_device *rdev, enum radeon_pm_action action); extern void r600_pm_misc(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 23b79eb..c703ae3 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -34,6 +34,128 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev); static void radeon_pm_idle_work_handler(struct work_struct *work); static int radeon_debugfs_pm_init(struct radeon_device *rdev); +static void radeon_pm_set_power_mode_static_locked(struct radeon_device *rdev) +{ + mutex_lock(&rdev->cp.mutex); + + /* wait for GPU idle */ + rdev->pm.gui_idle = false; + rdev->irq.gui_idle = true; + radeon_irq_set(rdev); + wait_event_interruptible_timeout( + rdev->irq.idle_queue, rdev->pm.gui_idle, + msecs_to_jiffies(RADEON_WAIT_IDLE_TIMEOUT)); + rdev->irq.gui_idle = false; + radeon_irq_set(rdev); + + radeon_set_power_state(rdev, true); + + /* update display watermarks based on new power state */ + radeon_update_bandwidth_info(rdev); + if (rdev->pm.active_crtc_count) + radeon_bandwidth_update(rdev); + + mutex_unlock(&rdev->cp.mutex); +} + +static ssize_t radeon_get_power_state_static(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; + + return snprintf(buf, PAGE_SIZE, "%d.%d\n", rdev->pm.current_power_state_index, + rdev->pm.current_clock_mode_index); +} + +static ssize_t radeon_set_power_state_static(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; + int ps, cm; + + if (sscanf(buf, "%u.%u", &ps, &cm) != 2) { + DRM_ERROR("Invalid power state!\n"); + return count; + } + + mutex_lock(&rdev->pm.mutex); + if ((ps >= 0) && (ps < rdev->pm.num_power_states) && + (cm >= 0) && (cm < rdev->pm.power_state[ps].num_clock_modes)) { + if ((rdev->pm.active_crtc_count > 1) && + (rdev->pm.power_state[ps].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)) { + DRM_ERROR("Invalid power state for multi-head: %d.%d\n", ps, cm); + } else { + /* disable dynpm */ + rdev->pm.state = PM_STATE_DISABLED; + rdev->pm.planned_action = PM_ACTION_NONE; + rdev->pm.requested_power_state_index = ps; + rdev->pm.requested_clock_mode_index = cm; + radeon_pm_set_power_mode_static_locked(rdev); + } + } else + DRM_ERROR("Invalid power state: %d.%d\n\n", ps, cm); + mutex_unlock(&rdev->pm.mutex); + + return count; +} + +static ssize_t radeon_get_dynpm(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; + + return snprintf(buf, PAGE_SIZE, "%s\n", + (rdev->pm.state == PM_STATE_DISABLED) ? "disabled" : "enabled"); +} + +static ssize_t radeon_set_dynpm(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; + int tmp = simple_strtoul(buf, NULL, 10); + + if (tmp == 0) { + /* update power mode info */ + radeon_pm_compute_clocks(rdev); + /* disable dynpm */ + mutex_lock(&rdev->pm.mutex); + rdev->pm.state = PM_STATE_DISABLED; + rdev->pm.planned_action = PM_ACTION_NONE; + mutex_unlock(&rdev->pm.mutex); + DRM_INFO("radeon: dynamic power management disabled\n"); + } else if (tmp == 1) { + if (rdev->pm.num_power_states > 1) { + /* enable dynpm */ + mutex_lock(&rdev->pm.mutex); + rdev->pm.state = PM_STATE_PAUSED; + rdev->pm.planned_action = PM_ACTION_DEFAULT; + radeon_get_power_state(rdev, rdev->pm.planned_action); + mutex_unlock(&rdev->pm.mutex); + /* update power mode info */ + radeon_pm_compute_clocks(rdev); + DRM_INFO("radeon: dynamic power management enabled\n"); + } else + DRM_ERROR("dynpm not valid on this system\n"); + } else + DRM_ERROR("Invalid setting: %d\n", tmp); + + return count; +} + +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, radeon_get_power_state_static, radeon_set_power_state_static); +static DEVICE_ATTR(dynpm, S_IRUGO | S_IWUSR, radeon_get_dynpm, radeon_set_dynpm); + + static const char *pm_state_names[4] = { "PM_STATE_DISABLED", "PM_STATE_MINIMUM", @@ -111,6 +233,10 @@ int radeon_pm_init(struct radeon_device *rdev) DRM_ERROR("Failed to register debugfs file for PM!\n"); } + /* where's the best place to put this? */ + device_create_file(rdev->dev, &dev_attr_power_state); + device_create_file(rdev->dev, &dev_attr_dynpm); + INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler); if ((radeon_dynpm != -1 && radeon_dynpm) && (rdev->pm.num_power_states > 1)) { @@ -132,8 +258,19 @@ void radeon_pm_fini(struct radeon_device *rdev) rdev->pm.state = PM_STATE_DISABLED; rdev->pm.planned_action = PM_ACTION_DEFAULT; radeon_pm_set_clocks(rdev); + } else if ((rdev->pm.current_power_state_index != + rdev->pm.default_power_state_index) || + (rdev->pm.current_clock_mode_index != 0)) { + rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.requested_clock_mode_index = 0; + mutex_lock(&rdev->pm.mutex); + radeon_pm_set_power_mode_static_locked(rdev); + mutex_unlock(&rdev->pm.mutex); } + device_remove_file(rdev->dev, &dev_attr_power_state); + device_remove_file(rdev->dev, &dev_attr_dynpm); + if (rdev->pm.i2c_bus) radeon_i2c_destroy(rdev->pm.i2c_bus); } @@ -267,7 +404,7 @@ static void radeon_pm_set_clocks_locked(struct radeon_device *rdev) { /*radeon_fence_wait_last(rdev);*/ - radeon_set_power_state(rdev); + radeon_set_power_state(rdev, false); rdev->pm.planned_action = PM_ACTION_NONE; }