drm/radeon/kms: add code to select power state

not hooked up yet.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 5346868..ece84fd 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -601,6 +601,13 @@
 	POWER_STATE_TYPE_PERFORMANCE,
 };
 
+enum radeon_pm_clock_mode_type {
+	POWER_MODE_TYPE_DEFAULT,
+	POWER_MODE_TYPE_LOW,
+	POWER_MODE_TYPE_MID,
+	POWER_MODE_TYPE_HIGH,
+};
+
 struct radeon_voltage {
 	enum radeon_voltage_type type;
 	/* gpio voltage */
@@ -641,6 +648,7 @@
 	int num_clock_modes;
 	/* currently selected clock mode */
 	struct radeon_pm_clock_info *current_clock_mode;
+	struct radeon_pm_clock_info *requested_clock_mode;
 	struct radeon_pm_clock_info *default_clock_mode;
 	/* non clock info about this state */
 	struct radeon_pm_non_clock_info non_clock_info;
@@ -678,6 +686,7 @@
 	/* number of valid power states */
 	int                     num_power_states;
 	struct radeon_power_state *current_power_state;
+	struct radeon_power_state *requested_power_state;
 	struct radeon_power_state *default_power_state;
 };
 
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 93ba0fb..87d2776 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -79,6 +79,128 @@
 	}
 }
 
+static struct radeon_power_state * radeon_pick_power_state(struct radeon_device *rdev,
+							   enum radeon_pm_state_type type)
+{
+	int i;
+	struct radeon_power_state *power_state = NULL;
+
+	switch (type) {
+	case POWER_STATE_TYPE_DEFAULT:
+	default:
+		return rdev->pm.default_power_state;
+	case POWER_STATE_TYPE_POWERSAVE:
+		for (i = 0; i < rdev->pm.num_power_states; i++) {
+			if (rdev->pm.power_state[i].type == POWER_STATE_TYPE_POWERSAVE) {
+				power_state = &rdev->pm.power_state[i];
+				break;
+			}
+		}
+		if (power_state == NULL) {
+			for (i = 0; i < rdev->pm.num_power_states; i++) {
+				if (rdev->pm.power_state[i].type == POWER_STATE_TYPE_BATTERY) {
+					power_state = &rdev->pm.power_state[i];
+					break;
+				}
+			}
+		}
+		break;
+	case POWER_STATE_TYPE_BATTERY:
+		for (i = 0; i < rdev->pm.num_power_states; i++) {
+			if (rdev->pm.power_state[i].type == POWER_STATE_TYPE_BATTERY) {
+				power_state = &rdev->pm.power_state[i];
+				break;
+			}
+		}
+		if (power_state == NULL) {
+			for (i = 0; i < rdev->pm.num_power_states; i++) {
+				if (rdev->pm.power_state[i].type == POWER_STATE_TYPE_POWERSAVE) {
+					power_state = &rdev->pm.power_state[i];
+					break;
+				}
+			}
+		}
+		break;
+	case POWER_STATE_TYPE_BALANCED:
+	case POWER_STATE_TYPE_PERFORMANCE:
+		for (i = 0; i < rdev->pm.num_power_states; i++) {
+			if (rdev->pm.power_state[i].type == type) {
+				power_state = &rdev->pm.power_state[i];
+				break;
+			}
+		}
+		break;
+	}
+
+	if (power_state == NULL)
+		return rdev->pm.default_power_state;
+
+	return power_state;
+}
+
+static struct radeon_pm_clock_info * radeon_pick_clock_mode(struct radeon_device *rdev,
+							    struct radeon_power_state *power_state,
+							    enum radeon_pm_clock_mode_type type)
+{
+	switch (type) {
+	case POWER_MODE_TYPE_DEFAULT:
+	default:
+		return power_state->default_clock_mode;
+	case POWER_MODE_TYPE_LOW:
+		return &power_state->clock_info[0];
+	case POWER_MODE_TYPE_MID:
+		if (power_state->num_clock_modes > 2)
+			return &power_state->clock_info[1];
+		else
+			return &power_state->clock_info[0];
+		break;
+	case POWER_MODE_TYPE_HIGH:
+		return &power_state->clock_info[power_state->num_clock_modes - 1];
+	}
+
+}
+
+static void radeon_get_power_state(struct radeon_device *rdev,
+				   enum radeon_pm_action action)
+{
+	switch (action) {
+	case PM_ACTION_NONE:
+	default:
+		rdev->pm.requested_power_state = rdev->pm.current_power_state;
+		rdev->pm.requested_power_state->requested_clock_mode =
+			rdev->pm.requested_power_state->current_clock_mode;
+		break;
+	case PM_ACTION_MINIMUM:
+		rdev->pm.requested_power_state = radeon_pick_power_state(rdev, POWER_STATE_TYPE_BATTERY);
+		rdev->pm.requested_power_state->requested_clock_mode =
+			radeon_pick_clock_mode(rdev, rdev->pm.requested_power_state, POWER_MODE_TYPE_LOW);
+		break;
+	case PM_ACTION_DOWNCLOCK:
+		rdev->pm.requested_power_state = radeon_pick_power_state(rdev, POWER_STATE_TYPE_POWERSAVE);
+		rdev->pm.requested_power_state->requested_clock_mode =
+			radeon_pick_clock_mode(rdev, rdev->pm.requested_power_state, POWER_MODE_TYPE_MID);
+		break;
+	case PM_ACTION_UPCLOCK:
+		rdev->pm.requested_power_state = radeon_pick_power_state(rdev, POWER_STATE_TYPE_DEFAULT);
+		rdev->pm.requested_power_state->requested_clock_mode =
+			radeon_pick_clock_mode(rdev, rdev->pm.requested_power_state, POWER_MODE_TYPE_HIGH);
+		break;
+	}
+}
+
+static void radeon_set_power_state(struct radeon_device *rdev)
+{
+	if (rdev->pm.requested_power_state == rdev->pm.current_power_state)
+		return;
+	/* set pcie lanes */
+	/* set voltage */
+	/* set engine clock */
+	radeon_set_engine_clock(rdev, rdev->pm.requested_power_state->requested_clock_mode->sclk);
+	/* set memory clock */
+
+	rdev->pm.current_power_state = rdev->pm.requested_power_state;
+}
+
 int radeon_pm_init(struct radeon_device *rdev)
 {
 	rdev->pm.state = PM_STATE_DISABLED;