drm/radeon/kms: pull power mode info from bios tables (v3)
authorAlex Deucher <alexdeucher@gmail.com>
Mon, 28 Dec 2009 18:58:44 +0000 (13:58 -0500)
committerDave Airlie <airlied@redhat.com>
Mon, 8 Feb 2010 23:32:27 +0000 (09:32 +1000)
The general idea is to validate the current hw state
against the set of power states and select a power
state based on that.  This patch just pulls the power
states from the bios and prints the information.  It
is not currently hooked up in the actual power management
code.  Hooking it up will require reworking the the current
power state selection code and will be handled in a future
patch.

Additionally, we'd need to decide on some default lower
power states for cards without power tables.

v2 - increment state_index after checking for default state
v3 - fix typo in pm init on pre-atom cards, handle pre-atom
cards without x86 bioses

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_atombios.c
drivers/gpu/drm/radeon/radeon_combios.c
drivers/gpu/drm/radeon/radeon_pm.c

index df21edcbae1689a1896c9b44c00067e33eb66330..d73d37d5effd6ae7e76256adb675ff642c676716 100644 (file)
@@ -150,6 +150,8 @@ struct radeon_clock {
  */
 int radeon_pm_init(struct radeon_device *rdev);
 void radeon_pm_compute_clocks(struct radeon_device *rdev);
+void radeon_combios_get_power_modes(struct radeon_device *rdev);
+void radeon_atombios_get_power_modes(struct radeon_device *rdev);
 
 /*
  * Fences.
@@ -583,6 +585,59 @@ enum radeon_pm_action {
        PM_ACTION_DOWNCLOCK,
        PM_ACTION_UPCLOCK
 };
+
+enum radeon_voltage_type {
+       VOLTAGE_NONE = 0,
+       VOLTAGE_GPIO,
+       VOLTAGE_VDDC,
+       VOLTAGE_SW
+};
+
+struct radeon_voltage {
+       enum radeon_voltage_type type;
+       /* gpio voltage */
+       struct radeon_gpio_rec gpio;
+       u32 delay; /* delay in usec from voltage drop to sclk change */
+       bool active_high; /* voltage drop is active when bit is high */
+       /* VDDC voltage */
+       u8 vddc_id; /* index into vddc voltage table */
+       u8 vddci_id; /* index into vddci voltage table */
+       bool vddci_enabled;
+       /* r6xx+ sw */
+       u32 voltage;
+};
+
+struct radeon_pm_non_clock_info {
+       /* pcie lanes */
+       int pcie_lanes;
+       /* standardized non-clock flags */
+       u32 flags;
+};
+
+struct radeon_pm_clock_info {
+       /* memory clock */
+       u32 mclk;
+       /* engine clock */
+       u32 sclk;
+       /* voltage info */
+       struct radeon_voltage voltage;
+       /* standardized clock flags - not sure we'll need these */
+       u32 flags;
+};
+
+struct radeon_power_state {
+       /* XXX: use a define for num clock modes */
+       struct radeon_pm_clock_info clock_info[8];
+       /* number of valid clock modes in this power state */
+       int num_clock_modes;
+       /* currently selected clock mode */
+       struct radeon_pm_clock_info *current_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;
+       bool voltage_drop_active;
+};
+
 struct radeon_pm {
        struct mutex            mutex;
        struct work_struct      reclock_work;
@@ -609,6 +664,12 @@ struct radeon_pm {
        fixed20_12              core_bandwidth;
        fixed20_12              sclk;
        fixed20_12              needed_bandwidth;
+       /* XXX: use a define for num power modes */
+       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_power_state *default_power_state;
 };
 
 
index d33fb4174b1338229d3aedb847e36c68a00eed28..adf900e368ac68aef6a31c97eaf0a808ea3287d5 100644 (file)
@@ -1393,6 +1393,305 @@ radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder)
        return tv_dac;
 }
 
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE info_4;
+};
+
+void radeon_atombios_get_power_modes(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+       u16 data_offset;
+       u8 frev, crev;
+       u32 misc, misc2 = 0, sclk, mclk;
+       union power_info *power_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       struct _ATOM_PPLIB_STATE *power_state;
+       int num_modes = 0, i, j;
+       int state_index = 0, mode_index = 0;
+
+       atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset);
+
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       rdev->pm.default_power_state = NULL;
+       rdev->pm.current_power_state = NULL;
+
+       if (power_info) {
+               if (frev < 4) {
+                       num_modes = power_info->info.ucNumOfPowerModeEntries;
+                       if (num_modes > ATOM_MAX_NUMBEROF_POWER_BLOCK)
+                               num_modes = ATOM_MAX_NUMBEROF_POWER_BLOCK;
+                       for (i = 0; i < num_modes; i++) {
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+                               switch (frev) {
+                               case 1:
+                                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                                       rdev->pm.power_state[state_index].clock_info[0].mclk =
+                                               le16_to_cpu(power_info->info.asPowerPlayInfo[i].usMemoryClock);
+                                       rdev->pm.power_state[state_index].clock_info[0].sclk =
+                                               le16_to_cpu(power_info->info.asPowerPlayInfo[i].usEngineClock);
+                                       /* skip invalid modes */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                                               continue;
+                                       /* skip overclock modes for now */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                                            rdev->clock.default_mclk) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                                            rdev->clock.default_sclk))
+                                               continue;
+                                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                               power_info->info.asPowerPlayInfo[i].ucNumPciELanes;
+                                       misc = le32_to_cpu(power_info->info.asPowerPlayInfo[i].ulMiscInfo);
+                                       if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_GPIO;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio =
+                                                       radeon_lookup_gpio(rdev,
+                                                       power_info->info.asPowerPlayInfo[i].ucVoltageDropIndex);
+                                               if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               true;
+                                               else
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               false;
+                                       } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_VDDC;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id =
+                                                       power_info->info.asPowerPlayInfo[i].ucVoltageDropIndex;
+                                       }
+                                       if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE) {
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.current_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                               rdev->pm.power_state[state_index].current_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                       }
+                                       state_index++;
+                                       break;
+                               case 2:
+                                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                                       rdev->pm.power_state[state_index].clock_info[0].mclk =
+                                               le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMemoryClock);
+                                       rdev->pm.power_state[state_index].clock_info[0].sclk =
+                                               le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulEngineClock);
+                                       /* skip invalid modes */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                                               continue;
+                                       /* skip overclock modes for now */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                                            rdev->clock.default_mclk) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                                            rdev->clock.default_sclk))
+                                               continue;
+                                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                               power_info->info_2.asPowerPlayInfo[i].ucNumPciELanes;
+                                       misc = le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMiscInfo);
+                                       misc2 = le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMiscInfo2);
+                                       if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_GPIO;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio =
+                                                       radeon_lookup_gpio(rdev,
+                                                       power_info->info_2.asPowerPlayInfo[i].ucVoltageDropIndex);
+                                               if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               true;
+                                               else
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               false;
+                                       } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_VDDC;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id =
+                                                       power_info->info_2.asPowerPlayInfo[i].ucVoltageDropIndex;
+                                       }
+                                       if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE) {
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.current_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                               rdev->pm.power_state[state_index].current_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                       }
+                                       state_index++;
+                                       break;
+                               case 3:
+                                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                                       rdev->pm.power_state[state_index].clock_info[0].mclk =
+                                               le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMemoryClock);
+                                       rdev->pm.power_state[state_index].clock_info[0].sclk =
+                                               le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulEngineClock);
+                                       /* skip invalid modes */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                                               continue;
+                                       /* skip overclock modes for now */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                                            rdev->clock.default_mclk) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                                            rdev->clock.default_sclk))
+                                               continue;
+                                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                               power_info->info_3.asPowerPlayInfo[i].ucNumPciELanes;
+                                       misc = le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMiscInfo);
+                                       misc2 = le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMiscInfo2);
+                                       if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_GPIO;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio =
+                                                       radeon_lookup_gpio(rdev,
+                                                       power_info->info_3.asPowerPlayInfo[i].ucVoltageDropIndex);
+                                               if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               true;
+                                               else
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               false;
+                                       } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_VDDC;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id =
+                                                       power_info->info_3.asPowerPlayInfo[i].ucVoltageDropIndex;
+                                               if (misc2 & ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN) {
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.vddci_enabled =
+                                                               true;
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.vddci_id =
+                                                       power_info->info_3.asPowerPlayInfo[i].ucVDDCI_VoltageDropIndex;
+                                               }
+                                       }
+                                       if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE) {
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.current_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                               rdev->pm.power_state[state_index].current_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                       }
+                                       state_index++;
+                                       break;
+                               }
+                       }
+               } else if (frev == 4) {
+                       for (i = 0; i < power_info->info_4.ucNumStates; i++) {
+                               mode_index = 0;
+                               power_state = (struct _ATOM_PPLIB_STATE *)
+                                       (mode_info->atom_context->bios +
+                                        data_offset +
+                                        le16_to_cpu(power_info->info_4.usStateArrayOffset) +
+                                        i * power_info->info_4.ucStateEntrySize);
+                               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                                       (mode_info->atom_context->bios +
+                                        data_offset +
+                                        le16_to_cpu(power_info->info_4.usNonClockInfoArrayOffset) +
+                                        (power_state->ucNonClockStateIndex *
+                                         power_info->info_4.ucNonClockSize));
+                               misc = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+                               rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                       ((misc & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >>
+                                        ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
+                               for (j = 0; j < (power_info->info_4.ucStateEntrySize - 1); j++) {
+                                       if (rdev->flags & RADEON_IS_IGP) {
+                                               struct _ATOM_PPLIB_RS780_CLOCK_INFO *clock_info =
+                                                       (struct _ATOM_PPLIB_RS780_CLOCK_INFO *)
+                                                       (mode_info->atom_context->bios +
+                                                        data_offset +
+                                                        le16_to_cpu(power_info->info_4.usClockInfoArrayOffset) +
+                                                        (power_state->ucClockStateIndices[j] *
+                                                         power_info->info_4.ucClockInfoSize));
+                                               sclk = le16_to_cpu(clock_info->usLowEngineClockLow);
+                                               sclk |= clock_info->ucLowEngineClockHigh << 16;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
+                                               /* skip invalid modes */
+                                               if (rdev->pm.power_state[state_index].clock_info[mode_index].sclk == 0)
+                                                       continue;
+                                               /* skip overclock modes for now */
+                                               if (rdev->pm.power_state[state_index].clock_info[mode_index].sclk >
+                                                   rdev->clock.default_sclk)
+                                                       continue;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type =
+                                                       VOLTAGE_SW;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage =
+                                                       clock_info->usVDDC;
+                                               mode_index++;
+                                       } else {
+                                               struct _ATOM_PPLIB_R600_CLOCK_INFO *clock_info =
+                                                       (struct _ATOM_PPLIB_R600_CLOCK_INFO *)
+                                                       (mode_info->atom_context->bios +
+                                                        data_offset +
+                                                        le16_to_cpu(power_info->info_4.usClockInfoArrayOffset) +
+                                                        (power_state->ucClockStateIndices[j] *
+                                                         power_info->info_4.ucClockInfoSize));
+                                               sclk = le16_to_cpu(clock_info->usEngineClockLow);
+                                               sclk |= clock_info->ucEngineClockHigh << 16;
+                                               mclk = le16_to_cpu(clock_info->usMemoryClockLow);
+                                               mclk |= clock_info->ucMemoryClockHigh << 16;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
+                                               /* skip invalid modes */
+                                               if ((rdev->pm.power_state[state_index].clock_info[mode_index].mclk == 0) ||
+                                                   (rdev->pm.power_state[state_index].clock_info[mode_index].sclk == 0))
+                                                       continue;
+                                               /* skip overclock modes for now */
+                                               if ((rdev->pm.power_state[state_index].clock_info[mode_index].mclk >
+                                                    rdev->clock.default_mclk) ||
+                                                   (rdev->pm.power_state[state_index].clock_info[mode_index].sclk >
+                                                    rdev->clock.default_sclk))
+                                                       continue;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type =
+                                                       VOLTAGE_SW;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage =
+                                                       clock_info->usVDDC;
+                                               mode_index++;
+                                       }
+                               }
+                               rdev->pm.power_state[state_index].num_clock_modes = mode_index;
+                               if (mode_index) {
+                                       misc2 = le16_to_cpu(non_clock_info->usClassification);
+                                       if (misc2 & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.current_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[mode_index - 1];
+                                               rdev->pm.power_state[state_index].current_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[mode_index - 1];
+                                       }
+                                       state_index++;
+                               }
+                       }
+               }
+       } else {
+               /* XXX figure out some good default low power mode for cards w/out power tables */
+       }
+
+       if (rdev->pm.default_power_state == NULL) {
+               /* add the default mode */
+               rdev->pm.power_state[state_index].num_clock_modes = 1;
+               rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk;
+               rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk;
+               rdev->pm.power_state[state_index].default_clock_mode =
+                       &rdev->pm.power_state[state_index].clock_info[0];
+               rdev->pm.power_state[state_index].current_clock_mode =
+                       &rdev->pm.power_state[state_index].clock_info[0];
+               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+               if (rdev->asic->get_pcie_lanes)
+                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = radeon_get_pcie_lanes(rdev);
+               else
+                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
+               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+               rdev->pm.current_power_state = &rdev->pm.power_state[state_index];
+               state_index++;
+       }
+       rdev->pm.num_power_states = state_index;
+}
+
 void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable)
 {
        DYNAMIC_CLOCK_GATING_PS_ALLOCATION args;
index 257ce1774e40b65d7f0f311de23685a96da886f0..eac2174abc5b29368a33a9cbf5184ebf6b056e19 100644 (file)
@@ -2374,6 +2374,114 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
        return true;
 }
 
+void radeon_combios_get_power_modes(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       u16 offset, misc, misc2 = 0;
+       u8 rev, blocks, tmp;
+       int state_index = 0;
+
+       rdev->pm.default_power_state = NULL;
+       rdev->pm.current_power_state = NULL;
+
+       /* XXX mac/sparc cards */
+       if (rdev->bios == NULL)
+               goto default_mode;
+
+       if (rdev->flags & RADEON_IS_MOBILITY) {
+               offset = combios_get_table_offset(dev, COMBIOS_POWERPLAY_INFO_TABLE);
+               if (offset) {
+                       rev = RBIOS8(offset);
+                       blocks = RBIOS8(offset + 0x2);
+                       /* power mode 0 tends to be the only valid one */
+                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                       rdev->pm.power_state[state_index].clock_info[0].mclk = RBIOS32(offset + 0x5 + 0x2);
+                       rdev->pm.power_state[state_index].clock_info[0].sclk = RBIOS32(offset + 0x5 + 0x6);
+                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                               goto default_mode;
+                       /* skip overclock modes for now */
+                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                            rdev->clock.default_mclk) ||
+                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                            rdev->clock.default_sclk))
+                               goto default_mode;
+                       misc = RBIOS16(offset + 0x5 + 0x0);
+                       if (rev > 4)
+                               misc2 = RBIOS16(offset + 0x5 + 0xe);
+                       if (misc & 0x4) {
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_GPIO;
+                               if (misc & 0x8)
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                               true;
+                               else
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                               false;
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.valid = true;
+                               if (rev < 6) {
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.reg =
+                                               RBIOS16(offset + 0x5 + 0xb) * 4;
+                                       tmp = RBIOS8(offset + 0x5 + 0xd);
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.mask = (1 << tmp);
+                               } else {
+                                       u8 entries = RBIOS8(offset + 0x5 + 0xb);
+                                       u16 voltage_table_offset = RBIOS16(offset + 0x5 + 0xc);
+                                       if (entries && voltage_table_offset) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.reg =
+                                                       RBIOS16(voltage_table_offset) * 4;
+                                               tmp = RBIOS8(voltage_table_offset + 0x2);
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.mask = (1 << tmp);
+                                       } else
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.valid = false;
+                               }
+                               switch ((misc2 & 0x700) >> 8) {
+                               case 0:
+                               default:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 0;
+                                       break;
+                               case 1:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 33;
+                                       break;
+                               case 2:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 66;
+                                       break;
+                               case 3:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 99;
+                                       break;
+                               case 4:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 132;
+                                       break;
+                               }
+                       } else
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+                       if (rev > 6)
+                               rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                       RBIOS8(offset + 0x5 + 0x10);
+                       state_index++;
+               } else {
+                       /* XXX figure out some good default low power mode for mobility cards w/out power tables */
+               }
+       } else {
+               /* XXX figure out some good default low power mode for desktop cards */
+       }
+
+default_mode:
+       /* add the default mode */
+       rdev->pm.power_state[state_index].num_clock_modes = 1;
+       rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk;
+       rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk;
+       rdev->pm.power_state[state_index].default_clock_mode = &rdev->pm.power_state[state_index].clock_info[0];
+       rdev->pm.power_state[state_index].current_clock_mode = &rdev->pm.power_state[state_index].clock_info[0];
+       rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+       if (rdev->asic->get_pcie_lanes)
+               rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = radeon_get_pcie_lanes(rdev);
+       else
+               rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
+       rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+       rdev->pm.current_power_state = &rdev->pm.power_state[state_index];
+       rdev->pm.num_power_states = state_index + 1;
+}
+
 void radeon_external_tmds_setup(struct drm_encoder *encoder)
 {
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
index a9c61f435c06f5866a6b259734dfee47c3172302..6eb0e0b3264ba226259b57326a6e12f9329a0c4b 100644 (file)
@@ -18,6 +18,7 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  *
  * Authors: Rafał Miłecki <zajec5@gmail.com>
+ *          Alex Deucher <alexdeucher@gmail.com>
  */
 #include "drmP.h"
 #include "radeon.h"
@@ -39,6 +40,35 @@ static const char *pm_state_names[4] = {
        "PM_STATE_ACTIVE"
 };
 
+static void radeon_print_power_mode_info(struct radeon_device *rdev)
+{
+       int i, j;
+       bool is_default;
+
+       DRM_INFO("%d Power State(s)\n", rdev->pm.num_power_states);
+       for (i = 0; i < rdev->pm.num_power_states; i++) {
+               if (rdev->pm.default_power_state == &rdev->pm.power_state[i])
+                       is_default = true;
+               else
+                       is_default = false;
+               DRM_INFO("State %d %s\n", i, is_default ? "(default)" : "");
+               if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP))
+                       DRM_INFO("\t%d PCIE Lanes\n", rdev->pm.power_state[i].non_clock_info.pcie_lanes);
+               DRM_INFO("\t%d Clock Mode(s)\n", rdev->pm.power_state[i].num_clock_modes);
+               for (j = 0; j < rdev->pm.power_state[i].num_clock_modes; j++) {
+                       if (rdev->flags & RADEON_IS_IGP)
+                               DRM_INFO("\t\t%d engine: %d\n",
+                                        j,
+                                        rdev->pm.power_state[i].clock_info[j].sclk * 10);
+                       else
+                               DRM_INFO("\t\t%d engine/memory: %d/%d\n",
+                                        j,
+                                        rdev->pm.power_state[i].clock_info[j].sclk * 10,
+                                        rdev->pm.power_state[i].clock_info[j].mclk * 10);
+               }
+       }
+}
+
 int radeon_pm_init(struct radeon_device *rdev)
 {
        rdev->pm.state = PM_STATE_DISABLED;
@@ -46,6 +76,14 @@ int radeon_pm_init(struct radeon_device *rdev)
        rdev->pm.downclocked = false;
        rdev->pm.vblank_callback = false;
 
+       if (rdev->bios) {
+               if (rdev->is_atom_bios)
+                       radeon_atombios_get_power_modes(rdev);
+               else
+                       radeon_combios_get_power_modes(rdev);
+               radeon_print_power_mode_info(rdev);
+       }
+
        radeon_pm_check_limits(rdev);
 
        if (radeon_debugfs_pm_init(rdev)) {