[PATCH] drm/amdgpu/acp: Power on/off acp tiles within driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux