| .. | .. |
|---|
| 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 | |
|---|