drm/radeon/kms: add code to select power state
authorAlex Deucher <alexdeucher@gmail.com>
Wed, 23 Dec 2009 19:28:05 +0000 (14:28 -0500)
committerDave Airlie <airlied@redhat.com>
Mon, 8 Feb 2010 23:32:29 +0000 (09:32 +1000)
not hooked up yet.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_pm.c

index 53468680de70588100888e477ed3c887c5322bbe..ece84fde76a6c4281e0250dba1515900d619034a 100644 (file)
@@ -601,6 +601,13 @@ enum radeon_pm_state_type {
        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 @@ struct radeon_power_state {
        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 @@ struct radeon_pm {
        /* 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;
 };
 
index 93ba0fb27e9d4e6f939c0369dc6a793c3630d9f2..87d2776624bbbc86e54a90c059040b316d9716d2 100644 (file)
@@ -79,6 +79,128 @@ static void radeon_print_power_mode_info(struct radeon_device *rdev)
        }
 }
 
+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;