.. | .. |
---|
24 | 24 | */ |
---|
25 | 25 | |
---|
26 | 26 | #include <linux/irqdomain.h> |
---|
| 27 | +#include <linux/pci.h> |
---|
27 | 28 | #include <linux/pm_domain.h> |
---|
28 | 29 | #include <linux/platform_device.h> |
---|
29 | 30 | #include <sound/designware_i2s.h> |
---|
.. | .. |
---|
116 | 117 | return 0; |
---|
117 | 118 | } |
---|
118 | 119 | |
---|
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 | 120 | struct acp_pm_domain { |
---|
204 | | - void *cgs_dev; |
---|
| 121 | + void *adev; |
---|
205 | 122 | struct generic_pm_domain gpd; |
---|
206 | 123 | }; |
---|
207 | 124 | |
---|
208 | 125 | static int acp_poweroff(struct generic_pm_domain *genpd) |
---|
209 | 126 | { |
---|
210 | | - int i, ret; |
---|
211 | 127 | struct acp_pm_domain *apd; |
---|
| 128 | + struct amdgpu_device *adev; |
---|
212 | 129 | |
---|
213 | 130 | apd = container_of(genpd, struct acp_pm_domain, gpd); |
---|
214 | 131 | if (apd != NULL) { |
---|
215 | | - /* Donot return abruptly if any of power tile fails to suspend. |
---|
216 | | - * Log it and continue powering off other tile |
---|
217 | | - */ |
---|
218 | | - for (i = 4; i >= 0 ; i--) { |
---|
219 | | - ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_P1 + i); |
---|
220 | | - if (ret) |
---|
221 | | - pr_err("ACP tile %d tile suspend failed\n", i); |
---|
222 | | - } |
---|
| 132 | + adev = apd->adev; |
---|
| 133 | + /* call smu to POWER GATE ACP block |
---|
| 134 | + * smu will |
---|
| 135 | + * 1. turn off the acp clock |
---|
| 136 | + * 2. power off the acp tiles |
---|
| 137 | + * 3. check and enter ulv state |
---|
| 138 | + */ |
---|
| 139 | + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); |
---|
223 | 140 | } |
---|
224 | 141 | return 0; |
---|
225 | 142 | } |
---|
226 | 143 | |
---|
227 | 144 | static int acp_poweron(struct generic_pm_domain *genpd) |
---|
228 | 145 | { |
---|
229 | | - int i, ret; |
---|
230 | 146 | struct acp_pm_domain *apd; |
---|
| 147 | + struct amdgpu_device *adev; |
---|
231 | 148 | |
---|
232 | 149 | apd = container_of(genpd, struct acp_pm_domain, gpd); |
---|
233 | 150 | if (apd != NULL) { |
---|
234 | | - for (i = 0; i < 2; i++) { |
---|
235 | | - ret = acp_resume_tile(apd->cgs_dev, ACP_TILE_P1 + i); |
---|
236 | | - if (ret) { |
---|
237 | | - pr_err("ACP tile %d resume failed\n", i); |
---|
238 | | - break; |
---|
239 | | - } |
---|
240 | | - } |
---|
241 | | - |
---|
242 | | - /* Disable DSPs which are not going to be used */ |
---|
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 | | - } |
---|
| 151 | + adev = apd->adev; |
---|
| 152 | + /* call smu to UNGATE ACP block |
---|
| 153 | + * smu will |
---|
| 154 | + * 1. exit ulv |
---|
| 155 | + * 2. turn on acp clock |
---|
| 156 | + * 3. power on acp tiles |
---|
| 157 | + */ |
---|
| 158 | + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); |
---|
249 | 159 | } |
---|
250 | 160 | return 0; |
---|
251 | 161 | } |
---|
252 | 162 | |
---|
253 | | -static struct device *get_mfd_cell_dev(const char *device_name, int r) |
---|
| 163 | +static int acp_genpd_add_device(struct device *dev, void *data) |
---|
254 | 164 | { |
---|
255 | | - char auto_dev_name[25]; |
---|
256 | | - struct device *dev; |
---|
| 165 | + struct generic_pm_domain *gpd = data; |
---|
| 166 | + int ret; |
---|
257 | 167 | |
---|
258 | | - snprintf(auto_dev_name, sizeof(auto_dev_name), |
---|
259 | | - "%s.%d.auto", device_name, r); |
---|
260 | | - dev = bus_find_device_by_name(&platform_bus_type, NULL, auto_dev_name); |
---|
261 | | - dev_info(dev, "device %s added to pm domain\n", auto_dev_name); |
---|
| 168 | + ret = pm_genpd_add_device(gpd, dev); |
---|
| 169 | + if (ret) |
---|
| 170 | + dev_err(dev, "Failed to add dev to genpd %d\n", ret); |
---|
262 | 171 | |
---|
263 | | - return dev; |
---|
| 172 | + return ret; |
---|
| 173 | +} |
---|
| 174 | + |
---|
| 175 | +static int acp_genpd_remove_device(struct device *dev, void *data) |
---|
| 176 | +{ |
---|
| 177 | + int ret; |
---|
| 178 | + |
---|
| 179 | + ret = pm_genpd_remove_device(dev); |
---|
| 180 | + if (ret) |
---|
| 181 | + dev_err(dev, "Failed to remove dev from genpd %d\n", ret); |
---|
| 182 | + |
---|
| 183 | + /* Continue to remove */ |
---|
| 184 | + return 0; |
---|
264 | 185 | } |
---|
265 | 186 | |
---|
266 | 187 | /** |
---|
.. | .. |
---|
271 | 192 | */ |
---|
272 | 193 | static int acp_hw_init(void *handle) |
---|
273 | 194 | { |
---|
274 | | - int r, i; |
---|
| 195 | + int r; |
---|
275 | 196 | uint64_t acp_base; |
---|
276 | 197 | u32 val = 0; |
---|
277 | 198 | u32 count = 0; |
---|
278 | | - struct device *dev; |
---|
279 | 199 | struct i2s_platform_data *i2s_pdata = NULL; |
---|
280 | 200 | |
---|
281 | 201 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
---|
.. | .. |
---|
289 | 209 | r = amd_acp_hw_init(adev->acp.cgs_device, |
---|
290 | 210 | ip_block->version->major, ip_block->version->minor); |
---|
291 | 211 | /* -ENODEV means board uses AZ rather than ACP */ |
---|
292 | | - if (r == -ENODEV) |
---|
| 212 | + if (r == -ENODEV) { |
---|
| 213 | + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); |
---|
293 | 214 | return 0; |
---|
294 | | - else if (r) |
---|
| 215 | + } else if (r) { |
---|
295 | 216 | return r; |
---|
| 217 | + } |
---|
296 | 218 | |
---|
297 | 219 | if (adev->rmmio_size == 0 || adev->rmmio_size < 0x5289) |
---|
298 | 220 | return -EINVAL; |
---|
299 | 221 | |
---|
300 | 222 | acp_base = adev->rmmio_base; |
---|
301 | 223 | |
---|
302 | | - if (adev->asic_type != CHIP_STONEY) { |
---|
303 | | - adev->acp.acp_genpd = kzalloc(sizeof(struct acp_pm_domain), GFP_KERNEL); |
---|
304 | | - if (adev->acp.acp_genpd == NULL) |
---|
305 | | - return -ENOMEM; |
---|
306 | 224 | |
---|
307 | | - adev->acp.acp_genpd->gpd.name = "ACP_AUDIO"; |
---|
308 | | - adev->acp.acp_genpd->gpd.power_off = acp_poweroff; |
---|
309 | | - adev->acp.acp_genpd->gpd.power_on = acp_poweron; |
---|
| 225 | + adev->acp.acp_genpd = kzalloc(sizeof(struct acp_pm_domain), GFP_KERNEL); |
---|
| 226 | + if (adev->acp.acp_genpd == NULL) |
---|
| 227 | + return -ENOMEM; |
---|
| 228 | + |
---|
| 229 | + adev->acp.acp_genpd->gpd.name = "ACP_AUDIO"; |
---|
| 230 | + adev->acp.acp_genpd->gpd.power_off = acp_poweroff; |
---|
| 231 | + adev->acp.acp_genpd->gpd.power_on = acp_poweron; |
---|
310 | 232 | |
---|
311 | 233 | |
---|
312 | | - adev->acp.acp_genpd->cgs_dev = adev->acp.cgs_device; |
---|
| 234 | + adev->acp.acp_genpd->adev = adev; |
---|
313 | 235 | |
---|
314 | | - pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false); |
---|
315 | | - } |
---|
| 236 | + pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false); |
---|
316 | 237 | |
---|
317 | 238 | adev->acp.acp_cell = kcalloc(ACP_DEVS, sizeof(struct mfd_cell), |
---|
318 | 239 | GFP_KERNEL); |
---|
.. | .. |
---|
430 | 351 | if (r) |
---|
431 | 352 | goto failure; |
---|
432 | 353 | |
---|
433 | | - if (adev->asic_type != CHIP_STONEY) { |
---|
434 | | - for (i = 0; i < ACP_DEVS ; i++) { |
---|
435 | | - dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); |
---|
436 | | - r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev); |
---|
437 | | - if (r) { |
---|
438 | | - dev_err(dev, "Failed to add dev to genpd\n"); |
---|
439 | | - goto failure; |
---|
440 | | - } |
---|
441 | | - } |
---|
442 | | - } |
---|
| 354 | + r = device_for_each_child(adev->acp.parent, &adev->acp.acp_genpd->gpd, |
---|
| 355 | + acp_genpd_add_device); |
---|
| 356 | + if (r) |
---|
| 357 | + goto failure; |
---|
443 | 358 | |
---|
444 | 359 | /* Assert Soft reset of ACP */ |
---|
445 | 360 | val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET); |
---|
.. | .. |
---|
500 | 415 | */ |
---|
501 | 416 | static int acp_hw_fini(void *handle) |
---|
502 | 417 | { |
---|
503 | | - int i, ret; |
---|
504 | 418 | u32 val = 0; |
---|
505 | 419 | u32 count = 0; |
---|
506 | | - struct device *dev; |
---|
507 | 420 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
---|
508 | 421 | |
---|
509 | 422 | /* return early if no ACP */ |
---|
510 | | - if (!adev->acp.acp_cell) |
---|
| 423 | + if (!adev->acp.acp_genpd) { |
---|
| 424 | + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); |
---|
511 | 425 | return 0; |
---|
| 426 | + } |
---|
512 | 427 | |
---|
513 | 428 | /* Assert Soft reset of ACP */ |
---|
514 | 429 | val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET); |
---|
.. | .. |
---|
546 | 461 | udelay(100); |
---|
547 | 462 | } |
---|
548 | 463 | |
---|
549 | | - if (adev->acp.acp_genpd) { |
---|
550 | | - for (i = 0; i < ACP_DEVS ; i++) { |
---|
551 | | - dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); |
---|
552 | | - ret = pm_genpd_remove_device(dev); |
---|
553 | | - /* If removal fails, dont giveup and try rest */ |
---|
554 | | - if (ret) |
---|
555 | | - dev_err(dev, "remove dev from genpd failed\n"); |
---|
556 | | - } |
---|
557 | | - kfree(adev->acp.acp_genpd); |
---|
558 | | - } |
---|
| 464 | + device_for_each_child(adev->acp.parent, NULL, |
---|
| 465 | + acp_genpd_remove_device); |
---|
559 | 466 | |
---|
560 | 467 | mfd_remove_devices(adev->acp.parent); |
---|
561 | 468 | kfree(adev->acp.acp_res); |
---|
| 469 | + kfree(adev->acp.acp_genpd); |
---|
562 | 470 | kfree(adev->acp.acp_cell); |
---|
563 | 471 | |
---|
564 | 472 | return 0; |
---|
.. | .. |
---|
566 | 474 | |
---|
567 | 475 | static int acp_suspend(void *handle) |
---|
568 | 476 | { |
---|
| 477 | + struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
---|
| 478 | + |
---|
| 479 | + /* power up on suspend */ |
---|
| 480 | + if (!adev->acp.acp_cell) |
---|
| 481 | + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); |
---|
569 | 482 | return 0; |
---|
570 | 483 | } |
---|
571 | 484 | |
---|
572 | 485 | static int acp_resume(void *handle) |
---|
573 | 486 | { |
---|
| 487 | + struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
---|
| 488 | + |
---|
| 489 | + /* power down again on resume */ |
---|
| 490 | + if (!adev->acp.acp_cell) |
---|
| 491 | + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); |
---|
574 | 492 | return 0; |
---|
575 | 493 | } |
---|
576 | 494 | |
---|
.. | .. |
---|
603 | 521 | static int acp_set_powergating_state(void *handle, |
---|
604 | 522 | enum amd_powergating_state state) |
---|
605 | 523 | { |
---|
| 524 | + struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
---|
| 525 | + bool enable = (state == AMD_PG_STATE_GATE); |
---|
| 526 | + |
---|
| 527 | + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, enable); |
---|
| 528 | + |
---|
606 | 529 | return 0; |
---|
607 | 530 | } |
---|
608 | 531 | |
---|