[PATCH] drm/amdgpu/acp: Power on/off acp tiles within driver
Rex Zhu
Rex.Zhu at amd.com
Fri Aug 3 08:41:47 UTC 2018
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
More information about the amd-gfx
mailing list