diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 134 |
1 files changed, 22 insertions, 112 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index bab8fab118d6..b5b66c3d1b43 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | |||
| @@ -116,136 +116,47 @@ static int acp_sw_fini(void *handle) | |||
| 116 | return 0; | 116 | return 0; |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | /* power off a tile/block within ACP */ | ||
| 120 | static int acp_suspend_tile(void *cgs_dev, int tile) | ||
| 121 | { | ||
| 122 | u32 val = 0; | ||
| 123 | u32 count = 0; | ||
| 124 | |||
| 125 | if ((tile < ACP_TILE_P1) || (tile > ACP_TILE_DSP2)) { | ||
| 126 | pr_err("Invalid ACP tile : %d to suspend\n", tile); | ||
| 127 | return -1; | ||
| 128 | } | ||
| 129 | |||
| 130 | val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 + tile); | ||
| 131 | val &= ACP_TILE_ON_MASK; | ||
| 132 | |||
| 133 | if (val == 0x0) { | ||
| 134 | val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG); | ||
| 135 | val = val | (1 << tile); | ||
| 136 | cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val); | ||
| 137 | cgs_write_register(cgs_dev, mmACP_PGFSM_CONFIG_REG, | ||
| 138 | 0x500 + tile); | ||
| 139 | |||
| 140 | count = ACP_TIMEOUT_LOOP; | ||
| 141 | while (true) { | ||
| 142 | val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 | ||
| 143 | + tile); | ||
| 144 | val = val & ACP_TILE_ON_MASK; | ||
| 145 | if (val == ACP_TILE_OFF_MASK) | ||
| 146 | break; | ||
| 147 | if (--count == 0) { | ||
| 148 | pr_err("Timeout reading ACP PGFSM status\n"); | ||
| 149 | return -ETIMEDOUT; | ||
| 150 | } | ||
| 151 | udelay(100); | ||
| 152 | } | ||
| 153 | |||
| 154 | val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG); | ||
| 155 | |||
| 156 | val |= ACP_TILE_OFF_RETAIN_REG_MASK; | ||
| 157 | cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val); | ||
| 158 | } | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | |||
| 162 | /* power on a tile/block within ACP */ | ||
| 163 | static int acp_resume_tile(void *cgs_dev, int tile) | ||
| 164 | { | ||
| 165 | u32 val = 0; | ||
| 166 | u32 count = 0; | ||
| 167 | |||
| 168 | if ((tile < ACP_TILE_P1) || (tile > ACP_TILE_DSP2)) { | ||
| 169 | pr_err("Invalid ACP tile to resume\n"); | ||
| 170 | return -1; | ||
| 171 | } | ||
| 172 | |||
| 173 | val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 + tile); | ||
| 174 | val = val & ACP_TILE_ON_MASK; | ||
| 175 | |||
| 176 | if (val != 0x0) { | ||
| 177 | cgs_write_register(cgs_dev, mmACP_PGFSM_CONFIG_REG, | ||
| 178 | 0x600 + tile); | ||
| 179 | count = ACP_TIMEOUT_LOOP; | ||
| 180 | while (true) { | ||
| 181 | val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 | ||
| 182 | + tile); | ||
| 183 | val = val & ACP_TILE_ON_MASK; | ||
| 184 | if (val == 0x0) | ||
| 185 | break; | ||
| 186 | if (--count == 0) { | ||
| 187 | pr_err("Timeout reading ACP PGFSM status\n"); | ||
| 188 | return -ETIMEDOUT; | ||
| 189 | } | ||
| 190 | udelay(100); | ||
| 191 | } | ||
| 192 | val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG); | ||
| 193 | if (tile == ACP_TILE_P1) | ||
| 194 | val = val & (ACP_TILE_P1_MASK); | ||
| 195 | else if (tile == ACP_TILE_P2) | ||
| 196 | val = val & (ACP_TILE_P2_MASK); | ||
| 197 | |||
| 198 | cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val); | ||
| 199 | } | ||
| 200 | return 0; | ||
| 201 | } | ||
| 202 | |||
| 203 | struct acp_pm_domain { | 119 | struct acp_pm_domain { |
| 204 | void *cgs_dev; | 120 | void *adev; |
| 205 | struct generic_pm_domain gpd; | 121 | struct generic_pm_domain gpd; |
| 206 | }; | 122 | }; |
| 207 | 123 | ||
| 208 | static int acp_poweroff(struct generic_pm_domain *genpd) | 124 | static int acp_poweroff(struct generic_pm_domain *genpd) |
| 209 | { | 125 | { |
| 210 | int i, ret; | ||
| 211 | struct acp_pm_domain *apd; | 126 | struct acp_pm_domain *apd; |
| 127 | struct amdgpu_device *adev; | ||
| 212 | 128 | ||
| 213 | apd = container_of(genpd, struct acp_pm_domain, gpd); | 129 | apd = container_of(genpd, struct acp_pm_domain, gpd); |
| 214 | if (apd != NULL) { | 130 | if (apd != NULL) { |
| 215 | /* Donot return abruptly if any of power tile fails to suspend. | 131 | adev = apd->adev; |
| 216 | * Log it and continue powering off other tile | 132 | /* call smu to POWER GATE ACP block |
| 217 | */ | 133 | * smu will |
| 218 | for (i = 4; i >= 0 ; i--) { | 134 | * 1. turn off the acp clock |
| 219 | ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_P1 + i); | 135 | * 2. power off the acp tiles |
| 220 | if (ret) | 136 | * 3. check and enter ulv state |
| 221 | pr_err("ACP tile %d tile suspend failed\n", i); | 137 | */ |
| 222 | } | 138 | if (adev->powerplay.pp_funcs->set_powergating_by_smu) |
| 139 | amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); | ||
| 223 | } | 140 | } |
| 224 | return 0; | 141 | return 0; |
| 225 | } | 142 | } |
| 226 | 143 | ||
| 227 | static int acp_poweron(struct generic_pm_domain *genpd) | 144 | static int acp_poweron(struct generic_pm_domain *genpd) |
| 228 | { | 145 | { |
| 229 | int i, ret; | ||
| 230 | struct acp_pm_domain *apd; | 146 | struct acp_pm_domain *apd; |
| 147 | struct amdgpu_device *adev; | ||
| 231 | 148 | ||
| 232 | apd = container_of(genpd, struct acp_pm_domain, gpd); | 149 | apd = container_of(genpd, struct acp_pm_domain, gpd); |
| 233 | if (apd != NULL) { | 150 | if (apd != NULL) { |
| 234 | for (i = 0; i < 2; i++) { | 151 | adev = apd->adev; |
| 235 | ret = acp_resume_tile(apd->cgs_dev, ACP_TILE_P1 + i); | 152 | /* call smu to UNGATE ACP block |
| 236 | if (ret) { | 153 | * smu will |
| 237 | pr_err("ACP tile %d resume failed\n", i); | 154 | * 1. exit ulv |
| 238 | break; | 155 | * 2. turn on acp clock |
| 239 | } | 156 | * 3. power on acp tiles |
| 240 | } | 157 | */ |
| 241 | 158 | if (adev->powerplay.pp_funcs->set_powergating_by_smu) | |
| 242 | /* Disable DSPs which are not going to be used */ | 159 | amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); |
| 243 | for (i = 0; i < 3; i++) { | ||
| 244 | ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_DSP0 + i); | ||
| 245 | /* Continue suspending other DSP, even if one fails */ | ||
| 246 | if (ret) | ||
| 247 | pr_err("ACP DSP %d suspend failed\n", i); | ||
| 248 | } | ||
| 249 | } | 160 | } |
| 250 | return 0; | 161 | return 0; |
| 251 | } | 162 | } |
| @@ -311,7 +222,7 @@ static int acp_hw_init(void *handle) | |||
| 311 | adev->acp.acp_genpd->gpd.power_on = acp_poweron; | 222 | adev->acp.acp_genpd->gpd.power_on = acp_poweron; |
| 312 | 223 | ||
| 313 | 224 | ||
| 314 | adev->acp.acp_genpd->cgs_dev = adev->acp.cgs_device; | 225 | adev->acp.acp_genpd->adev = adev; |
| 315 | 226 | ||
| 316 | pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false); | 227 | pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false); |
| 317 | 228 | ||
| @@ -430,7 +341,6 @@ static int acp_hw_init(void *handle) | |||
| 430 | if (r) | 341 | if (r) |
| 431 | return r; | 342 | return r; |
| 432 | 343 | ||
| 433 | |||
| 434 | for (i = 0; i < ACP_DEVS ; i++) { | 344 | for (i = 0; i < ACP_DEVS ; i++) { |
| 435 | dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); | 345 | dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); |
| 436 | r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev); | 346 | r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev); |
