diff options
Diffstat (limited to 'meta-amdfalconx86/recipes-kernel/linux/files/1132-drm-amd-Power-management-related-modifications-in-th.patch')
-rw-r--r-- | meta-amdfalconx86/recipes-kernel/linux/files/1132-drm-amd-Power-management-related-modifications-in-th.patch | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/meta-amdfalconx86/recipes-kernel/linux/files/1132-drm-amd-Power-management-related-modifications-in-th.patch b/meta-amdfalconx86/recipes-kernel/linux/files/1132-drm-amd-Power-management-related-modifications-in-th.patch new file mode 100644 index 00000000..f1a7886b --- /dev/null +++ b/meta-amdfalconx86/recipes-kernel/linux/files/1132-drm-amd-Power-management-related-modifications-in-th.patch @@ -0,0 +1,280 @@ +From 70cc896479d8491bb6892312b54613439af01316 Mon Sep 17 00:00:00 2001 +From: Maruthi Srinivas Bayyavarapu <Maruthi.Bayyavarapu@amd.com> +Date: Fri, 23 Sep 2016 17:35:10 +0530 +Subject: [PATCH 07/17] drm/amd: Power management related modifications in the + +amdgpu ACP driver. + +Signed-off-by: Maruthi Bayyavarapu <maruthi.bayyavarapu@amd.com> +Signed-off-by: Kalyan Alle <kalyan.alle@amd.com> +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 206 +++++++++++++++++++++++++++++++- + 1 file changed, 205 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +index cbbc31a8..37ecc36 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); + +@@ -211,6 +375,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; + } + +@@ -222,10 +395,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; +@@ -238,6 +423,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; + } + +-- +2.7.4 + |