diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/ci_dpm.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/ci_dpm.c | 369 |
1 files changed, 332 insertions, 37 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index bda9e3de191e..11ccda83d767 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c @@ -889,7 +889,16 @@ static void ci_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate) pi->uvd_power_gated = gate; - ci_update_uvd_dpm(adev, gate); + if (gate) { + /* stop the UVD block */ + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_GATE); + ci_update_uvd_dpm(adev, gate); + } else { + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_UNGATE); + ci_update_uvd_dpm(adev, gate); + } } static bool ci_dpm_vblank_too_short(struct amdgpu_device *adev) @@ -2201,7 +2210,6 @@ static void ci_clear_vc(struct amdgpu_device *adev) static int ci_upload_firmware(struct amdgpu_device *adev) { - struct ci_power_info *pi = ci_get_pi(adev); int i, ret; if (amdgpu_ci_is_smc_running(adev)) { @@ -2218,7 +2226,7 @@ static int ci_upload_firmware(struct amdgpu_device *adev) amdgpu_ci_stop_smc_clock(adev); amdgpu_ci_reset_smc(adev); - ret = amdgpu_ci_load_smc_ucode(adev, pi->sram_end); + ret = amdgpu_ci_load_smc_ucode(adev, SMC_RAM_END); return ret; @@ -3673,6 +3681,40 @@ static int ci_find_boot_level(struct ci_single_dpm_table *table, return ret; } +static void ci_save_default_power_profile(struct amdgpu_device *adev) +{ + struct ci_power_info *pi = ci_get_pi(adev); + struct SMU7_Discrete_GraphicsLevel *levels = + pi->smc_state_table.GraphicsLevel; + uint32_t min_level = 0; + + pi->default_gfx_power_profile.activity_threshold = + be16_to_cpu(levels[0].ActivityLevel); + pi->default_gfx_power_profile.up_hyst = levels[0].UpH; + pi->default_gfx_power_profile.down_hyst = levels[0].DownH; + pi->default_gfx_power_profile.type = AMD_PP_GFX_PROFILE; + + pi->default_compute_power_profile = pi->default_gfx_power_profile; + pi->default_compute_power_profile.type = AMD_PP_COMPUTE_PROFILE; + + /* Optimize compute power profile: Use only highest + * 2 power levels (if more than 2 are available), Hysteresis: + * 0ms up, 5ms down + */ + if (pi->smc_state_table.GraphicsDpmLevelCount > 2) + min_level = pi->smc_state_table.GraphicsDpmLevelCount - 2; + else if (pi->smc_state_table.GraphicsDpmLevelCount == 2) + min_level = 1; + pi->default_compute_power_profile.min_sclk = + be32_to_cpu(levels[min_level].SclkFrequency); + + pi->default_compute_power_profile.up_hyst = 0; + pi->default_compute_power_profile.down_hyst = 5; + + pi->gfx_power_profile = pi->default_gfx_power_profile; + pi->compute_power_profile = pi->default_compute_power_profile; +} + static int ci_init_smc_table(struct amdgpu_device *adev) { struct ci_power_info *pi = ci_get_pi(adev); @@ -3818,6 +3860,8 @@ static int ci_init_smc_table(struct amdgpu_device *adev) if (ret) return ret; + ci_save_default_power_profile(adev); + return 0; } @@ -4248,12 +4292,6 @@ static int ci_update_vce_dpm(struct amdgpu_device *adev, if (amdgpu_current_state->evclk != amdgpu_new_state->evclk) { if (amdgpu_new_state->evclk) { - /* turn the clocks on when encoding */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_UNGATE); - if (ret) - return ret; - pi->smc_state_table.VceBootLevel = ci_get_vce_boot_level(adev); tmp = RREG32_SMC(ixDPM_TABLE_475); tmp &= ~DPM_TABLE_475__VceBootLevel_MASK; @@ -4265,9 +4303,6 @@ static int ci_update_vce_dpm(struct amdgpu_device *adev, ret = ci_enable_vce_dpm(adev, false); if (ret) return ret; - /* turn the clocks off when not encoding */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_GATE); } } return ret; @@ -4336,13 +4371,13 @@ static u32 ci_get_lowest_enabled_level(struct amdgpu_device *adev, static int ci_dpm_force_performance_level(struct amdgpu_device *adev, - enum amdgpu_dpm_forced_level level) + enum amd_dpm_forced_level level) { struct ci_power_info *pi = ci_get_pi(adev); u32 tmp, levels, i; int ret; - if (level == AMDGPU_DPM_FORCED_LEVEL_HIGH) { + if (level == AMD_DPM_FORCED_LEVEL_HIGH) { if ((!pi->pcie_dpm_key_disabled) && pi->dpm_level_enable_mask.pcie_dpm_enable_mask) { levels = 0; @@ -4403,7 +4438,7 @@ static int ci_dpm_force_performance_level(struct amdgpu_device *adev, } } } - } else if (level == AMDGPU_DPM_FORCED_LEVEL_LOW) { + } else if (level == AMD_DPM_FORCED_LEVEL_LOW) { if ((!pi->sclk_dpm_key_disabled) && pi->dpm_level_enable_mask.sclk_dpm_enable_mask) { levels = ci_get_lowest_enabled_level(adev, @@ -4452,7 +4487,7 @@ static int ci_dpm_force_performance_level(struct amdgpu_device *adev, udelay(1); } } - } else if (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) { + } else if (level == AMD_DPM_FORCED_LEVEL_AUTO) { if (!pi->pcie_dpm_key_disabled) { PPSMC_Result smc_result; @@ -5805,9 +5840,7 @@ static int ci_dpm_init_microcode(struct amdgpu_device *adev) out: if (err) { - printk(KERN_ERR - "cik_smc: Failed to load firmware \"%s\"\n", - fw_name); + pr_err("cik_smc: Failed to load firmware \"%s\"\n", fw_name); release_firmware(adev->pm.fw); adev->pm.fw = NULL; } @@ -6251,31 +6284,33 @@ static int ci_dpm_sw_init(void *handle) int ret; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - ret = amdgpu_irq_add_id(adev, 230, &adev->pm.dpm.thermal.irq); + ret = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_LEGACY, 230, + &adev->pm.dpm.thermal.irq); if (ret) return ret; - ret = amdgpu_irq_add_id(adev, 231, &adev->pm.dpm.thermal.irq); + ret = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_LEGACY, 231, + &adev->pm.dpm.thermal.irq); if (ret) return ret; /* default to balanced state */ adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; - adev->pm.dpm.forced_level = AMDGPU_DPM_FORCED_LEVEL_AUTO; + adev->pm.dpm.forced_level = AMD_DPM_FORCED_LEVEL_AUTO; adev->pm.default_sclk = adev->clock.default_sclk; adev->pm.default_mclk = adev->clock.default_mclk; adev->pm.current_sclk = adev->clock.default_sclk; adev->pm.current_mclk = adev->clock.default_mclk; adev->pm.int_thermal_type = THERMAL_TYPE_NONE; - if (amdgpu_dpm == 0) - return 0; - ret = ci_dpm_init_microcode(adev); if (ret) return ret; + if (amdgpu_dpm == 0) + return 0; + INIT_WORK(&adev->pm.dpm.thermal.work, amdgpu_dpm_thermal_work_handler); mutex_lock(&adev->pm.mutex); ret = ci_dpm_init(adev); @@ -6319,8 +6354,15 @@ static int ci_dpm_hw_init(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!amdgpu_dpm) + if (!amdgpu_dpm) { + ret = ci_upload_firmware(adev); + if (ret) { + DRM_ERROR("ci_upload_firmware failed\n"); + return ret; + } + ci_dpm_start_smc(adev); return 0; + } mutex_lock(&adev->pm.mutex); ci_dpm_setup_asic(adev); @@ -6342,6 +6384,8 @@ static int ci_dpm_hw_fini(void *handle) mutex_lock(&adev->pm.mutex); ci_dpm_disable(adev); mutex_unlock(&adev->pm.mutex); + } else { + ci_dpm_stop_smc(adev); } return 0; @@ -6571,8 +6615,9 @@ static int ci_dpm_force_clock_level(struct amdgpu_device *adev, { struct ci_power_info *pi = ci_get_pi(adev); - if (adev->pm.dpm.forced_level - != AMDGPU_DPM_FORCED_LEVEL_MANUAL) + if (adev->pm.dpm.forced_level & (AMD_DPM_FORCED_LEVEL_AUTO | + AMD_DPM_FORCED_LEVEL_LOW | + AMD_DPM_FORCED_LEVEL_HIGH)) return -EINVAL; switch (type) { @@ -6679,6 +6724,260 @@ static int ci_dpm_set_mclk_od(struct amdgpu_device *adev, uint32_t value) return 0; } +static int ci_dpm_get_power_profile_state(struct amdgpu_device *adev, + struct amd_pp_profile *query) +{ + struct ci_power_info *pi = ci_get_pi(adev); + + if (!pi || !query) + return -EINVAL; + + if (query->type == AMD_PP_GFX_PROFILE) + memcpy(query, &pi->gfx_power_profile, + sizeof(struct amd_pp_profile)); + else if (query->type == AMD_PP_COMPUTE_PROFILE) + memcpy(query, &pi->compute_power_profile, + sizeof(struct amd_pp_profile)); + else + return -EINVAL; + + return 0; +} + +static int ci_populate_requested_graphic_levels(struct amdgpu_device *adev, + struct amd_pp_profile *request) +{ + struct ci_power_info *pi = ci_get_pi(adev); + struct ci_dpm_table *dpm_table = &(pi->dpm_table); + struct SMU7_Discrete_GraphicsLevel *levels = + pi->smc_state_table.GraphicsLevel; + uint32_t array = pi->dpm_table_start + + offsetof(SMU7_Discrete_DpmTable, GraphicsLevel); + uint32_t array_size = sizeof(struct SMU7_Discrete_GraphicsLevel) * + SMU7_MAX_LEVELS_GRAPHICS; + uint32_t i; + + for (i = 0; i < dpm_table->sclk_table.count; i++) { + levels[i].ActivityLevel = + cpu_to_be16(request->activity_threshold); + levels[i].EnabledForActivity = 1; + levels[i].UpH = request->up_hyst; + levels[i].DownH = request->down_hyst; + } + + return amdgpu_ci_copy_bytes_to_smc(adev, array, (uint8_t *)levels, + array_size, pi->sram_end); +} + +static void ci_find_min_clock_masks(struct amdgpu_device *adev, + uint32_t *sclk_mask, uint32_t *mclk_mask, + uint32_t min_sclk, uint32_t min_mclk) +{ + struct ci_power_info *pi = ci_get_pi(adev); + struct ci_dpm_table *dpm_table = &(pi->dpm_table); + uint32_t i; + + for (i = 0; i < dpm_table->sclk_table.count; i++) { + if (dpm_table->sclk_table.dpm_levels[i].enabled && + dpm_table->sclk_table.dpm_levels[i].value >= min_sclk) + *sclk_mask |= 1 << i; + } + + for (i = 0; i < dpm_table->mclk_table.count; i++) { + if (dpm_table->mclk_table.dpm_levels[i].enabled && + dpm_table->mclk_table.dpm_levels[i].value >= min_mclk) + *mclk_mask |= 1 << i; + } +} + +static int ci_set_power_profile_state(struct amdgpu_device *adev, + struct amd_pp_profile *request) +{ + struct ci_power_info *pi = ci_get_pi(adev); + int tmp_result, result = 0; + uint32_t sclk_mask = 0, mclk_mask = 0; + + tmp_result = ci_freeze_sclk_mclk_dpm(adev); + if (tmp_result) { + DRM_ERROR("Failed to freeze SCLK MCLK DPM!"); + result = tmp_result; + } + + tmp_result = ci_populate_requested_graphic_levels(adev, + request); + if (tmp_result) { + DRM_ERROR("Failed to populate requested graphic levels!"); + result = tmp_result; + } + + tmp_result = ci_unfreeze_sclk_mclk_dpm(adev); + if (tmp_result) { + DRM_ERROR("Failed to unfreeze SCLK MCLK DPM!"); + result = tmp_result; + } + + ci_find_min_clock_masks(adev, &sclk_mask, &mclk_mask, + request->min_sclk, request->min_mclk); + + if (sclk_mask) { + if (!pi->sclk_dpm_key_disabled) + amdgpu_ci_send_msg_to_smc_with_parameter( + adev, + PPSMC_MSG_SCLKDPM_SetEnabledMask, + pi->dpm_level_enable_mask. + sclk_dpm_enable_mask & + sclk_mask); + } + + if (mclk_mask) { + if (!pi->mclk_dpm_key_disabled) + amdgpu_ci_send_msg_to_smc_with_parameter( + adev, + PPSMC_MSG_MCLKDPM_SetEnabledMask, + pi->dpm_level_enable_mask. + mclk_dpm_enable_mask & + mclk_mask); + } + + + return result; +} + +static int ci_dpm_set_power_profile_state(struct amdgpu_device *adev, + struct amd_pp_profile *request) +{ + struct ci_power_info *pi = ci_get_pi(adev); + int ret = -1; + + if (!pi || !request) + return -EINVAL; + + if (adev->pm.dpm.forced_level != + AMD_DPM_FORCED_LEVEL_AUTO) + return -EINVAL; + + if (request->min_sclk || + request->min_mclk || + request->activity_threshold || + request->up_hyst || + request->down_hyst) { + if (request->type == AMD_PP_GFX_PROFILE) + memcpy(&pi->gfx_power_profile, request, + sizeof(struct amd_pp_profile)); + else if (request->type == AMD_PP_COMPUTE_PROFILE) + memcpy(&pi->compute_power_profile, request, + sizeof(struct amd_pp_profile)); + else + return -EINVAL; + + if (request->type == pi->current_power_profile) + ret = ci_set_power_profile_state( + adev, + request); + } else { + /* set power profile if it exists */ + switch (request->type) { + case AMD_PP_GFX_PROFILE: + ret = ci_set_power_profile_state( + adev, + &pi->gfx_power_profile); + break; + case AMD_PP_COMPUTE_PROFILE: + ret = ci_set_power_profile_state( + adev, + &pi->compute_power_profile); + break; + default: + return -EINVAL; + } + } + + if (!ret) + pi->current_power_profile = request->type; + + return 0; +} + +static int ci_dpm_reset_power_profile_state(struct amdgpu_device *adev, + struct amd_pp_profile *request) +{ + struct ci_power_info *pi = ci_get_pi(adev); + + if (!pi || !request) + return -EINVAL; + + if (request->type == AMD_PP_GFX_PROFILE) { + pi->gfx_power_profile = pi->default_gfx_power_profile; + return ci_dpm_set_power_profile_state(adev, + &pi->gfx_power_profile); + } else if (request->type == AMD_PP_COMPUTE_PROFILE) { + pi->compute_power_profile = + pi->default_compute_power_profile; + return ci_dpm_set_power_profile_state(adev, + &pi->compute_power_profile); + } else + return -EINVAL; +} + +static int ci_dpm_switch_power_profile(struct amdgpu_device *adev, + enum amd_pp_profile_type type) +{ + struct ci_power_info *pi = ci_get_pi(adev); + struct amd_pp_profile request = {0}; + + if (!pi) + return -EINVAL; + + if (pi->current_power_profile != type) { + request.type = type; + return ci_dpm_set_power_profile_state(adev, &request); + } + + return 0; +} + +static int ci_dpm_read_sensor(struct amdgpu_device *adev, int idx, + void *value, int *size) +{ + u32 activity_percent = 50; + int ret; + + /* size must be at least 4 bytes for all sensors */ + if (*size < 4) + return -EINVAL; + + switch (idx) { + case AMDGPU_PP_SENSOR_GFX_SCLK: + *((uint32_t *)value) = ci_get_average_sclk_freq(adev); + *size = 4; + return 0; + case AMDGPU_PP_SENSOR_GFX_MCLK: + *((uint32_t *)value) = ci_get_average_mclk_freq(adev); + *size = 4; + return 0; + case AMDGPU_PP_SENSOR_GPU_TEMP: + *((uint32_t *)value) = ci_dpm_get_temp(adev); + *size = 4; + return 0; + case AMDGPU_PP_SENSOR_GPU_LOAD: + ret = ci_read_smc_soft_register(adev, + offsetof(SMU7_SoftRegisters, + AverageGraphicsA), + &activity_percent); + if (ret == 0) { + activity_percent += 0x80; + activity_percent >>= 8; + activity_percent = + activity_percent > 100 ? 100 : activity_percent; + } + *((uint32_t *)value) = activity_percent; + *size = 4; + return 0; + default: + return -EINVAL; + } +} + const struct amd_ip_funcs ci_dpm_ip_funcs = { .name = "ci_dpm", .early_init = ci_dpm_early_init, @@ -6721,6 +7020,11 @@ static const struct amdgpu_dpm_funcs ci_dpm_funcs = { .set_mclk_od = ci_dpm_set_mclk_od, .check_state_equal = ci_check_state_equal, .get_vce_clock_state = amdgpu_get_vce_clock_state, + .get_power_profile_state = ci_dpm_get_power_profile_state, + .set_power_profile_state = ci_dpm_set_power_profile_state, + .reset_power_profile_state = ci_dpm_reset_power_profile_state, + .switch_power_profile = ci_dpm_switch_power_profile, + .read_sensor = ci_dpm_read_sensor, }; static void ci_dpm_set_dpm_funcs(struct amdgpu_device *adev) @@ -6739,12 +7043,3 @@ static void ci_dpm_set_irq_funcs(struct amdgpu_device *adev) adev->pm.dpm.thermal.irq.num_types = AMDGPU_THERMAL_IRQ_LAST; adev->pm.dpm.thermal.irq.funcs = &ci_dpm_irq_funcs; } - -const struct amdgpu_ip_block_version ci_dpm_ip_block = -{ - .type = AMD_IP_BLOCK_TYPE_SMC, - .major = 7, - .minor = 0, - .rev = 0, - .funcs = &ci_dpm_ip_funcs, -}; |