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

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

drm/radeon/kms/pm: add asic specific callbacks for getting power state (v2)



This also simplifies the code and enables reclocking with multiple heads
active by tracking whether the power states are single or multi-head
capable.

Eventually, we will want to select a power state based on external
factors (AC/DC state, user selection, etc.).

(v2) Update for evergreen

Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent bae6b562
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -251,6 +251,7 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
		atombios_blank_crtc(crtc, ATOM_DISABLE);
		drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
		radeon_crtc_load_lut(crtc);
		radeon_crtc->enabled = true;
		break;
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
@@ -260,6 +261,7 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
		if (ASIC_IS_DCE3(rdev))
			atombios_enable_crtc_memreq(crtc, ATOM_DISABLE);
		atombios_enable_crtc(crtc, ATOM_DISABLE);
		radeon_crtc->enabled = false;
		break;
	}

+116 −24
Original line number Diff line number Diff line
@@ -67,16 +67,100 @@ 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)
{
	int i;
	rdev->pm.can_upclock = true;
	rdev->pm.can_downclock = true;

	switch (action) {
	case PM_ACTION_MINIMUM:
		rdev->pm.requested_power_state_index = 0;
		rdev->pm.can_downclock = false;
		break;
	case PM_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;
		} else {
			if (rdev->pm.active_crtc_count > 1) {
				for (i = 0; i < rdev->pm.num_power_states; i++) {
					if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
						continue;
					else if (i >= rdev->pm.current_power_state_index) {
						rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
						break;
					} else {
						rdev->pm.requested_power_state_index = i;
						break;
					}
				}
			} else
				rdev->pm.requested_power_state_index =
					rdev->pm.current_power_state_index - 1;
		}
		break;
	case PM_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;
		} else {
			if (rdev->pm.active_crtc_count > 1) {
				for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) {
					if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
						continue;
					else if (i <= rdev->pm.current_power_state_index) {
						rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
						break;
					} else {
						rdev->pm.requested_power_state_index = i;
						break;
					}
				}
			} else
				rdev->pm.requested_power_state_index =
					rdev->pm.current_power_state_index + 1;
		}
		break;
	case PM_ACTION_NONE:
	default:
		DRM_ERROR("Requested mode for not defined action\n");
		return;
	}
	/* only one clock mode per power state */
	rdev->pm.requested_clock_mode_index = 0;

	DRM_INFO("Requested: e: %d m: %d p: %d\n",
		 rdev->pm.power_state[rdev->pm.requested_power_state_index].
		 clock_info[rdev->pm.requested_clock_mode_index].sclk,
		 rdev->pm.power_state[rdev->pm.requested_power_state_index].
		 clock_info[rdev->pm.requested_clock_mode_index].mclk,
		 rdev->pm.power_state[rdev->pm.requested_power_state_index].
		 non_clock_info.pcie_lanes);
}

void r100_set_power_state(struct radeon_device *rdev)
{
	/* if *_clock_mode are the same, *_power_state are as well */
	if (rdev->pm.requested_clock_mode == rdev->pm.current_clock_mode)
	u32 sclk, mclk;

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

	DRM_INFO("Setting: e: %d m: %d p: %d\n",
		 rdev->pm.requested_clock_mode->sclk,
		 rdev->pm.requested_clock_mode->mclk,
		 rdev->pm.requested_power_state->non_clock_info.pcie_lanes);
	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;

		/* set pcie lanes */
		/* TODO */
@@ -85,23 +169,31 @@ void r100_set_power_state(struct radeon_device *rdev)
		/* 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, rdev->pm.requested_clock_mode->sclk);
			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) {
		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, rdev->pm.requested_clock_mode->mclk);
			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);
		}
#endif

	rdev->pm.current_power_state = rdev->pm.requested_power_state;
	rdev->pm.current_clock_mode = rdev->pm.requested_clock_mode;
		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("GUI not idle!!!\n");
}

bool r100_gui_idle(struct radeon_device *rdev)
+191 −26
Original line number Diff line number Diff line
@@ -92,16 +92,173 @@ void r600_gpu_init(struct radeon_device *rdev);
void r600_fini(struct radeon_device *rdev);
void r600_irq_disable(struct radeon_device *rdev);

void r600_get_power_state(struct radeon_device *rdev,
			  enum radeon_pm_action action)
{
	int i;

	rdev->pm.can_upclock = true;
	rdev->pm.can_downclock = true;

	/* power state array is low to high, default is first */
	if ((rdev->flags & RADEON_IS_IGP) || (rdev->family == CHIP_R600)) {
		int min_power_state_index = 0;

		if (rdev->pm.num_power_states > 2)
			min_power_state_index = 1;

		switch (action) {
		case PM_ACTION_MINIMUM:
			rdev->pm.requested_power_state_index = min_power_state_index;
			rdev->pm.requested_clock_mode_index = 0;
			rdev->pm.can_downclock = false;
			break;
		case PM_ACTION_DOWNCLOCK:
			if (rdev->pm.current_power_state_index == min_power_state_index) {
				rdev->pm.requested_power_state_index = rdev->pm.current_power_state_index;
				rdev->pm.can_downclock = false;
			} else {
				if (rdev->pm.active_crtc_count > 1) {
					for (i = 0; i < rdev->pm.num_power_states; i++) {
						if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
							continue;
						else if (i >= rdev->pm.current_power_state_index) {
							rdev->pm.requested_power_state_index =
								rdev->pm.current_power_state_index;
							break;
						} else {
							rdev->pm.requested_power_state_index = i;
							break;
						}
					}
				} else
					rdev->pm.requested_power_state_index =
						rdev->pm.current_power_state_index - 1;
			}
			rdev->pm.requested_clock_mode_index = 0;
			break;
		case PM_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;
			} else {
				if (rdev->pm.active_crtc_count > 1) {
					for (i = (rdev->pm.num_power_states - 1); i >= 0; i--) {
						if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
							continue;
						else if (i <= rdev->pm.current_power_state_index) {
							rdev->pm.requested_power_state_index =
								rdev->pm.current_power_state_index;
							break;
						} else {
							rdev->pm.requested_power_state_index = i;
							break;
						}
					}
				} else
					rdev->pm.requested_power_state_index =
						rdev->pm.current_power_state_index + 1;
			}
			rdev->pm.requested_clock_mode_index = 0;
			break;
		case PM_ACTION_NONE:
		default:
			DRM_ERROR("Requested mode for not defined action\n");
			return;
		}
	} else {
		/* XXX select a power state based on AC/DC, single/dualhead, etc. */
		/* for now just select the first power state and switch between clock modes */
		/* power state array is low to high, default is first (0) */
		if (rdev->pm.active_crtc_count > 1) {
			rdev->pm.requested_power_state_index = -1;
			/* start at 1 as we don't want the default mode */
			for (i = 1; i < rdev->pm.num_power_states; i++) {
				if (rdev->pm.power_state[i].flags & RADEON_PM_SINGLE_DISPLAY_ONLY)
					continue;
				else if ((rdev->pm.power_state[i].type == POWER_STATE_TYPE_PERFORMANCE) ||
					 (rdev->pm.power_state[i].type == POWER_STATE_TYPE_BATTERY)) {
					rdev->pm.requested_power_state_index = i;
					break;
				}
			}
			/* if nothing selected, grab the default state. */
			if (rdev->pm.requested_power_state_index == -1)
				rdev->pm.requested_power_state_index = 0;
		} else
			rdev->pm.requested_power_state_index = 1;

		switch (action) {
		case PM_ACTION_MINIMUM:
			rdev->pm.requested_clock_mode_index = 0;
			rdev->pm.can_downclock = false;
			break;
		case PM_ACTION_DOWNCLOCK:
			if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) {
				if (rdev->pm.current_clock_mode_index == 0) {
					rdev->pm.requested_clock_mode_index = 0;
					rdev->pm.can_downclock = false;
				} else
					rdev->pm.requested_clock_mode_index =
						rdev->pm.current_clock_mode_index - 1;
			} else {
				rdev->pm.requested_clock_mode_index = 0;
				rdev->pm.can_downclock = false;
			}
			break;
		case PM_ACTION_UPCLOCK:
			if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) {
				if (rdev->pm.current_clock_mode_index ==
				    (rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1)) {
					rdev->pm.requested_clock_mode_index = rdev->pm.current_clock_mode_index;
					rdev->pm.can_upclock = false;
				} else
					rdev->pm.requested_clock_mode_index =
						rdev->pm.current_clock_mode_index + 1;
			} else {
				rdev->pm.requested_clock_mode_index =
					rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1;
				rdev->pm.can_upclock = false;
			}
			break;
		case PM_ACTION_NONE:
		default:
			DRM_ERROR("Requested mode for not defined action\n");
			return;
		}
	}

	DRM_INFO("Requested: e: %d m: %d p: %d\n",
		 rdev->pm.power_state[rdev->pm.requested_power_state_index].
		 clock_info[rdev->pm.requested_clock_mode_index].sclk,
		 rdev->pm.power_state[rdev->pm.requested_power_state_index].
		 clock_info[rdev->pm.requested_clock_mode_index].mclk,
		 rdev->pm.power_state[rdev->pm.requested_power_state_index].
		 non_clock_info.pcie_lanes);
}

void r600_set_power_state(struct radeon_device *rdev)
{
	/* if *_clock_mode are the same, *_power_state are as well */
	if (rdev->pm.requested_clock_mode == rdev->pm.current_clock_mode)
	u32 sclk, mclk;

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

	DRM_INFO("Setting: e: %d m: %d p: %d\n",
		 rdev->pm.requested_clock_mode->sclk,
		 rdev->pm.requested_clock_mode->mclk,
		 rdev->pm.requested_power_state->non_clock_info.pcie_lanes);
	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;

		/* set pcie lanes */
		/* TODO */
@@ -110,23 +267,31 @@ void r600_set_power_state(struct radeon_device *rdev)
		/* 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, rdev->pm.requested_clock_mode->sclk);
			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) {
		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, rdev->pm.requested_clock_mode->mclk);
			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);
		}
#endif

	rdev->pm.current_power_state = rdev->pm.requested_power_state;
	rdev->pm.current_clock_mode = rdev->pm.requested_clock_mode;
		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("GUI not idle!!!\n");
}

bool r600_gui_idle(struct radeon_device *rdev)
+18 −7
Original line number Diff line number Diff line
@@ -672,6 +672,9 @@ struct radeon_pm_clock_info {
	u32 flags;
};

/* state flags */
#define RADEON_PM_SINGLE_DISPLAY_ONLY (1 << 0)

struct radeon_power_state {
	enum radeon_pm_state_type type;
	/* XXX: use a define for num clock modes */
@@ -682,6 +685,8 @@ struct radeon_power_state {
	/* non clock info about this state */
	struct radeon_pm_non_clock_info non_clock_info;
	bool voltage_drop_active;
	/* standardized state flags */
	u32 flags;
};

/*
@@ -695,8 +700,10 @@ struct radeon_pm {
	enum radeon_pm_state	state;
	enum radeon_pm_action	planned_action;
	unsigned long		action_timeout;
	bool 			downclocked;
	int			active_crtcs;
	bool                    can_upclock;
	bool                    can_downclock;
	u32			active_crtcs;
	int			active_crtc_count;
	int			req_vblank;
	bool			vblank_sync;
	bool			gui_idle;
@@ -716,11 +723,13 @@ struct radeon_pm {
	struct radeon_power_state power_state[8];
	/* number of valid power states */
	int                     num_power_states;
	struct radeon_power_state *current_power_state;
	struct radeon_pm_clock_info *current_clock_mode;
	struct radeon_power_state *requested_power_state;
	struct radeon_pm_clock_info *requested_clock_mode;
	struct radeon_power_state *default_power_state;
	int                     current_power_state_index;
	int                     current_clock_mode_index;
	int                     requested_power_state_index;
	int                     requested_clock_mode_index;
	int                     default_power_state_index;
	u32                     current_sclk;
	u32                     current_mclk;
	struct radeon_i2c_chan *i2c_bus;
};

@@ -810,6 +819,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);
};

@@ -1218,6 +1228,7 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
#define radeon_hpd_sense(rdev, hpd) (rdev)->asic->hpd_sense((rdev), (hpd))
#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))

/* Common functions */
+14 −0
Original line number Diff line number Diff line
@@ -166,6 +166,7 @@ static struct radeon_asic r100_asic = {
	.hpd_set_polarity = &r100_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -206,6 +207,7 @@ static struct radeon_asic r200_asic = {
	.hpd_set_polarity = &r100_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -247,6 +249,7 @@ static struct radeon_asic r300_asic = {
	.hpd_set_polarity = &r100_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -287,6 +290,7 @@ static struct radeon_asic r300_asic_pcie = {
	.hpd_set_polarity = &r100_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -328,6 +332,7 @@ static struct radeon_asic r420_asic = {
	.hpd_set_polarity = &r100_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -369,6 +374,7 @@ static struct radeon_asic rs400_asic = {
	.hpd_set_polarity = &r100_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -410,6 +416,7 @@ static struct radeon_asic rs600_asic = {
	.hpd_set_polarity = &rs600_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -451,6 +458,7 @@ static struct radeon_asic rs690_asic = {
	.hpd_set_polarity = &rs600_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -492,6 +500,7 @@ static struct radeon_asic rv515_asic = {
	.hpd_set_polarity = &rs600_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -533,6 +542,7 @@ static struct radeon_asic r520_asic = {
	.hpd_set_polarity = &rs600_hpd_set_polarity,
	.ioctl_wait_idle = NULL,
	.gui_idle = &r100_gui_idle,
	.get_power_state = &r100_get_power_state,
	.set_power_state = &r100_set_power_state,
};

@@ -573,6 +583,7 @@ static struct radeon_asic r600_asic = {
	.hpd_set_polarity = &r600_hpd_set_polarity,
	.ioctl_wait_idle = r600_ioctl_wait_idle,
	.gui_idle = &r600_gui_idle,
	.get_power_state = &r600_get_power_state,
	.set_power_state = &r600_set_power_state,
};

@@ -613,6 +624,7 @@ static struct radeon_asic rs780_asic = {
	.hpd_set_polarity = &r600_hpd_set_polarity,
	.ioctl_wait_idle = r600_ioctl_wait_idle,
	.gui_idle = &r600_gui_idle,
	.get_power_state = &r600_get_power_state,
	.set_power_state = &r600_set_power_state,
};

@@ -653,6 +665,7 @@ static struct radeon_asic rv770_asic = {
	.hpd_set_polarity = &r600_hpd_set_polarity,
	.ioctl_wait_idle = r600_ioctl_wait_idle,
	.gui_idle = &r600_gui_idle,
	.get_power_state = &r600_get_power_state,
	.set_power_state = &r600_set_power_state,
};

@@ -691,6 +704,7 @@ static struct radeon_asic evergreen_asic = {
	.hpd_sense = &evergreen_hpd_sense,
	.hpd_set_polarity = &evergreen_hpd_set_polarity,
	.gui_idle = &r600_gui_idle,
	.get_power_state = &r600_get_power_state,
	.set_power_state = &r600_set_power_state,
};

Loading