From 9d2ff48d6955d6435ee68602f521b0bb6e85a460 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 4 Jun 2010 00:34:39 -0400 Subject: [PATCH] drm/radeon/kms/pm: step through voltage levels Step incrementally through voltage levels when changing voltage rather than setting the new value directly. May fix: https://bugs.freedesktop.org/show_bug.cgi?id=28375 Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/evergreen.c | 9 ++++- drivers/gpu/drm/radeon/r600.c | 10 ++++-- drivers/gpu/drm/radeon/radeon.h | 4 ++ drivers/gpu/drm/radeon/radeon_atombios.c | 53 ++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/radeon_pm.c | 1 + drivers/gpu/drm/radeon/rv770.c | 9 ++++- 6 files changed, 79 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 2fa10a2..2e7e03d 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -62,8 +62,13 @@ void evergreen_pm_misc(struct radeon_device *rdev) struct radeon_power_state *ps = &rdev->pm.power_state[requested_index]; struct radeon_voltage *voltage = &ps->clock_info[0].voltage; - if ((voltage->type == VOLTAGE_SW) && voltage->voltage) - radeon_atom_set_voltage(rdev, voltage->voltage); + if (voltage->voltage != rdev->pm.current_vddc) { + if ((voltage->type == VOLTAGE_SW) && voltage->voltage) + radeon_atom_change_voltage(rdev, + rdev->pm.current_vddc, + voltage->voltage); + rdev->pm.current_vddc = voltage->voltage; + } } void evergreen_pm_prepare(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 668e750..9f557d7 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -576,9 +576,13 @@ void r600_pm_misc(struct radeon_device *rdev) struct radeon_power_state *ps = &rdev->pm.power_state[requested_index]; struct radeon_voltage *voltage = &ps->clock_info[0].voltage; - if ((voltage->type == VOLTAGE_SW) && voltage->voltage) - radeon_atom_set_voltage(rdev, voltage->voltage); - + if (voltage->voltage != rdev->pm.current_vddc) { + if ((voltage->type == VOLTAGE_SW) && voltage->voltage) + radeon_atom_change_voltage(rdev, + rdev->pm.current_vddc, + voltage->voltage); + rdev->pm.current_vddc = voltage->voltage; + } } bool r600_gui_idle(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 8b35a3b..48f74ad 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -177,6 +177,9 @@ void radeon_pm_resume(struct radeon_device *rdev); void radeon_combios_get_power_modes(struct radeon_device *rdev); void radeon_atombios_get_power_modes(struct radeon_device *rdev); void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level); +void radeon_atom_change_voltage(struct radeon_device *rdev, + u16 current_level, + u16 new_level); extern u32 rv6xx_get_temp(struct radeon_device *rdev); extern u32 rv770_get_temp(struct radeon_device *rdev); extern u32 evergreen_get_temp(struct radeon_device *rdev); @@ -758,6 +761,7 @@ struct radeon_pm { int default_power_state_index; u32 current_sclk; u32 current_mclk; + u32 current_vddc; struct radeon_i2c_chan *i2c_bus; /* selected pm method */ enum radeon_pm_method pm_method; diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 821c319..4d086e0 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1977,6 +1977,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; rdev->pm.current_clock_mode_index = 0; + rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; } void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable) @@ -2066,7 +2067,59 @@ void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level) atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } +union voltage_info { + struct _ATOM_VOLTAGE_OBJECT_INFO v1; + struct _ATOM_VOLTAGE_OBJECT_INFO_V2 v2; +}; + +/* steps through voltage levels */ +void radeon_atom_change_voltage(struct radeon_device *rdev, + u16 current_level, + u16 new_level) +{ + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); + int i; + u16 data_offset, step_size, table_size, object_size; + u8 frev, crev; + union voltage_info *voltage_info; + + if (current_level == new_level) + return; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL, + &frev, &crev, &data_offset)) { + voltage_info = (union voltage_info *)(rdev->mode_info.atom_context->bios + data_offset); + + switch (crev) { + case 1: + table_size = voltage_info->v1.sHeader.usStructureSize - sizeof(ATOM_COMMON_TABLE_HEADER); + object_size = voltage_info->v1.asVoltageObj[0].ucSize; + for (i = 0; i < (table_size / object_size); i++) { + if (voltage_info->v1.asVoltageObj[i].ucVoltageType == SET_VOLTAGE_TYPE_ASIC_VDDC) { + step_size = voltage_info->v1.asVoltageObj[i].asFormula.usVoltageStep; + if (step_size == 0) + return; + while (current_level != new_level) { + if (current_level > new_level) + current_level -= step_size; + else + current_level += step_size; + radeon_atom_set_voltage(rdev, current_level); + } + break; + } + } + break; + case 2: + radeon_atom_set_voltage(rdev, new_level); + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return; + } + } +} void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev) { diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 19a796d..1547796 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -474,6 +474,7 @@ void radeon_pm_resume(struct radeon_device *rdev) rdev->pm.current_clock_mode_index = 0; rdev->pm.current_sclk = rdev->clock.default_sclk; rdev->pm.current_mclk = rdev->clock.default_mclk; + rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage; mutex_unlock(&rdev->pm.mutex); radeon_pm_compute_clocks(rdev); } diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index c89d192..e74e0d9 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -63,8 +63,13 @@ void rv770_pm_misc(struct radeon_device *rdev) struct radeon_power_state *ps = &rdev->pm.power_state[requested_index]; struct radeon_voltage *voltage = &ps->clock_info[0].voltage; - if ((voltage->type == VOLTAGE_SW) && voltage->voltage) - radeon_atom_set_voltage(rdev, voltage->voltage); + if (voltage->voltage != rdev->pm.current_vddc) { + if ((voltage->type == VOLTAGE_SW) && voltage->voltage) + radeon_atom_change_voltage(rdev, + rdev->pm.current_vddc, + voltage->voltage); + rdev->pm.current_vddc = voltage->voltage; + } } /* -- 1.7.0.1