Commit 56278a8e authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

drm/radeon/kms: pull power mode info from bios tables (v3)

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: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent c836a412
......@@ -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;
};
......
This diff is collapsed.
......@@ -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);
......
......@@ -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)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment