Different from ordinary stoney,For Stoney Fanless, smu firmware do not poweron/off acp tiles, so need to poweron/off acp in driver. Partially revert 'commit f766dd23e5ce ("drm/amdgpu/acp: Powrgate acp via smu")' Signed-off-by: Rex Zhu <Rex.Zhu at amd.com> --- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 118 +++++++++++++++++++++++++++++--- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h | 4 ++ 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 8bf3a98..f673c99 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -91,6 +91,8 @@ enum { ACP_TILE_DSP1, ACP_TILE_DSP2, }; +static int acp_set_powergating_state(void *handle, + enum amd_powergating_state state); static int acp_sw_init(void *handle) { @@ -135,8 +137,7 @@ static int acp_poweroff(struct generic_pm_domain *genpd) * 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); + acp_set_powergating_state(adev, AMD_PG_STATE_GATE); } return 0; } @@ -155,8 +156,7 @@ static int acp_poweron(struct generic_pm_domain *genpd) * 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); + acp_set_powergating_state(adev, AMD_PG_STATE_UNGATE); } return 0; } @@ -201,7 +201,7 @@ static int acp_hw_init(void *handle) ip_block->version->major, ip_block->version->minor); /* -ENODEV means board uses AZ rather than ACP */ if (r == -ENODEV) { - amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); + acp_set_powergating_state(adev, AMD_PG_STATE_GATE); return 0; } else if (r) { return r; @@ -407,7 +407,7 @@ static int acp_hw_fini(void *handle) /* return early if no ACP */ if (!adev->acp.acp_genpd) { - amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); + acp_set_powergating_state(adev, AMD_PG_STATE_UNGATE); return 0; } @@ -469,7 +469,7 @@ static int acp_suspend(void *handle) /* power up on suspend */ if (!adev->acp.acp_cell) - amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, false); + acp_set_powergating_state(adev, AMD_PG_STATE_UNGATE); return 0; } @@ -479,7 +479,7 @@ static int acp_resume(void *handle) /* power down again on resume */ if (!adev->acp.acp_cell) - amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true); + acp_set_powergating_state(adev, AMD_PG_STATE_GATE); return 0; } @@ -509,15 +509,117 @@ static int acp_set_clockgating_state(void *handle, return 0; } +/* power off a tile/block within ACP */ +static int acp_suspend_tile(struct amdgpu_device *adev, 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 = RREG32(mmACP_PGFSM_READ_REG_0 + tile); + val &= ACP_TILE_ON_MASK; + + if (val == 0x0) { + val = RREG32(mmACP_PGFSM_RETAIN_REG); + val = val | (1 << tile); + WREG32(mmACP_PGFSM_RETAIN_REG, val); + WREG32(mmACP_PGFSM_CONFIG_REG, + 0x500 + tile); + + count = ACP_TIMEOUT_LOOP; + while (true) { + val = RREG32(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 = RREG32(mmACP_PGFSM_RETAIN_REG); + + val |= ACP_TILE_OFF_RETAIN_REG_MASK; + WREG32(mmACP_PGFSM_RETAIN_REG, val); + } + return 0; +} + +/* power on a tile/block within ACP */ +static int acp_resume_tile(struct amdgpu_device *adev, 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 = RREG32(mmACP_PGFSM_READ_REG_0 + tile); + val = val & ACP_TILE_ON_MASK; + + if (val != 0x0) { + WREG32(mmACP_PGFSM_CONFIG_REG, + 0x600 + tile); + count = ACP_TIMEOUT_LOOP; + while (true) { + val = RREG32(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 = RREG32(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); + + WREG32(mmACP_PGFSM_RETAIN_REG, val); + } + return 0; +} + 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; + int i, ret; + + if (!enable && IS_ST_KICKER(adev)) { + for (i = 0; i < 2; i++) { + /* do not power up DSPs which are not going to be used */ + ret = acp_resume_tile(adev, ACP_TILE_P1 + i); + if (ret) { + pr_err("ACP tile %d resume failed\n", i); + break; + } + } + } if (adev->powerplay.pp_funcs->set_powergating_by_smu) amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, enable); + if (enable && IS_ST_KICKER(adev)) { + for (i = 0; i < 5; i++) { + ret = acp_suspend_tile(adev, ACP_TILE_P1 + i); + if (ret) + pr_err("ACP tile %d suspend failed\n", i); + } + } + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h index a288ce2..fcb2e7d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h @@ -39,4 +39,8 @@ struct amdgpu_acp { extern const struct amdgpu_ip_block_version acp_ip_block; +#define IS_ST_KICKER(dev) ((dev)->asic_type == CHIP_STONEY && \ + (((dev)->pm.fw_version & 0xff00) == 0x2100 || \ + ((dev)->pm.fw_version & 0xff00) == 0x1A00)) + #endif /* __AMDGPU_ACP_H__ */ -- 1.9.1