[PATCH 069/165] drm/radeon/kms: add common dpm infrastructure
Jerome Glisse
j.glisse at gmail.com
Wed Jun 26 03:27:27 PDT 2013
On Wed, Jun 26, 2013 at 09:22:29AM -0400, alexdeucher at gmail.com wrote:
> From: Alex Deucher <alexander.deucher at amd.com>
>
> This adds the common dpm (dynamic power management)
> infrastructure:
> - dpm callbacks
> - dpm init/fini/suspend/resume
> - dpm power state selection
>
> No device specific code is enabled yet.
>
> Signed-off-by: Alex Deucher <alexander.deucher at amd.com>
Reviewed-by: Jerome Glisse <jglisse at redhat.com>
> ---
> drivers/gpu/drm/radeon/radeon.h | 100 +++++++-
> drivers/gpu/drm/radeon/radeon_drv.c | 4 +
> drivers/gpu/drm/radeon/radeon_pm.c | 496 ++++++++++++++++++++++++++++++++++-
> 3 files changed, 591 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
> index 6c445f5..c43673c 100644
> --- a/drivers/gpu/drm/radeon/radeon.h
> +++ b/drivers/gpu/drm/radeon/radeon.h
> @@ -96,6 +96,7 @@ extern int radeon_pcie_gen2;
> extern int radeon_msi;
> extern int radeon_lockup_timeout;
> extern int radeon_fastfb;
> +extern int radeon_dpm;
>
> /*
> * Copy from radeon_drv.h so we don't have to include both and have conflicting
> @@ -1048,6 +1049,7 @@ struct radeon_wb {
> enum radeon_pm_method {
> PM_METHOD_PROFILE,
> PM_METHOD_DYNPM,
> + PM_METHOD_DPM,
> };
>
> enum radeon_dynpm_state {
> @@ -1073,11 +1075,23 @@ enum radeon_voltage_type {
> };
>
> enum radeon_pm_state_type {
> + /* not used for dpm */
> POWER_STATE_TYPE_DEFAULT,
> POWER_STATE_TYPE_POWERSAVE,
> + /* user selectable states */
> POWER_STATE_TYPE_BATTERY,
> POWER_STATE_TYPE_BALANCED,
> POWER_STATE_TYPE_PERFORMANCE,
> + /* internal states */
> + POWER_STATE_TYPE_INTERNAL_UVD,
> + POWER_STATE_TYPE_INTERNAL_UVD_SD,
> + POWER_STATE_TYPE_INTERNAL_UVD_HD,
> + POWER_STATE_TYPE_INTERNAL_UVD_HD2,
> + POWER_STATE_TYPE_INTERNAL_UVD_MVC,
> + POWER_STATE_TYPE_INTERNAL_BOOT,
> + POWER_STATE_TYPE_INTERNAL_THERMAL,
> + POWER_STATE_TYPE_INTERNAL_ACPI,
> + POWER_STATE_TYPE_INTERNAL_ULV,
> };
>
> enum radeon_pm_profile_type {
> @@ -1106,12 +1120,16 @@ struct radeon_pm_profile {
>
> enum radeon_int_thermal_type {
> THERMAL_TYPE_NONE,
> + THERMAL_TYPE_EXTERNAL,
> + THERMAL_TYPE_EXTERNAL_GPIO,
> THERMAL_TYPE_RV6XX,
> THERMAL_TYPE_RV770,
> + THERMAL_TYPE_ADT7473_WITH_INTERNAL,
> THERMAL_TYPE_EVERGREEN,
> THERMAL_TYPE_SUMO,
> THERMAL_TYPE_NI,
> THERMAL_TYPE_SI,
> + THERMAL_TYPE_EMC2103_WITH_INTERNAL,
> THERMAL_TYPE_CI,
> };
>
> @@ -1166,6 +1184,60 @@ struct radeon_power_state {
> */
> #define RADEON_MODE_OVERCLOCK_MARGIN 500 /* 5 MHz */
>
> +struct radeon_ps {
> + u32 caps; /* vbios flags */
> + u32 class; /* vbios flags */
> + u32 class2; /* vbios flags */
> + /* UVD clocks */
> + u32 vclk;
> + u32 dclk;
> + /* asic priv */
> + void *ps_priv;
> +};
> +
> +struct radeon_dpm_thermal {
> + /* thermal interrupt work */
> + struct work_struct work;
> + /* low temperature threshold */
> + int min_temp;
> + /* high temperature threshold */
> + int max_temp;
> + /* was interrupt low to high or high to low */
> + bool high_to_low;
> +};
> +
> +struct radeon_dpm {
> + struct radeon_ps *ps;
> + /* number of valid power states */
> + int num_ps;
> + /* current power state that is active */
> + struct radeon_ps *current_ps;
> + /* requested power state */
> + struct radeon_ps *requested_ps;
> + /* boot up power state */
> + struct radeon_ps *boot_ps;
> + /* default uvd power state */
> + struct radeon_ps *uvd_ps;
> + enum radeon_pm_state_type state;
> + enum radeon_pm_state_type user_state;
> + u32 platform_caps;
> + u32 voltage_response_time;
> + u32 backbias_response_time;
> + void *priv;
Just nitpick all of the above have broken indentation space instead of tab.
> + u32 new_active_crtcs;
> + int new_active_crtc_count;
> + u32 current_active_crtcs;
> + int current_active_crtc_count;
> + /* special states active */
> + bool thermal_active;
> + /* thermal handling */
> + struct radeon_dpm_thermal thermal;
> +};
> +
> +void radeon_dpm_enable_power_state(struct radeon_device *rdev,
> + enum radeon_pm_state_type dpm_state);
> +
> +
> struct radeon_pm {
> struct mutex mutex;
> /* write locked while reprogramming mclk */
> @@ -1219,6 +1291,9 @@ struct radeon_pm {
> /* internal thermal controller on rv6xx+ */
> enum radeon_int_thermal_type int_thermal_type;
> struct device *int_hwmon_dev;
> + /* dpm */
> + bool dpm_enabled;
> + struct radeon_dpm dpm;
> };
>
> int radeon_pm_get_type_index(struct radeon_device *rdev,
> @@ -1416,7 +1491,7 @@ struct radeon_asic {
> bool (*sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
> void (*set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
> } hpd;
> - /* power management */
> + /* static power management */
> struct {
> void (*misc)(struct radeon_device *rdev);
> void (*prepare)(struct radeon_device *rdev);
> @@ -1433,6 +1508,19 @@ struct radeon_asic {
> int (*set_uvd_clocks)(struct radeon_device *rdev, u32 vclk, u32 dclk);
> int (*get_temperature)(struct radeon_device *rdev);
> } pm;
> + /* dynamic power management */
> + struct {
> + int (*init)(struct radeon_device *rdev);
> + void (*setup_asic)(struct radeon_device *rdev);
> + int (*enable)(struct radeon_device *rdev);
> + void (*disable)(struct radeon_device *rdev);
> + int (*set_power_state)(struct radeon_device *rdev);
> + void (*display_configuration_changed)(struct radeon_device *rdev);
> + void (*fini)(struct radeon_device *rdev);
> + u32 (*get_sclk)(struct radeon_device *rdev, bool low);
> + u32 (*get_mclk)(struct radeon_device *rdev, bool low);
> + void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps);
> + } dpm;
> /* pageflipping */
> struct {
> void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
> @@ -2122,6 +2210,16 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
> #define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev))
> #define radeon_get_xclk(rdev) (rdev)->asic->get_xclk((rdev))
> #define radeon_get_gpu_clock_counter(rdev) (rdev)->asic->get_gpu_clock_counter((rdev))
> +#define radeon_dpm_init(rdev) rdev->asic->dpm.init((rdev))
> +#define radeon_dpm_setup_asic(rdev) rdev->asic->dpm.setup_asic((rdev))
> +#define radeon_dpm_enable(rdev) rdev->asic->dpm.enable((rdev))
> +#define radeon_dpm_disable(rdev) rdev->asic->dpm.disable((rdev))
> +#define radeon_dpm_set_power_state(rdev) rdev->asic->dpm.set_power_state((rdev))
> +#define radeon_dpm_display_configuration_changed(rdev) rdev->asic->dpm.display_configuration_changed((rdev))
> +#define radeon_dpm_fini(rdev) rdev->asic->dpm.fini((rdev))
> +#define radeon_dpm_get_sclk(rdev, l) rdev->asic->dpm.get_sclk((rdev), (l))
> +#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))
>
> /* Common functions */
> /* AGP */
> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
> index 02709e4..00cc52e 100644
> --- a/drivers/gpu/drm/radeon/radeon_drv.c
> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
> @@ -165,6 +165,7 @@ int radeon_pcie_gen2 = -1;
> int radeon_msi = -1;
> int radeon_lockup_timeout = 10000;
> int radeon_fastfb = 0;
> +int radeon_dpm = -1;
>
> MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
> module_param_named(no_wb, radeon_no_wb, int, 0444);
> @@ -220,6 +221,9 @@ module_param_named(lockup_timeout, radeon_lockup_timeout, int, 0444);
> MODULE_PARM_DESC(fastfb, "Direct FB access for IGP chips (0 = disable, 1 = enable)");
> module_param_named(fastfb, radeon_fastfb, int, 0444);
>
> +MODULE_PARM_DESC(dpm, "DPM support (1 = enable, 0 = disable, -1 = auto)");
> +module_param_named(dpm, radeon_dpm, int, 0444);
> +
> static struct pci_device_id pciidlist[] = {
> radeon_PCI_IDS
> };
> diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
> index e8c1bea..4f5422e 100644
> --- a/drivers/gpu/drm/radeon/radeon_pm.c
> +++ b/drivers/gpu/drm/radeon/radeon_pm.c
> @@ -388,7 +388,8 @@ static ssize_t radeon_get_pm_method(struct device *dev,
> int pm = rdev->pm.pm_method;
>
> return snprintf(buf, PAGE_SIZE, "%s\n",
> - (pm == PM_METHOD_DYNPM) ? "dynpm" : "profile");
> + (pm == PM_METHOD_DYNPM) ? "dynpm" :
> + (pm == PM_METHOD_PROFILE) ? "profile" : "dpm");
> }
>
> static ssize_t radeon_set_pm_method(struct device *dev,
> @@ -399,6 +400,11 @@ static ssize_t radeon_set_pm_method(struct device *dev,
> struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
> struct radeon_device *rdev = ddev->dev_private;
>
> + /* we don't support the legacy modes with dpm */
> + if (rdev->pm.pm_method == PM_METHOD_DPM) {
> + count = -EINVAL;
> + goto fail;
> + }
>
> if (strncmp("dynpm", buf, strlen("dynpm")) == 0) {
> mutex_lock(&rdev->pm.mutex);
> @@ -423,8 +429,48 @@ fail:
> return count;
> }
>
> +static ssize_t radeon_get_dpm_state(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_pm_state_type pm = rdev->pm.dpm.user_state;
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n",
> + (pm == POWER_STATE_TYPE_BATTERY) ? "battery" :
> + (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");
> +}
> +
> +static ssize_t radeon_set_dpm_state(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;
> +
> + mutex_lock(&rdev->pm.mutex);
> + if (strncmp("battery", buf, strlen("battery")) == 0)
> + rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY;
> + else if (strncmp("balanced", buf, strlen("balanced")) == 0)
> + rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
> + else if (strncmp("performance", buf, strlen("performance")) == 0)
> + rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
> + else {
> + mutex_unlock(&rdev->pm.mutex);
> + count = -EINVAL;
> + goto fail;
> + }
> + mutex_unlock(&rdev->pm.mutex);
> + radeon_pm_compute_clocks(rdev);
> +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 ssize_t radeon_hwmon_show_temp(struct device *dev,
> struct device_attribute *attr,
> @@ -508,7 +554,228 @@ static void radeon_hwmon_fini(struct radeon_device *rdev)
> }
> }
>
> -void radeon_pm_suspend(struct radeon_device *rdev)
> +static void radeon_dpm_thermal_work_handler(struct work_struct *work)
> +{
> + struct radeon_device *rdev =
> + container_of(work, struct radeon_device,
> + pm.dpm.thermal.work);
> + /* switch to the thermal state */
> + enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL;
> +
> + if (!rdev->pm.dpm_enabled)
> + return;
> +
> + if (rdev->asic->pm.get_temperature) {
> + int temp = radeon_get_temperature(rdev);
> +
> + if (temp < rdev->pm.dpm.thermal.min_temp)
> + /* switch back the user state */
> + dpm_state = rdev->pm.dpm.user_state;
> + } else {
> + if (rdev->pm.dpm.thermal.high_to_low)
> + /* switch back the user state */
> + dpm_state = rdev->pm.dpm.user_state;
> + }
> + radeon_dpm_enable_power_state(rdev, dpm_state);
> +}
> +
> +static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
> + enum radeon_pm_state_type dpm_state)
> +{
> + int i;
> + struct radeon_ps *ps;
> + u32 ui_class;
> +
> +restart_search:
> + /* balanced states don't exist at the moment */
> + if (dpm_state == POWER_STATE_TYPE_BALANCED)
> + dpm_state = POWER_STATE_TYPE_PERFORMANCE;
> +
> + /* Pick the best power state based on current conditions */
> + for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
> + ps = &rdev->pm.dpm.ps[i];
> + ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK;
> + switch (dpm_state) {
> + /* user states */
> + case POWER_STATE_TYPE_BATTERY:
> + if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
> + if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
> + if (rdev->pm.dpm.new_active_crtc_count < 2)
> + return ps;
> + } else
> + return ps;
> + }
> + break;
> + case POWER_STATE_TYPE_BALANCED:
> + if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
> + if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
> + if (rdev->pm.dpm.new_active_crtc_count < 2)
> + return ps;
> + } else
> + return ps;
> + }
> + break;
> + case POWER_STATE_TYPE_PERFORMANCE:
> + if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
> + if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
> + if (rdev->pm.dpm.new_active_crtc_count < 2)
> + return ps;
> + } else
> + return ps;
> + }
> + break;
> + /* internal states */
> + case POWER_STATE_TYPE_INTERNAL_UVD:
> + return rdev->pm.dpm.uvd_ps;
> + case POWER_STATE_TYPE_INTERNAL_UVD_SD:
> + if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
> + return ps;
> + break;
> + case POWER_STATE_TYPE_INTERNAL_UVD_HD:
> + if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
> + return ps;
> + break;
> + case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
> + if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
> + return ps;
> + break;
> + case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
> + if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
> + return ps;
> + break;
> + case POWER_STATE_TYPE_INTERNAL_BOOT:
> + return rdev->pm.dpm.boot_ps;
> + case POWER_STATE_TYPE_INTERNAL_THERMAL:
> + if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
> + return ps;
> + break;
> + case POWER_STATE_TYPE_INTERNAL_ACPI:
> + if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI)
> + return ps;
> + break;
> + case POWER_STATE_TYPE_INTERNAL_ULV:
> + if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
> + return ps;
> + break;
> + default:
> + break;
> + }
> + }
> + /* use a fallback state if we didn't match */
> + switch (dpm_state) {
> + case POWER_STATE_TYPE_INTERNAL_UVD_SD:
> + case POWER_STATE_TYPE_INTERNAL_UVD_HD:
> + case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
> + case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
> + return rdev->pm.dpm.uvd_ps;
> + case POWER_STATE_TYPE_INTERNAL_THERMAL:
> + dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI;
> + goto restart_search;
> + case POWER_STATE_TYPE_INTERNAL_ACPI:
> + dpm_state = POWER_STATE_TYPE_BATTERY;
> + goto restart_search;
> + case POWER_STATE_TYPE_BATTERY:
> + dpm_state = POWER_STATE_TYPE_PERFORMANCE;
> + goto restart_search;
> + default:
> + break;
> + }
> +
> + return NULL;
> +}
> +
> +static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
> +{
> + int i;
> + struct radeon_ps *ps;
> + enum radeon_pm_state_type dpm_state;
> +
> + /* if dpm init failed */
> + if (!rdev->pm.dpm_enabled)
> + return;
> +
> + if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) {
> + /* add other state override checks here */
> + if (!rdev->pm.dpm.thermal_active)
> + rdev->pm.dpm.state = rdev->pm.dpm.user_state;
> + }
> + dpm_state = rdev->pm.dpm.state;
> +
> + ps = radeon_dpm_pick_power_state(rdev, dpm_state);
> + if (ps)
> + rdev->pm.dpm.requested_ps = ps;
> + else
> + return;
> +
> + /* no need to reprogram if nothing changed */
> + if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
> + /* update display watermarks based on new power state */
> + if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) {
> + radeon_bandwidth_update(rdev);
> + /* update displays */
> + radeon_dpm_display_configuration_changed(rdev);
> + rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
> + rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
> + }
> + return;
> + }
> +
> + printk("switching from power state:\n");
> + radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
> + printk("switching to power state:\n");
> + radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
> +
> + mutex_lock(&rdev->ddev->struct_mutex);
> + down_write(&rdev->pm.mclk_lock);
> + mutex_lock(&rdev->ring_lock);
> +
> + /* update display watermarks based on new power state */
> + radeon_bandwidth_update(rdev);
> + /* update displays */
> + radeon_dpm_display_configuration_changed(rdev);
> +
> + rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
> + rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
> +
> + /* wait for the rings to drain */
> + for (i = 0; i < RADEON_NUM_RINGS; i++) {
> + struct radeon_ring *ring = &rdev->ring[i];
> + if (ring->ready)
> + radeon_fence_wait_empty_locked(rdev, i);
> + }
> +
> + /* program the new power state */
> + radeon_dpm_set_power_state(rdev);
> +
> + /* update current power state */
> + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
> +
> + mutex_unlock(&rdev->ring_lock);
> + up_write(&rdev->pm.mclk_lock);
> + mutex_unlock(&rdev->ddev->struct_mutex);
> +}
> +
> +void radeon_dpm_enable_power_state(struct radeon_device *rdev,
> + enum radeon_pm_state_type dpm_state)
> +{
> + if (!rdev->pm.dpm_enabled)
> + return;
> +
> + mutex_lock(&rdev->pm.mutex);
> + switch (dpm_state) {
> + case POWER_STATE_TYPE_INTERNAL_THERMAL:
> + rdev->pm.dpm.thermal_active = true;
> + break;
> + default:
> + rdev->pm.dpm.thermal_active = false;
> + break;
> + }
> + rdev->pm.dpm.state = dpm_state;
> + mutex_unlock(&rdev->pm.mutex);
> + radeon_pm_compute_clocks(rdev);
> +}
> +
> +static void radeon_pm_suspend_old(struct radeon_device *rdev)
> {
> mutex_lock(&rdev->pm.mutex);
> if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
> @@ -520,7 +787,26 @@ void radeon_pm_suspend(struct radeon_device *rdev)
> cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
> }
>
> -void radeon_pm_resume(struct radeon_device *rdev)
> +static void radeon_pm_suspend_dpm(struct radeon_device *rdev)
> +{
> + mutex_lock(&rdev->pm.mutex);
> + /* disable dpm */
> + radeon_dpm_disable(rdev);
> + /* reset the power state */
> + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
> + rdev->pm.dpm_enabled = false;
> + mutex_unlock(&rdev->pm.mutex);
> +}
> +
> +void radeon_pm_suspend(struct radeon_device *rdev)
> +{
> + if (rdev->pm.pm_method == PM_METHOD_DPM)
> + radeon_pm_suspend_dpm(rdev);
> + else
> + radeon_pm_suspend_old(rdev);
> +}
> +
> +static void radeon_pm_resume_old(struct radeon_device *rdev)
> {
> /* set up the default clocks if the MC ucode is loaded */
> if ((rdev->family >= CHIP_BARTS) &&
> @@ -555,12 +841,50 @@ void radeon_pm_resume(struct radeon_device *rdev)
> radeon_pm_compute_clocks(rdev);
> }
>
> -int radeon_pm_init(struct radeon_device *rdev)
> +static void radeon_pm_resume_dpm(struct radeon_device *rdev)
> +{
> + int ret;
> +
> + /* asic init will reset to the boot state */
> + mutex_lock(&rdev->pm.mutex);
> + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
> + radeon_dpm_setup_asic(rdev);
> + ret = radeon_dpm_enable(rdev);
> + mutex_unlock(&rdev->pm.mutex);
> + if (ret) {
> + DRM_ERROR("radeon: dpm resume failed\n");
> + if ((rdev->family >= CHIP_BARTS) &&
> + (rdev->family <= CHIP_CAYMAN) &&
> + rdev->mc_fw) {
> + if (rdev->pm.default_vddc)
> + radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
> + SET_VOLTAGE_TYPE_ASIC_VDDC);
> + if (rdev->pm.default_vddci)
> + radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
> + SET_VOLTAGE_TYPE_ASIC_VDDCI);
> + if (rdev->pm.default_sclk)
> + radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
> + if (rdev->pm.default_mclk)
> + radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
> + }
> + } else {
> + rdev->pm.dpm_enabled = true;
> + radeon_pm_compute_clocks(rdev);
> + }
> +}
> +
> +void radeon_pm_resume(struct radeon_device *rdev)
> +{
> + if (rdev->pm.pm_method == PM_METHOD_DPM)
> + radeon_pm_resume_dpm(rdev);
> + else
> + radeon_pm_resume_old(rdev);
> +}
> +
> +static int radeon_pm_init_old(struct radeon_device *rdev)
> {
> int ret;
>
> - /* default to profile method */
> - rdev->pm.pm_method = PM_METHOD_PROFILE;
> rdev->pm.profile = PM_PROFILE_DEFAULT;
> rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
> rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
> @@ -622,7 +946,103 @@ int radeon_pm_init(struct radeon_device *rdev)
> return 0;
> }
>
> -void radeon_pm_fini(struct radeon_device *rdev)
> +static void radeon_dpm_print_power_states(struct radeon_device *rdev)
> +{
> + int i;
> +
> + for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
> + printk("== power state %d ==\n", i);
> + radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]);
> + }
> +}
> +
> +static int radeon_pm_init_dpm(struct radeon_device *rdev)
> +{
> + int ret;
> +
> + /* default to performance state */
> + rdev->pm.dpm.state = POWER_STATE_TYPE_PERFORMANCE;
> + rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
> + rdev->pm.default_sclk = rdev->clock.default_sclk;
> + rdev->pm.default_mclk = rdev->clock.default_mclk;
> + rdev->pm.current_sclk = rdev->clock.default_sclk;
> + rdev->pm.current_mclk = rdev->clock.default_mclk;
> + rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
> +
> + if (rdev->bios && rdev->is_atom_bios)
> + radeon_atombios_get_power_modes(rdev);
> + else
> + return -EINVAL;
> +
> + /* set up the internal thermal sensor if applicable */
> + ret = radeon_hwmon_init(rdev);
> + if (ret)
> + return ret;
> +
> + INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
> + mutex_lock(&rdev->pm.mutex);
> + radeon_dpm_init(rdev);
> + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
> + radeon_dpm_print_power_states(rdev);
> + radeon_dpm_setup_asic(rdev);
> + ret = radeon_dpm_enable(rdev);
> + mutex_unlock(&rdev->pm.mutex);
> + if (ret) {
> + rdev->pm.dpm_enabled = false;
> + if ((rdev->family >= CHIP_BARTS) &&
> + (rdev->family <= CHIP_CAYMAN) &&
> + rdev->mc_fw) {
> + if (rdev->pm.default_vddc)
> + radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
> + SET_VOLTAGE_TYPE_ASIC_VDDC);
> + if (rdev->pm.default_vddci)
> + radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
> + SET_VOLTAGE_TYPE_ASIC_VDDCI);
> + if (rdev->pm.default_sclk)
> + radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
> + if (rdev->pm.default_mclk)
> + radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
> + }
> + DRM_ERROR("radeon: dpm initialization failed\n");
> + return ret;
> + }
> + rdev->pm.dpm_enabled = true;
> + radeon_pm_compute_clocks(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");
> + /* XXX: these are noops for dpm but are here for backwards compat */
> + ret = device_create_file(rdev->dev, &dev_attr_power_profile);
> + if (ret)
> + DRM_ERROR("failed to create device file for power profile\n");
> + ret = device_create_file(rdev->dev, &dev_attr_power_method);
> + if (ret)
> + DRM_ERROR("failed to create device file for power method\n");
> + DRM_INFO("radeon: dpm initialized\n");
> + }
> +
> + return 0;
> +}
> +
> +int radeon_pm_init(struct radeon_device *rdev)
> +{
> + /* enable dpm on rv6xx+ */
> + switch (rdev->family) {
> + default:
> + /* default to profile method */
> + rdev->pm.pm_method = PM_METHOD_PROFILE;
> + break;
> + }
> +
> + if (rdev->pm.pm_method == PM_METHOD_DPM)
> + return radeon_pm_init_dpm(rdev);
> + else
> + return radeon_pm_init_old(rdev);
> +}
> +
> +static void radeon_pm_fini_old(struct radeon_device *rdev)
> {
> if (rdev->pm.num_power_states > 1) {
> mutex_lock(&rdev->pm.mutex);
> @@ -650,7 +1070,35 @@ void radeon_pm_fini(struct radeon_device *rdev)
> radeon_hwmon_fini(rdev);
> }
>
> -void radeon_pm_compute_clocks(struct radeon_device *rdev)
> +static void radeon_pm_fini_dpm(struct radeon_device *rdev)
> +{
> + if (rdev->pm.num_power_states > 1) {
> + mutex_lock(&rdev->pm.mutex);
> + radeon_dpm_disable(rdev);
> + mutex_unlock(&rdev->pm.mutex);
> +
> + device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
> + /* XXX backwards compat */
> + device_remove_file(rdev->dev, &dev_attr_power_profile);
> + device_remove_file(rdev->dev, &dev_attr_power_method);
> + }
> + radeon_dpm_fini(rdev);
> +
> + if (rdev->pm.power_state)
> + kfree(rdev->pm.power_state);
> +
> + radeon_hwmon_fini(rdev);
> +}
> +
> +void radeon_pm_fini(struct radeon_device *rdev)
> +{
> + if (rdev->pm.pm_method == PM_METHOD_DPM)
> + radeon_pm_fini_dpm(rdev);
> + else
> + radeon_pm_fini_old(rdev);
> +}
> +
> +static void radeon_pm_compute_clocks_old(struct radeon_device *rdev)
> {
> struct drm_device *ddev = rdev->ddev;
> struct drm_crtc *crtc;
> @@ -721,6 +1169,38 @@ void radeon_pm_compute_clocks(struct radeon_device *rdev)
> mutex_unlock(&rdev->pm.mutex);
> }
>
> +static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev)
> +{
> + struct drm_device *ddev = rdev->ddev;
> + struct drm_crtc *crtc;
> + struct radeon_crtc *radeon_crtc;
> +
> + mutex_lock(&rdev->pm.mutex);
> +
> + rdev->pm.dpm.new_active_crtcs = 0;
> + rdev->pm.dpm.new_active_crtc_count = 0;
> + list_for_each_entry(crtc,
> + &ddev->mode_config.crtc_list, head) {
> + radeon_crtc = to_radeon_crtc(crtc);
> + if (crtc->enabled) {
> + rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id);
> + rdev->pm.dpm.new_active_crtc_count++;
> + }
> + }
> +
> + radeon_dpm_change_power_state_locked(rdev);
> +
> + mutex_unlock(&rdev->pm.mutex);
> +}
> +
> +void radeon_pm_compute_clocks(struct radeon_device *rdev)
> +{
> + if (rdev->pm.pm_method == PM_METHOD_DPM)
> + radeon_pm_compute_clocks_dpm(rdev);
> + else
> + radeon_pm_compute_clocks_old(rdev);
> +}
> +
> static bool radeon_pm_in_vbl(struct radeon_device *rdev)
> {
> int crtc, vpos, hpos, vbl_status;
> --
> 1.7.7.5
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
More information about the dri-devel
mailing list