aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaruthi Srinivas Bayyavarapu <Maruthi.Bayyavarapu@amd.com>2015-11-23 10:37:30 -0500
committerAlex Deucher <alexander.deucher@amd.com>2016-02-10 14:17:09 -0500
commit25030321ba2860c56651429a4f28d94a6483d952 (patch)
treefe5743ed9cf02b33fd64a0b6dee19f2e8a1ef126
parenta8fe58cec351c25e09c393bf46117c0c47b5a17c (diff)
drm/amd: add pm domain for ACP IP sub blocks
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>
-rw-r--r--drivers/gpu/drm/amd/acp/Kconfig1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c206
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h1
3 files changed, 207 insertions, 1 deletions
diff --git a/drivers/gpu/drm/amd/acp/Kconfig b/drivers/gpu/drm/amd/acp/Kconfig
index 28b5e70b502f..2b07813bceed 100644
--- a/drivers/gpu/drm/amd/acp/Kconfig
+++ b/drivers/gpu/drm/amd/acp/Kconfig
@@ -4,6 +4,7 @@ config DRM_AMD_ACP
4 bool "Enable ACP IP support" 4 bool "Enable ACP IP support"
5 default y 5 default y
6 select MFD_CORE 6 select MFD_CORE
7 select PM_GENERIC_DOMAINS if PM
7 help 8 help
8 Choose this option to enable ACP IP support for AMD SOCs. 9 Choose this option to enable ACP IP support for AMD SOCs.
9 10
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
index 71f26e93ef58..9f8cfaab3004 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
@@ -24,6 +24,7 @@
24 */ 24 */
25 25
26#include <linux/irqdomain.h> 26#include <linux/irqdomain.h>
27#include <linux/pm_domain.h>
27#include <linux/platform_device.h> 28#include <linux/platform_device.h>
28#include <sound/designware_i2s.h> 29#include <sound/designware_i2s.h>
29#include <sound/pcm.h> 30#include <sound/pcm.h>
@@ -102,6 +103,155 @@ static int acp_sw_fini(void *handle)
102 return 0; 103 return 0;
103} 104}
104 105
106/* power off a tile/block within ACP */
107static int acp_suspend_tile(void *cgs_dev, int tile)
108{
109 u32 val = 0;
110 u32 count = 0;
111
112 if ((tile < ACP_TILE_P1) || (tile > ACP_TILE_DSP2)) {
113 pr_err("Invalid ACP tile : %d to suspend\n", tile);
114 return -1;
115 }
116
117 val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 + tile);
118 val &= ACP_TILE_ON_MASK;
119
120 if (val == 0x0) {
121 val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG);
122 val = val | (1 << tile);
123 cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val);
124 cgs_write_register(cgs_dev, mmACP_PGFSM_CONFIG_REG,
125 0x500 + tile);
126
127 count = ACP_TIMEOUT_LOOP;
128 while (true) {
129 val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0
130 + tile);
131 val = val & ACP_TILE_ON_MASK;
132 if (val == ACP_TILE_OFF_MASK)
133 break;
134 if (--count == 0) {
135 pr_err("Timeout reading ACP PGFSM status\n");
136 return -ETIMEDOUT;
137 }
138 udelay(100);
139 }
140
141 val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG);
142
143 val |= ACP_TILE_OFF_RETAIN_REG_MASK;
144 cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val);
145 }
146 return 0;
147}
148
149/* power on a tile/block within ACP */
150static int acp_resume_tile(void *cgs_dev, int tile)
151{
152 u32 val = 0;
153 u32 count = 0;
154
155 if ((tile < ACP_TILE_P1) || (tile > ACP_TILE_DSP2)) {
156 pr_err("Invalid ACP tile to resume\n");
157 return -1;
158 }
159
160 val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0 + tile);
161 val = val & ACP_TILE_ON_MASK;
162
163 if (val != 0x0) {
164 cgs_write_register(cgs_dev, mmACP_PGFSM_CONFIG_REG,
165 0x600 + tile);
166 count = ACP_TIMEOUT_LOOP;
167 while (true) {
168 val = cgs_read_register(cgs_dev, mmACP_PGFSM_READ_REG_0
169 + tile);
170 val = val & ACP_TILE_ON_MASK;
171 if (val == 0x0)
172 break;
173 if (--count == 0) {
174 pr_err("Timeout reading ACP PGFSM status\n");
175 return -ETIMEDOUT;
176 }
177 udelay(100);
178 }
179 val = cgs_read_register(cgs_dev, mmACP_PGFSM_RETAIN_REG);
180 if (tile == ACP_TILE_P1)
181 val = val & (ACP_TILE_P1_MASK);
182 else if (tile == ACP_TILE_P2)
183 val = val & (ACP_TILE_P2_MASK);
184
185 cgs_write_register(cgs_dev, mmACP_PGFSM_RETAIN_REG, val);
186 }
187 return 0;
188}
189
190struct acp_pm_domain {
191 void *cgs_dev;
192 struct generic_pm_domain gpd;
193};
194
195static int acp_poweroff(struct generic_pm_domain *genpd)
196{
197 int i, ret;
198 struct acp_pm_domain *apd;
199
200 apd = container_of(genpd, struct acp_pm_domain, gpd);
201 if (apd != NULL) {
202 /* Donot return abruptly if any of power tile fails to suspend.
203 * Log it and continue powering off other tile
204 */
205 for (i = 4; i >= 0 ; i--) {
206 ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_P1 + i);
207 if (ret)
208 pr_err("ACP tile %d tile suspend failed\n", i);
209 }
210 }
211 return 0;
212}
213
214static int acp_poweron(struct generic_pm_domain *genpd)
215{
216 int i, ret;
217 struct acp_pm_domain *apd;
218
219 apd = container_of(genpd, struct acp_pm_domain, gpd);
220 if (apd != NULL) {
221 for (i = 0; i < 2; i++) {
222 ret = acp_resume_tile(apd->cgs_dev, ACP_TILE_P1 + i);
223 if (ret) {
224 pr_err("ACP tile %d resume failed\n", i);
225 break;
226 }
227 }
228
229 /* Disable DSPs which are not going to be used */
230 for (i = 0; i < 3; i++) {
231 ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_DSP0 + i);
232 /* Continue suspending other DSP, even if one fails */
233 if (ret)
234 pr_err("ACP DSP %d suspend failed\n", i);
235 }
236 }
237 return 0;
238}
239
240static struct device *get_mfd_cell_dev(const char *device_name, int r)
241{
242 char auto_dev_name[25];
243 char buf[8];
244 struct device *dev;
245
246 sprintf(buf, ".%d.auto", r);
247 strcpy(auto_dev_name, device_name);
248 strcat(auto_dev_name, buf);
249 dev = bus_find_device_by_name(&platform_bus_type, NULL, auto_dev_name);
250 dev_info(dev, "device %s added to pm domain\n", auto_dev_name);
251
252 return dev;
253}
254
105/** 255/**
106 * acp_hw_init - start and test ACP block 256 * acp_hw_init - start and test ACP block
107 * 257 *
@@ -110,8 +260,9 @@ static int acp_sw_fini(void *handle)
110 */ 260 */
111static int acp_hw_init(void *handle) 261static int acp_hw_init(void *handle)
112{ 262{
113 int r; 263 int r, i;
114 uint64_t acp_base; 264 uint64_t acp_base;
265 struct device *dev;
115 struct i2s_platform_data *i2s_pdata; 266 struct i2s_platform_data *i2s_pdata;
116 267
117 struct amdgpu_device *adev = (struct amdgpu_device *)handle; 268 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -137,6 +288,19 @@ static int acp_hw_init(void *handle)
137 else if (r) 288 else if (r)
138 return r; 289 return r;
139 290
291 adev->acp.acp_genpd = kzalloc(sizeof(struct acp_pm_domain), GFP_KERNEL);
292 if (adev->acp.acp_genpd == NULL)
293 return -ENOMEM;
294
295 adev->acp.acp_genpd->gpd.name = "ACP_AUDIO";
296 adev->acp.acp_genpd->gpd.power_off = acp_poweroff;
297 adev->acp.acp_genpd->gpd.power_on = acp_poweron;
298
299
300 adev->acp.acp_genpd->cgs_dev = adev->acp.cgs_device;
301
302 pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false);
303
140 adev->acp.acp_cell = kzalloc(sizeof(struct mfd_cell) * ACP_DEVS, 304 adev->acp.acp_cell = kzalloc(sizeof(struct mfd_cell) * ACP_DEVS,
141 GFP_KERNEL); 305 GFP_KERNEL);
142 306
@@ -211,6 +375,15 @@ static int acp_hw_init(void *handle)
211 if (r) 375 if (r)
212 return r; 376 return r;
213 377
378 for (i = 0; i < ACP_DEVS ; i++) {
379 dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i);
380 r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev);
381 if (r) {
382 dev_err(dev, "Failed to add dev to genpd\n");
383 return r;
384 }
385 }
386
214 return 0; 387 return 0;
215} 388}
216 389
@@ -222,10 +395,22 @@ static int acp_hw_init(void *handle)
222 */ 395 */
223static int acp_hw_fini(void *handle) 396static int acp_hw_fini(void *handle)
224{ 397{
398 int i, ret;
399 struct device *dev;
400
225 struct amdgpu_device *adev = (struct amdgpu_device *)handle; 401 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
226 402
403 for (i = 0; i < ACP_DEVS ; i++) {
404 dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i);
405 ret = pm_genpd_remove_device(&adev->acp.acp_genpd->gpd, dev);
406 /* If removal fails, dont giveup and try rest */
407 if (ret)
408 dev_err(dev, "remove dev from genpd failed\n");
409 }
410
227 mfd_remove_devices(adev->acp.parent); 411 mfd_remove_devices(adev->acp.parent);
228 kfree(adev->acp.acp_res); 412 kfree(adev->acp.acp_res);
413 kfree(adev->acp.acp_genpd);
229 kfree(adev->acp.acp_cell); 414 kfree(adev->acp.acp_cell);
230 415
231 return 0; 416 return 0;
@@ -238,6 +423,25 @@ static int acp_suspend(void *handle)
238 423
239static int acp_resume(void *handle) 424static int acp_resume(void *handle)
240{ 425{
426 int i, ret;
427 struct acp_pm_domain *apd;
428 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
429
430 /* SMU block will power on ACP irrespective of ACP runtime status.
431 * Power off explicitly based on genpd ACP runtime status so that ACP
432 * hw and ACP-genpd status are in sync.
433 * 'suspend_power_off' represents "Power status before system suspend"
434 */
435 if (adev->acp.acp_genpd->gpd.suspend_power_off == true) {
436 apd = container_of(&adev->acp.acp_genpd->gpd,
437 struct acp_pm_domain, gpd);
438
439 for (i = 4; i >= 0 ; i--) {
440 ret = acp_suspend_tile(apd->cgs_dev, ACP_TILE_P1 + i);
441 if (ret)
442 pr_err("ACP tile %d tile suspend failed\n", i);
443 }
444 }
241 return 0; 445 return 0;
242} 446}
243 447
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h
index 24952ed8209e..f6e32a639107 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 {
34 struct amd_acp_private *private; 34 struct amd_acp_private *private;
35 struct mfd_cell *acp_cell; 35 struct mfd_cell *acp_cell;
36 struct resource *acp_res; 36 struct resource *acp_res;
37 struct acp_pm_domain *acp_genpd;
37}; 38};
38 39
39extern const struct amd_ip_funcs acp_ip_funcs; 40extern const struct amd_ip_funcs acp_ip_funcs;