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); |