alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
From: Alex Deucher <alexdeucher@gmail.com>
To: broonie@kernel.org, airlied@gmail.com,
	dri-devel@lists.freedesktop.org, alsa-devel@alsa-project.org,
	maruthi.bayyavarapu@amd.com, rajeevkumar.linux@gmail.com
Cc: Alex Deucher <alexander.deucher@amd.com>,
	lgirdwood@gmail.com, perex@perex.cz
Subject: [PATCH 08/13] drm/amd: add pm domain for ACP IP sub blocks
Date: Fri,  4 Dec 2015 18:40:36 -0500	[thread overview]
Message-ID: <1449272440-8735-9-git-send-email-alexander.deucher@amd.com> (raw)
In-Reply-To: <1449272440-8735-1-git-send-email-alexander.deucher@amd.com>

ACP IP have internal DMA controller, DW I2S controller and DSPs
as separate power tiles. DMA and I2S devices are added to generic
pm domain, so that entire IP can be powered off/on at appropriate
times. Unused DSPs are made to be powered off though they are powered
on during ACP pm domain power on sequence.

Signed-off-by: Maruthi Bayyavarapu <maruthi.bayyavarapu@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
---
 drivers/gpu/drm/amd/acp/Kconfig         |   1 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 206 +++++++++++++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h |   1 +
 3 files changed, 207 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/acp/Kconfig b/drivers/gpu/drm/amd/acp/Kconfig
index 28b5e70..2b07813 100644
--- a/drivers/gpu/drm/amd/acp/Kconfig
+++ b/drivers/gpu/drm/amd/acp/Kconfig
@@ -4,6 +4,7 @@ config DRM_AMD_ACP
        bool "Enable ACP IP support"
        default y
        select MFD_CORE
+       select PM_GENERIC_DOMAINS if PM
        help
 	Choose this option to enable ACP IP support for AMD SOCs.
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
index eb5dc10..b425c44 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
@@ -24,6 +24,7 @@
  */
 
 #include <linux/irqdomain.h>
+#include <linux/pm_domain.h>
 #include <linux/platform_device.h>
 #include <sound/designware_i2s.h>
 #include <sound/pcm.h>
@@ -102,6 +103,155 @@ 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;
+	struct generic_pm_domain gpd;
+};
+
+static int acp_poweroff(struct generic_pm_domain *genpd)
+{
+	int i, ret;
+	struct acp_pm_domain *apd;
+
+	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);
+		}
+	}
+	return 0;
+}
+
+static int acp_poweron(struct generic_pm_domain *genpd)
+{
+	int i, ret;
+	struct acp_pm_domain *apd;
+
+	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);
+		}
+	}
+	return 0;
+}
+
+static struct device *get_mfd_cell_dev(const char *device_name, int r)
+{
+	char auto_dev_name[25];
+	char buf[8];
+	struct device *dev;
+
+	sprintf(buf, ".%d.auto", r);
+	strcpy(auto_dev_name, device_name);
+	strcat(auto_dev_name, buf);
+	dev = bus_find_device_by_name(&platform_bus_type, NULL, auto_dev_name);
+	dev_info(dev, "device %s added to pm domain\n", auto_dev_name);
+
+	return dev;
+}
+
 /**
  * acp_hw_init - start and test ACP block
  *
@@ -110,8 +260,9 @@ static int acp_sw_fini(void *handle)
  */
 static int acp_hw_init(void *handle)
 {
-	int r;
+	int r, i;
 	uint64_t acp_base;
+	struct device *dev;
 	struct i2s_platform_data *i2s_pdata;
 
 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -137,6 +288,19 @@ static int acp_hw_init(void *handle)
 	else if (r)
 		return r;
 
+	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;
+
+	pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false);
+
 	adev->acp.acp_cell = kzalloc(sizeof(struct mfd_cell) * ACP_DEVS,
 							GFP_KERNEL);
 
@@ -210,6 +374,15 @@ static int acp_hw_init(void *handle)
 	if (r)
 		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;
+		}
+	}
+
 	return 0;
 }
 
@@ -221,10 +394,22 @@ static int acp_hw_init(void *handle)
  */
 static int acp_hw_fini(void *handle)
 {
+	int i, ret;
+	struct device *dev;
+
 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
+	for (i = 0; i < ACP_DEVS ; i++) {
+		dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i);
+		ret = pm_genpd_remove_device(&adev->acp.acp_genpd->gpd, 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;
@@ -237,6 +422,25 @@ static int acp_suspend(void *handle)
 
 static int acp_resume(void *handle)
 {
+	int i, ret;
+	struct acp_pm_domain *apd;
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	/* SMU block will power on ACP irrespective of ACP runtime status.
+	 * Power off explicitly based on genpd ACP runtime status so that ACP
+	 * hw and ACP-genpd status are in sync.
+	 * 'suspend_power_off' represents "Power status before system suspend"
+	*/
+	if (adev->acp.acp_genpd->gpd.suspend_power_off == true) {
+		apd = container_of(&adev->acp.acp_genpd->gpd,
+					struct acp_pm_domain, gpd);
+
+		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);
+		}
+	}
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h
index 24952ed..f6e32a6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h
@@ -34,6 +34,7 @@ struct amdgpu_acp {
 	struct amd_acp_private *private;
 	struct mfd_cell *acp_cell;
 	struct resource *acp_res;
+	struct acp_pm_domain *acp_genpd;
 };
 
 extern const struct amd_ip_funcs acp_ip_funcs;
-- 
1.8.3.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

  parent reply	other threads:[~2015-12-04 23:40 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-04 23:40 [PATCH 00/13] Add ASoC support for AMD APUs [v5] Alex Deucher
2015-12-04 23:40 ` [PATCH 01/13] drm/amdgpu/cgs: add an interface to access PCI resources Alex Deucher
2015-12-04 23:40 ` [PATCH 02/13] drm/amdgpu: add irq domain support Alex Deucher
2015-12-04 23:40 ` [PATCH 03/13] ASoC: dwc: add runtime suspend/resume functionality Alex Deucher
2015-12-07 19:54   ` Applied "ASoC: dwc: add runtime suspend/resume functionality" to the asoc tree Mark Brown
2015-12-04 23:40 ` [PATCH 04/13] ASoC: dwc: add quirk for different register offset Alex Deucher
2015-12-07 19:54   ` Applied "ASoC: dwc: add quirk for different register offset" to the asoc tree Mark Brown
2015-12-04 23:40 ` [PATCH 05/13] ASoC: dwc: reconfigure dwc in 'resume' from 'suspend' Alex Deucher
2015-12-07 19:54   ` Applied "ASoC: dwc: reconfigure dwc in 'resume' from 'suspend'" to the asoc tree Mark Brown
2015-12-04 23:40 ` [PATCH 06/13] PM / Domains: export symbols to add/remove devices from genpd Alex Deucher
2015-12-18 11:25   ` Mark Brown
2015-12-04 23:40 ` [PATCH 07/13] drm/amd: add ACP driver support Alex Deucher
2015-12-04 23:40 ` Alex Deucher [this message]
2015-12-04 23:40 ` [PATCH 10/13] ASoC: AMD: add ACP 2.x IP DMA abstraction layer Alex Deucher
2015-12-18 11:22   ` Mark Brown
2015-12-04 23:40 ` [PATCH 11/13] ASoC: AMD: add AMD ASoC ACP 2.x DMA driver Alex Deucher
2015-12-18 12:04   ` Mark Brown
2015-12-04 23:40 ` [PATCH 12/13] ASoC: AMD: add pm ops Alex Deucher
2015-12-04 23:40 ` [PATCH 13/13] ASoC: AMD: Manage ACP 2.x SRAM banks power Alex Deucher
2015-12-18 12:08   ` Mark Brown
2015-12-18 12:09 ` [PATCH 00/13] Add ASoC support for AMD APUs [v5] Mark Brown
2015-12-21 19:08 ` Christian König

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1449272440-8735-9-git-send-email-alexander.deucher@amd.com \
    --to=alexdeucher@gmail.com \
    --cc=airlied@gmail.com \
    --cc=alexander.deucher@amd.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=lgirdwood@gmail.com \
    --cc=maruthi.bayyavarapu@amd.com \
    --cc=perex@perex.cz \
    --cc=rajeevkumar.linux@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).