diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 207 |
1 files changed, 67 insertions, 140 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 71efcf38f11b..297a5490ad8c 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) return 0; } -/* power off a tile/block within ACP */ -static int acp_suspend_tile(void *cgs_dev, int tile) -{ - u32 val = 0; - u32 count = 0; - - if ((tile < ACP_TILE_P1) || (tile > ACP_TILE_DSP2)) { - pr_err("Invalid ACP tile : %d to suspend\n", tile); - return -1; - } - - val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 + tile); - val &= ACP_TILE_ON_MASK; - - if (val == 0x0) { - val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG); - val = val | (1 << tile); - cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val); - cgs_write_register(cgs_dev, mmACP_PGFSM_CONFIG_REG, - 0x500 + tile); - - count = ACP_TIMEOUT_LOOP; - while (true) { - val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 - + tile); - val = val & ACP_TILE_ON_MASK; - if (val == ACP_TILE_OFF_MASK) - break; - if (--count == 0) { - pr_err("Timeout reading ACP PGFSM status\n"); - return -ETIMEDOUT; - } - udelay(100); - } - - val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG); - - val |= ACP_TILE_OFF_RETAIN_REG_MASK; - cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val); - } - return 0; -} - -/* power on a tile/block within ACP */ -static int acp_resume_tile(void *cgs_dev, int tile) -{ - u32 val = 0; - u32 count = 0; - - if ((tile < ACP_TILE_P1) || (tile > ACP_TILE_DSP2)) { - pr_err("Invalid ACP tile to resume\n"); - return -1; - } - - val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 + tile); - val = val & ACP_TILE_ON_MASK; - - if (val != 0x0) { - cgs_write_register(cgs_dev, mmACP_PGFSM_CONFIG_REG, - 0x600 + tile); - count = ACP_TIMEOUT_LOOP; - while (true) { - val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 - + tile); - val = val & ACP_TILE_ON_MASK; - if (val == 0x0) - break; - if (--count == 0) { - pr_err("Timeout reading ACP PGFSM status\n"); - return -ETIMEDOUT; - } - udelay(100); - } - val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG); - if (tile == ACP_TILE_P1) - val = val & (ACP_TILE_P1_MASK); - else if (tile == ACP_TILE_P2) - val = val & (ACP_TILE_P2_MASK); - - cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val); - } - return 0; -} - struct acp_pm_domain { - void *cgs_dev; + void *adev; struct generic_pm_domain gpd; }; static int acp_poweroff(struct generic_pm_domain *genpd) { - int i, ret; struct acp_pm_domain *apd; + struct amdgpu_device *adev; apd = container_of(genpd, struct acp_pm_domain, gpd); if (apd != NULL) { - /* Donot return abruptly if any of power tile fails to suspend. - * Log it and continue powering off other tile - */ - for (i = 4; i >= 0 ; i--) { - ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_P1 + i); - if (ret) - pr_err("ACP tile %d tile suspend failed\n", i); - } + adev = apd->adev; + /* call smu to POWER GATE ACP block + * smu will + * 1. turn off the acp clock + * 2. power off the acp tiles + * 3. check and enter ulv state + */ + if (adev->powerplay.pp_funcs->set_powergating_by_smu) + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); } return 0; } static int acp_poweron(struct generic_pm_domain *genpd) { - int i, ret; struct acp_pm_domain *apd; + struct amdgpu_device *adev; apd = container_of(genpd, struct acp_pm_domain, gpd); if (apd != NULL) { - for (i = 0; i < 2; i++) { - ret = acp_resume_tile(apd->cgs_dev, ACP_TILE_P1 + i); - if (ret) { - pr_err("ACP tile %d resume failed\n", i); - break; - } - } - - /* Disable DSPs which are not going to be used */ - for (i = 0; i < 3; i++) { - ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_DSP0 + i); - /* Continue suspending other DSP, even if one fails */ - if (ret) - pr_err("ACP DSP %d suspend failed\n", i); - } + adev = apd->adev; + /* call smu to UNGATE ACP block + * smu will + * 1. exit ulv + * 2. turn on acp clock + * 3. power on acp tiles + */ + if (adev->powerplay.pp_funcs->set_powergating_by_smu) + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); } return 0; } @@ -289,30 +200,31 @@ static int acp_hw_init(void *handle) r = amd_acp_hw_init(adev->acp.cgs_device, ip_block->version->major, ip_block->version->minor); /* -ENODEV means board uses AZ rather than ACP */ - if (r == -ENODEV) + if (r == -ENODEV) { + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); return 0; - else if (r) + } else if (r) { return r; + } if (adev->rmmio_size == 0 || adev->rmmio_size < 0x5289) return -EINVAL; acp_base = adev->rmmio_base; - if (adev->asic_type != CHIP_STONEY) { - adev->acp.acp_genpd = kzalloc(sizeof(struct acp_pm_domain), GFP_KERNEL); - if (adev->acp.acp_genpd == NULL) - return -ENOMEM; - adev->acp.acp_genpd->gpd.name = "ACP_AUDIO"; - adev->acp.acp_genpd->gpd.power_off = acp_poweroff; - adev->acp.acp_genpd->gpd.power_on = acp_poweron; + adev->acp.acp_genpd = kzalloc(sizeof(struct acp_pm_domain), GFP_KERNEL); + if (adev->acp.acp_genpd == NULL) + return -ENOMEM; + + adev->acp.acp_genpd->gpd.name = "ACP_AUDIO"; + adev->acp.acp_genpd->gpd.power_off = acp_poweroff; + adev->acp.acp_genpd->gpd.power_on = acp_poweron; - adev->acp.acp_genpd->cgs_dev = adev->acp.cgs_device; + adev->acp.acp_genpd->adev = adev; - pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false); - } + pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false); adev->acp.acp_cell = kcalloc(ACP_DEVS, sizeof(struct mfd_cell), GFP_KERNEL); @@ -429,17 +341,16 @@ static int acp_hw_init(void *handle) if (r) return r; - if (adev->asic_type != CHIP_STONEY) { - for (i = 0; i < ACP_DEVS ; i++) { - dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); - r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev); - if (r) { - dev_err(dev, "Failed to add dev to genpd\n"); - return r; - } + for (i = 0; i < ACP_DEVS ; i++) { + dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); + r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev); + if (r) { + dev_err(dev, "Failed to add dev to genpd\n"); + return r; } } + /* Assert Soft reset of ACP */ val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET); @@ -497,8 +408,10 @@ static int acp_hw_fini(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; /* return early if no ACP */ - if (!adev->acp.acp_cell) + if (!adev->acp.acp_genpd) { + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); return 0; + } /* Assert Soft reset of ACP */ val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET); @@ -536,19 +449,17 @@ static int acp_hw_fini(void *handle) udelay(100); } - if (adev->acp.acp_genpd) { - for (i = 0; i < ACP_DEVS ; i++) { - dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); - ret = pm_genpd_remove_device(dev); - /* If removal fails, dont giveup and try rest */ - if (ret) - dev_err(dev, "remove dev from genpd failed\n"); - } - kfree(adev->acp.acp_genpd); + for (i = 0; i < ACP_DEVS ; i++) { + dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); + ret = pm_genpd_remove_device(dev); + /* If removal fails, dont giveup and try rest */ + if (ret) + dev_err(dev, "remove dev from genpd failed\n"); } mfd_remove_devices(adev->acp.parent); kfree(adev->acp.acp_res); + kfree(adev->acp.acp_genpd); kfree(adev->acp.acp_cell); return 0; @@ -556,11 +467,21 @@ static int acp_hw_fini(void *handle) static int acp_suspend(void *handle) { + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + /* power up on suspend */ + if (!adev->acp.acp_cell) + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); return 0; } static int acp_resume(void *handle) { + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + /* power down again on resume */ + if (!adev->acp.acp_cell) + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); return 0; } @@ -593,6 +514,12 @@ static int acp_set_clockgating_state(void *handle, static int acp_set_powergating_state(void *handle, enum amd_powergating_state state) { + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + bool enable = state == AMD_PG_STATE_GATE ? true : false; + + if (adev->powerplay.pp_funcs->set_powergating_by_smu) + amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, enable); + return 0; } |