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

Commit ce8f5370 authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie
Browse files

drm/radeon/kms/pm: rework power management



- Separate dynpm and profile based power management methods.  You can select the pm method
  by echoing the selected method ("dynpm" or "profile") to power_method in sysfs.
- Expose basic 4 profile in profile method
  "default" - default clocks
  "auto" - select between low and high based on ac/dc state
  "low" - DC, low power mode
  "high" - AC, performance mode
  The current base profile is "default", but it should switched to "auto" once we've tested
  on more systems.  Switching the state is a matter of echoing the requested profile to
  power_profile in sysfs.  The lowest power states are selected automatically when dpms turns
  the monitors off in all states but default.
- Remove dynamic fence-based reclocking for the moment.  We can revisit this later once we
  have basic pm in.
- Move pm init/fini to modesetting path.  pm is tightly coupled with display state.  Make sure
  display side is initialized before pm.
- Add pm suspend/resume functions to make sure pm state is properly reinitialized on resume.
- Remove dynpm module option.  It's now selectable via sysfs.

Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent d7311171
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
config DRM_RADEON_KMS
	bool "Enable modesetting on radeon by default - NEW DRIVER"
	depends on DRM_RADEON
	depends on POWER_SUPPLY
	help
	  Choose this option if you want kernel modesetting enabled by default.

+0 −3
Original line number Diff line number Diff line
@@ -2115,8 +2115,6 @@ int evergreen_init(struct radeon_device *rdev)
	r = radeon_clocks_init(rdev);
	if (r)
		return r;
	/* Initialize power management */
	radeon_pm_init(rdev);
	/* Fence driver */
	r = radeon_fence_driver_init(rdev);
	if (r)
@@ -2178,7 +2176,6 @@ int evergreen_init(struct radeon_device *rdev)

void evergreen_fini(struct radeon_device *rdev)
{
	radeon_pm_fini(rdev);
	/*r600_blit_fini(rdev);*/
	r700_cp_fini(rdev);
	r600_wb_fini(rdev);
+40 −88
Original line number Diff line number Diff line
@@ -68,22 +68,21 @@ MODULE_FIRMWARE(FIRMWARE_R520);
 * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
 */

void r100_get_power_state(struct radeon_device *rdev,
			  enum radeon_pm_action action)
void r100_pm_get_dynpm_state(struct radeon_device *rdev)
{
	int i;
	rdev->pm.can_upclock = true;
	rdev->pm.can_downclock = true;
	rdev->pm.dynpm_can_upclock = true;
	rdev->pm.dynpm_can_downclock = true;

	switch (action) {
	case PM_ACTION_MINIMUM:
	switch (rdev->pm.dynpm_planned_action) {
	case DYNPM_ACTION_MINIMUM:
		rdev->pm.requested_power_state_index = 0;
		rdev->pm.can_downclock = false;
		rdev->pm.dynpm_can_downclock = false;
		break;
	case PM_ACTION_DOWNCLOCK:
	case DYNPM_ACTION_DOWNCLOCK:
		if (rdev->pm.current_power_state_index == 0) {
			rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
			rdev->pm.can_downclock = false;
			rdev->pm.dynpm_can_downclock = false;
		} else {
			if (rdev->pm.active_crtc_count > 1) {
				for (i = 0; i < rdev->pm.num_power_states; i++) {
@@ -108,10 +107,10 @@ void r100_get_power_state(struct radeon_device *rdev,
			rdev->pm.requested_power_state_index++;
		}
		break;
	case PM_ACTION_UPCLOCK:
	case DYNPM_ACTION_UPCLOCK:
		if (rdev->pm.current_power_state_index == (rdev->pm.num_power_states - 1)) {
			rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
			rdev->pm.can_upclock = false;
			rdev->pm.dynpm_can_upclock = false;
		} else {
			if (rdev->pm.active_crtc_count > 1) {
				for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) {
@@ -130,11 +129,11 @@ void r100_get_power_state(struct radeon_device *rdev,
					rdev->pm.current_power_state_index + 1;
		}
		break;
	case PM_ACTION_DEFAULT:
	case DYNPM_ACTION_DEFAULT:
		rdev->pm.requested_power_state_index = rdev->pm.default_power_state_index;
		rdev->pm.can_upclock = false;
		rdev->pm.dynpm_can_upclock = false;
		break;
	case PM_ACTION_NONE:
	case DYNPM_ACTION_NONE:
	default:
		DRM_ERROR("Requested mode for not defined action\n");
		return;
@@ -151,77 +150,33 @@ void r100_get_power_state(struct radeon_device *rdev,
		 pcie_lanes);
}

void r100_set_power_state(struct radeon_device *rdev, bool static_switch)
{
	u32 sclk, mclk;

	if (rdev->pm.current_power_state_index == rdev->pm.requested_power_state_index)
		return;

	if (radeon_gui_idle(rdev)) {

		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
			clock_info[rdev->pm.requested_clock_mode_index].sclk;
		if (sclk > rdev->clock.default_sclk)
			sclk = rdev->clock.default_sclk;

		mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
			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;

		/* voltage, pcie lanes, etc.*/
		radeon_pm_misc(rdev);

		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);
			}
			/* 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);
			}
			radeon_pm_finish(rdev);
		} else {
			radeon_sync_with_vblank(rdev);

			if (!radeon_pm_in_vbl(rdev))
				return;

			radeon_pm_prepare(rdev);
			/* set engine clock */
			if (sclk != rdev->pm.current_sclk) {
				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);
			}

			/* set memory clock */
			if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
				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);
			}
			radeon_pm_finish(rdev);
		}

		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
	} else
		DRM_INFO("pm: GUI not idle!!!\n");
void r100_pm_init_profile(struct radeon_device *rdev)
{
	/* default */
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
	/* low sh */
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
	/* high sh */
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0;
	/* low mh */
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
	/* high mh */
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0;
}

void r100_pm_misc(struct radeon_device *rdev)
@@ -3815,7 +3770,6 @@ int r100_suspend(struct radeon_device *rdev)

void r100_fini(struct radeon_device *rdev)
{
	radeon_pm_fini(rdev);
	r100_cp_fini(rdev);
	r100_wb_fini(rdev);
	r100_ib_fini(rdev);
@@ -3871,8 +3825,6 @@ int r100_init(struct radeon_device *rdev)
	r100_errata(rdev);
	/* Initialize clocks */
	radeon_get_clock_info(rdev->ddev);
	/* Initialize power management */
	radeon_pm_init(rdev);
	/* initialize AGP */
	if (rdev->flags & RADEON_IS_AGP) {
		r = radeon_agp_init(rdev);
+0 −3
Original line number Diff line number Diff line
@@ -1345,7 +1345,6 @@ int r300_suspend(struct radeon_device *rdev)

void r300_fini(struct radeon_device *rdev)
{
	radeon_pm_fini(rdev);
	r100_cp_fini(rdev);
	r100_wb_fini(rdev);
	r100_ib_fini(rdev);
@@ -1401,8 +1400,6 @@ int r300_init(struct radeon_device *rdev)
	r300_errata(rdev);
	/* Initialize clocks */
	radeon_get_clock_info(rdev->ddev);
	/* Initialize power management */
	radeon_pm_init(rdev);
	/* initialize AGP */
	if (rdev->flags & RADEON_IS_AGP) {
		r = radeon_agp_init(rdev);
+29 −3
Original line number Diff line number Diff line
@@ -36,6 +36,35 @@
#include "r420d.h"
#include "r420_reg_safe.h"

void r420_pm_init_profile(struct radeon_device *rdev)
{
	/* default */
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 0;
	/* low sh */
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = 1;
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0;
	/* high sh */
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 0;
	/* low mh */
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0;
	/* high mh */
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
	rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 0;
}

static void r420_set_reg_safe(struct radeon_device *rdev)
{
	rdev->config.r300.reg_safe_bm = r420_reg_safe_bm;
@@ -268,7 +297,6 @@ int r420_suspend(struct radeon_device *rdev)

void r420_fini(struct radeon_device *rdev)
{
	radeon_pm_fini(rdev);
	r100_cp_fini(rdev);
	r100_wb_fini(rdev);
	r100_ib_fini(rdev);
@@ -328,8 +356,6 @@ int r420_init(struct radeon_device *rdev)

	/* Initialize clocks */
	radeon_get_clock_info(rdev->ddev);
	/* Initialize power management */
	radeon_pm_init(rdev);
	/* initialize AGP */
	if (rdev->flags & RADEON_IS_AGP) {
		r = radeon_agp_init(rdev);
Loading