diff options
| author | Eric Huang <JinHuiEric.Huang@amd.com> | 2016-10-14 14:21:19 -0400 |
|---|---|---|
| committer | Alex Deucher <alexander.deucher@amd.com> | 2017-03-29 23:52:52 -0400 |
| commit | 618c0483736f4e963770aa6076cca35935604a12 (patch) | |
| tree | ee799884ba808e3e9abbbed405413ceea68fde94 /drivers | |
| parent | 03609ebc4cbdbdb32b72df2b611390d19b9a99bf (diff) | |
drm/amd/amdgpu: add power profile support for CI
Signed-off-by: Eric Huang <JinHuiEric.Huang@amd.com>
Acked-by: Rex Zhu <Rex.Zhu@amd.com>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/ci_dpm.c | 252 | ||||
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/ci_dpm.h | 7 |
2 files changed, 259 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index f97ecb49972e..578878d1d4c0 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c | |||
| @@ -3681,6 +3681,40 @@ static int ci_find_boot_level(struct ci_single_dpm_table *table, | |||
| 3681 | return ret; | 3681 | return ret; |
| 3682 | } | 3682 | } |
| 3683 | 3683 | ||
| 3684 | static void ci_save_default_power_profile(struct amdgpu_device *adev) | ||
| 3685 | { | ||
| 3686 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 3687 | struct SMU7_Discrete_GraphicsLevel *levels = | ||
| 3688 | pi->smc_state_table.GraphicsLevel; | ||
| 3689 | uint32_t min_level = 0; | ||
| 3690 | |||
| 3691 | pi->default_gfx_power_profile.activity_threshold = | ||
| 3692 | be16_to_cpu(levels[0].ActivityLevel); | ||
| 3693 | pi->default_gfx_power_profile.up_hyst = levels[0].UpH; | ||
| 3694 | pi->default_gfx_power_profile.down_hyst = levels[0].DownH; | ||
| 3695 | pi->default_gfx_power_profile.type = AMD_PP_GFX_PROFILE; | ||
| 3696 | |||
| 3697 | pi->default_compute_power_profile = pi->default_gfx_power_profile; | ||
| 3698 | pi->default_compute_power_profile.type = AMD_PP_COMPUTE_PROFILE; | ||
| 3699 | |||
| 3700 | /* Optimize compute power profile: Use only highest | ||
| 3701 | * 2 power levels (if more than 2 are available), Hysteresis: | ||
| 3702 | * 0ms up, 5ms down | ||
| 3703 | */ | ||
| 3704 | if (pi->smc_state_table.GraphicsDpmLevelCount > 2) | ||
| 3705 | min_level = pi->smc_state_table.GraphicsDpmLevelCount - 2; | ||
| 3706 | else if (pi->smc_state_table.GraphicsDpmLevelCount == 2) | ||
| 3707 | min_level = 1; | ||
| 3708 | pi->default_compute_power_profile.min_sclk = | ||
| 3709 | be32_to_cpu(levels[min_level].SclkFrequency); | ||
| 3710 | |||
| 3711 | pi->default_compute_power_profile.up_hyst = 0; | ||
| 3712 | pi->default_compute_power_profile.down_hyst = 5; | ||
| 3713 | |||
| 3714 | pi->gfx_power_profile = pi->default_gfx_power_profile; | ||
| 3715 | pi->compute_power_profile = pi->default_compute_power_profile; | ||
| 3716 | } | ||
| 3717 | |||
| 3684 | static int ci_init_smc_table(struct amdgpu_device *adev) | 3718 | static int ci_init_smc_table(struct amdgpu_device *adev) |
| 3685 | { | 3719 | { |
| 3686 | struct ci_power_info *pi = ci_get_pi(adev); | 3720 | struct ci_power_info *pi = ci_get_pi(adev); |
| @@ -3826,6 +3860,8 @@ static int ci_init_smc_table(struct amdgpu_device *adev) | |||
| 3826 | if (ret) | 3860 | if (ret) |
| 3827 | return ret; | 3861 | return ret; |
| 3828 | 3862 | ||
| 3863 | ci_save_default_power_profile(adev); | ||
| 3864 | |||
| 3829 | return 0; | 3865 | return 0; |
| 3830 | } | 3866 | } |
| 3831 | 3867 | ||
| @@ -6688,6 +6724,218 @@ static int ci_dpm_set_mclk_od(struct amdgpu_device *adev, uint32_t value) | |||
| 6688 | return 0; | 6724 | return 0; |
| 6689 | } | 6725 | } |
| 6690 | 6726 | ||
| 6727 | static int ci_dpm_get_power_profile_state(struct amdgpu_device *adev, | ||
| 6728 | struct amd_pp_profile *query) | ||
| 6729 | { | ||
| 6730 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 6731 | |||
| 6732 | if (!pi || !query) | ||
| 6733 | return -EINVAL; | ||
| 6734 | |||
| 6735 | if (query->type == AMD_PP_GFX_PROFILE) | ||
| 6736 | memcpy(query, &pi->gfx_power_profile, | ||
| 6737 | sizeof(struct amd_pp_profile)); | ||
| 6738 | else if (query->type == AMD_PP_COMPUTE_PROFILE) | ||
| 6739 | memcpy(query, &pi->compute_power_profile, | ||
| 6740 | sizeof(struct amd_pp_profile)); | ||
| 6741 | else | ||
| 6742 | return -EINVAL; | ||
| 6743 | |||
| 6744 | return 0; | ||
| 6745 | } | ||
| 6746 | |||
| 6747 | static int ci_populate_requested_graphic_levels(struct amdgpu_device *adev, | ||
| 6748 | struct amd_pp_profile *request) | ||
| 6749 | { | ||
| 6750 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 6751 | struct ci_dpm_table *dpm_table = &(pi->dpm_table); | ||
| 6752 | struct SMU7_Discrete_GraphicsLevel *levels = | ||
| 6753 | pi->smc_state_table.GraphicsLevel; | ||
| 6754 | uint32_t array = pi->dpm_table_start + | ||
| 6755 | offsetof(SMU7_Discrete_DpmTable, GraphicsLevel); | ||
| 6756 | uint32_t array_size = sizeof(struct SMU7_Discrete_GraphicsLevel) * | ||
| 6757 | SMU7_MAX_LEVELS_GRAPHICS; | ||
| 6758 | uint32_t i; | ||
| 6759 | |||
| 6760 | for (i = 0; i < dpm_table->sclk_table.count; i++) { | ||
| 6761 | levels[i].ActivityLevel = | ||
| 6762 | cpu_to_be16(request->activity_threshold); | ||
| 6763 | levels[i].EnabledForActivity = 1; | ||
| 6764 | levels[i].UpH = request->up_hyst; | ||
| 6765 | levels[i].DownH = request->down_hyst; | ||
| 6766 | } | ||
| 6767 | |||
| 6768 | return amdgpu_ci_copy_bytes_to_smc(adev, array, (uint8_t *)levels, | ||
| 6769 | array_size, pi->sram_end); | ||
| 6770 | } | ||
| 6771 | |||
| 6772 | static void ci_find_min_clock_masks(struct amdgpu_device *adev, | ||
| 6773 | uint32_t *sclk_mask, uint32_t *mclk_mask, | ||
| 6774 | uint32_t min_sclk, uint32_t min_mclk) | ||
| 6775 | { | ||
| 6776 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 6777 | struct ci_dpm_table *dpm_table = &(pi->dpm_table); | ||
| 6778 | uint32_t i; | ||
| 6779 | |||
| 6780 | for (i = 0; i < dpm_table->sclk_table.count; i++) { | ||
| 6781 | if (dpm_table->sclk_table.dpm_levels[i].enabled && | ||
| 6782 | dpm_table->sclk_table.dpm_levels[i].value >= min_sclk) | ||
| 6783 | *sclk_mask |= 1 << i; | ||
| 6784 | } | ||
| 6785 | |||
| 6786 | for (i = 0; i < dpm_table->mclk_table.count; i++) { | ||
| 6787 | if (dpm_table->mclk_table.dpm_levels[i].enabled && | ||
| 6788 | dpm_table->mclk_table.dpm_levels[i].value >= min_mclk) | ||
| 6789 | *mclk_mask |= 1 << i; | ||
| 6790 | } | ||
| 6791 | } | ||
| 6792 | |||
| 6793 | static int ci_set_power_profile_state(struct amdgpu_device *adev, | ||
| 6794 | struct amd_pp_profile *request) | ||
| 6795 | { | ||
| 6796 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 6797 | int tmp_result, result = 0; | ||
| 6798 | uint32_t sclk_mask = 0, mclk_mask = 0; | ||
| 6799 | |||
| 6800 | tmp_result = ci_freeze_sclk_mclk_dpm(adev); | ||
| 6801 | if (tmp_result) { | ||
| 6802 | DRM_ERROR("Failed to freeze SCLK MCLK DPM!"); | ||
| 6803 | result = tmp_result; | ||
| 6804 | } | ||
| 6805 | |||
| 6806 | tmp_result = ci_populate_requested_graphic_levels(adev, | ||
| 6807 | request); | ||
| 6808 | if (tmp_result) { | ||
| 6809 | DRM_ERROR("Failed to populate requested graphic levels!"); | ||
| 6810 | result = tmp_result; | ||
| 6811 | } | ||
| 6812 | |||
| 6813 | tmp_result = ci_unfreeze_sclk_mclk_dpm(adev); | ||
| 6814 | if (tmp_result) { | ||
| 6815 | DRM_ERROR("Failed to unfreeze SCLK MCLK DPM!"); | ||
| 6816 | result = tmp_result; | ||
| 6817 | } | ||
| 6818 | |||
| 6819 | ci_find_min_clock_masks(adev, &sclk_mask, &mclk_mask, | ||
| 6820 | request->min_sclk, request->min_mclk); | ||
| 6821 | |||
| 6822 | if (sclk_mask) { | ||
| 6823 | if (!pi->sclk_dpm_key_disabled) | ||
| 6824 | amdgpu_ci_send_msg_to_smc_with_parameter( | ||
| 6825 | adev, | ||
| 6826 | PPSMC_MSG_SCLKDPM_SetEnabledMask, | ||
| 6827 | pi->dpm_level_enable_mask. | ||
| 6828 | sclk_dpm_enable_mask & | ||
| 6829 | sclk_mask); | ||
| 6830 | } | ||
| 6831 | |||
| 6832 | if (mclk_mask) { | ||
| 6833 | if (!pi->mclk_dpm_key_disabled) | ||
| 6834 | amdgpu_ci_send_msg_to_smc_with_parameter( | ||
| 6835 | adev, | ||
| 6836 | PPSMC_MSG_MCLKDPM_SetEnabledMask, | ||
| 6837 | pi->dpm_level_enable_mask. | ||
| 6838 | mclk_dpm_enable_mask & | ||
| 6839 | mclk_mask); | ||
| 6840 | } | ||
| 6841 | |||
| 6842 | |||
| 6843 | return result; | ||
| 6844 | } | ||
| 6845 | |||
| 6846 | static int ci_dpm_set_power_profile_state(struct amdgpu_device *adev, | ||
| 6847 | struct amd_pp_profile *request) | ||
| 6848 | { | ||
| 6849 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 6850 | int ret = -1; | ||
| 6851 | |||
| 6852 | if (!pi || !request) | ||
| 6853 | return -EINVAL; | ||
| 6854 | |||
| 6855 | if (adev->pm.dpm.forced_level != | ||
| 6856 | AMD_DPM_FORCED_LEVEL_AUTO) | ||
| 6857 | return -EINVAL; | ||
| 6858 | |||
| 6859 | if (request->min_sclk || | ||
| 6860 | request->min_mclk || | ||
| 6861 | request->activity_threshold || | ||
| 6862 | request->up_hyst || | ||
| 6863 | request->down_hyst) { | ||
| 6864 | if (request->type == AMD_PP_GFX_PROFILE) | ||
| 6865 | memcpy(&pi->gfx_power_profile, request, | ||
| 6866 | sizeof(struct amd_pp_profile)); | ||
| 6867 | else if (request->type == AMD_PP_COMPUTE_PROFILE) | ||
| 6868 | memcpy(&pi->compute_power_profile, request, | ||
| 6869 | sizeof(struct amd_pp_profile)); | ||
| 6870 | else | ||
| 6871 | return -EINVAL; | ||
| 6872 | |||
| 6873 | if (request->type == pi->current_power_profile) | ||
| 6874 | ret = ci_set_power_profile_state( | ||
| 6875 | adev, | ||
| 6876 | request); | ||
| 6877 | } else { | ||
| 6878 | /* set power profile if it exists */ | ||
| 6879 | switch (request->type) { | ||
| 6880 | case AMD_PP_GFX_PROFILE: | ||
| 6881 | ret = ci_set_power_profile_state( | ||
| 6882 | adev, | ||
| 6883 | &pi->gfx_power_profile); | ||
| 6884 | break; | ||
| 6885 | case AMD_PP_COMPUTE_PROFILE: | ||
| 6886 | ret = ci_set_power_profile_state( | ||
| 6887 | adev, | ||
| 6888 | &pi->compute_power_profile); | ||
| 6889 | break; | ||
| 6890 | default: | ||
| 6891 | return -EINVAL; | ||
| 6892 | } | ||
| 6893 | } | ||
| 6894 | |||
| 6895 | if (!ret) | ||
| 6896 | pi->current_power_profile = request->type; | ||
| 6897 | |||
| 6898 | return 0; | ||
| 6899 | } | ||
| 6900 | |||
| 6901 | static int ci_dpm_reset_power_profile_state(struct amdgpu_device *adev, | ||
| 6902 | struct amd_pp_profile *request) | ||
| 6903 | { | ||
| 6904 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 6905 | |||
| 6906 | if (!pi || !request) | ||
| 6907 | return -EINVAL; | ||
| 6908 | |||
| 6909 | if (request->type == AMD_PP_GFX_PROFILE) { | ||
| 6910 | pi->gfx_power_profile = pi->default_gfx_power_profile; | ||
| 6911 | return ci_dpm_set_power_profile_state(adev, | ||
| 6912 | &pi->gfx_power_profile); | ||
| 6913 | } else if (request->type == AMD_PP_COMPUTE_PROFILE) { | ||
| 6914 | pi->compute_power_profile = | ||
| 6915 | pi->default_compute_power_profile; | ||
| 6916 | return ci_dpm_set_power_profile_state(adev, | ||
| 6917 | &pi->compute_power_profile); | ||
| 6918 | } else | ||
| 6919 | return -EINVAL; | ||
| 6920 | } | ||
| 6921 | |||
| 6922 | static int ci_dpm_switch_power_profile(struct amdgpu_device *adev, | ||
| 6923 | enum amd_pp_profile_type type) | ||
| 6924 | { | ||
| 6925 | struct ci_power_info *pi = ci_get_pi(adev); | ||
| 6926 | struct amd_pp_profile request = {0}; | ||
| 6927 | |||
| 6928 | if (!pi) | ||
| 6929 | return -EINVAL; | ||
| 6930 | |||
| 6931 | if (pi->current_power_profile != type) { | ||
| 6932 | request.type = type; | ||
| 6933 | return ci_dpm_set_power_profile_state(adev, &request); | ||
| 6934 | } | ||
| 6935 | |||
| 6936 | return 0; | ||
| 6937 | } | ||
| 6938 | |||
| 6691 | const struct amd_ip_funcs ci_dpm_ip_funcs = { | 6939 | const struct amd_ip_funcs ci_dpm_ip_funcs = { |
| 6692 | .name = "ci_dpm", | 6940 | .name = "ci_dpm", |
| 6693 | .early_init = ci_dpm_early_init, | 6941 | .early_init = ci_dpm_early_init, |
| @@ -6730,6 +6978,10 @@ static const struct amdgpu_dpm_funcs ci_dpm_funcs = { | |||
| 6730 | .set_mclk_od = ci_dpm_set_mclk_od, | 6978 | .set_mclk_od = ci_dpm_set_mclk_od, |
| 6731 | .check_state_equal = ci_check_state_equal, | 6979 | .check_state_equal = ci_check_state_equal, |
| 6732 | .get_vce_clock_state = amdgpu_get_vce_clock_state, | 6980 | .get_vce_clock_state = amdgpu_get_vce_clock_state, |
| 6981 | .get_power_profile_state = ci_dpm_get_power_profile_state, | ||
| 6982 | .set_power_profile_state = ci_dpm_set_power_profile_state, | ||
| 6983 | .reset_power_profile_state = ci_dpm_reset_power_profile_state, | ||
| 6984 | .switch_power_profile = ci_dpm_switch_power_profile, | ||
| 6733 | }; | 6985 | }; |
| 6734 | 6986 | ||
| 6735 | static void ci_dpm_set_dpm_funcs(struct amdgpu_device *adev) | 6987 | static void ci_dpm_set_dpm_funcs(struct amdgpu_device *adev) |
diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.h b/drivers/gpu/drm/amd/amdgpu/ci_dpm.h index 91be2996ae7c..84cbc9c45f4d 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.h +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.h | |||
| @@ -295,6 +295,13 @@ struct ci_power_info { | |||
| 295 | bool fan_is_controlled_by_smc; | 295 | bool fan_is_controlled_by_smc; |
| 296 | u32 t_min; | 296 | u32 t_min; |
| 297 | u32 fan_ctrl_default_mode; | 297 | u32 fan_ctrl_default_mode; |
| 298 | |||
| 299 | /* power profile */ | ||
| 300 | struct amd_pp_profile gfx_power_profile; | ||
| 301 | struct amd_pp_profile compute_power_profile; | ||
| 302 | struct amd_pp_profile default_gfx_power_profile; | ||
| 303 | struct amd_pp_profile default_compute_power_profile; | ||
| 304 | enum amd_pp_profile_type current_power_profile; | ||
| 298 | }; | 305 | }; |
| 299 | 306 | ||
| 300 | #define CISLANDS_VOLTAGE_CONTROL_NONE 0x0 | 307 | #define CISLANDS_VOLTAGE_CONTROL_NONE 0x0 |
