blob: 9f8cfaab30040b2f3bcc051d3b051055b1094477 [file] [log] [blame]
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -04001/*
2 * Copyright 2015 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: AMD
23 *
24 */
25
26#include <linux/irqdomain.h>
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +053027#include <linux/pm_domain.h>
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -040028#include <linux/platform_device.h>
29#include <sound/designware_i2s.h>
30#include <sound/pcm.h>
31
32#include "amdgpu.h"
33#include "atom.h"
34#include "amdgpu_acp.h"
35
36#include "acp_gfx_if.h"
37
38#define ACP_TILE_ON_MASK 0x03
39#define ACP_TILE_OFF_MASK 0x02
40#define ACP_TILE_ON_RETAIN_REG_MASK 0x1f
41#define ACP_TILE_OFF_RETAIN_REG_MASK 0x20
42
43#define ACP_TILE_P1_MASK 0x3e
44#define ACP_TILE_P2_MASK 0x3d
45#define ACP_TILE_DSP0_MASK 0x3b
46#define ACP_TILE_DSP1_MASK 0x37
47
48#define ACP_TILE_DSP2_MASK 0x2f
49
50#define ACP_DMA_REGS_END 0x146c0
51#define ACP_I2S_PLAY_REGS_START 0x14840
52#define ACP_I2S_PLAY_REGS_END 0x148b4
53#define ACP_I2S_CAP_REGS_START 0x148b8
54#define ACP_I2S_CAP_REGS_END 0x1496c
55
56#define ACP_I2S_COMP1_CAP_REG_OFFSET 0xac
57#define ACP_I2S_COMP2_CAP_REG_OFFSET 0xa8
58#define ACP_I2S_COMP1_PLAY_REG_OFFSET 0x6c
59#define ACP_I2S_COMP2_PLAY_REG_OFFSET 0x68
60
61#define mmACP_PGFSM_RETAIN_REG 0x51c9
62#define mmACP_PGFSM_CONFIG_REG 0x51ca
63#define mmACP_PGFSM_READ_REG_0 0x51cc
64
65#define mmACP_MEM_SHUT_DOWN_REQ_LO 0x51f8
66#define mmACP_MEM_SHUT_DOWN_REQ_HI 0x51f9
67#define mmACP_MEM_SHUT_DOWN_STS_LO 0x51fa
68#define mmACP_MEM_SHUT_DOWN_STS_HI 0x51fb
69
70#define ACP_TIMEOUT_LOOP 0x000000FF
71#define ACP_DEVS 3
72#define ACP_SRC_ID 162
73
74enum {
75 ACP_TILE_P1 = 0,
76 ACP_TILE_P2,
77 ACP_TILE_DSP0,
78 ACP_TILE_DSP1,
79 ACP_TILE_DSP2,
80};
81
82static int acp_sw_init(void *handle)
83{
84 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
85
86 adev->acp.parent = adev->dev;
87
88 adev->acp.cgs_device =
89 amdgpu_cgs_create_device(adev);
90 if (!adev->acp.cgs_device)
91 return -EINVAL;
92
93 return 0;
94}
95
96static int acp_sw_fini(void *handle)
97{
98 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
99
100 if (adev->acp.cgs_device)
101 amdgpu_cgs_destroy_device(adev->acp.cgs_device);
102
103 return 0;
104}
105
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530106/* 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
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400255/**
256 * acp_hw_init - start and test ACP block
257 *
258 * @adev: amdgpu_device pointer
259 *
260 */
261static int acp_hw_init(void *handle)
262{
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530263 int r, i;
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400264 uint64_t acp_base;
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530265 struct device *dev;
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400266 struct i2s_platform_data *i2s_pdata;
267
268 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
269
270 const struct amdgpu_ip_block_version *ip_version =
271 amdgpu_get_ip_block(adev, AMD_IP_BLOCK_TYPE_ACP);
272
273 if (!ip_version)
274 return -EINVAL;
275
276 r = amd_acp_hw_init(adev->acp.cgs_device,
277 ip_version->major, ip_version->minor);
278 /* -ENODEV means board uses AZ rather than ACP */
279 if (r == -ENODEV)
280 return 0;
281 else if (r)
282 return r;
283
284 r = cgs_get_pci_resource(adev->acp.cgs_device, CGS_RESOURCE_TYPE_MMIO,
285 0x5289, 0, &acp_base);
286 if (r == -ENODEV)
287 return 0;
288 else if (r)
289 return r;
290
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530291 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
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400304 adev->acp.acp_cell = kzalloc(sizeof(struct mfd_cell) * ACP_DEVS,
305 GFP_KERNEL);
306
307 if (adev->acp.acp_cell == NULL)
308 return -ENOMEM;
309
310 adev->acp.acp_res = kzalloc(sizeof(struct resource) * 4, GFP_KERNEL);
311
312 if (adev->acp.acp_res == NULL) {
313 kfree(adev->acp.acp_cell);
314 return -ENOMEM;
315 }
316
317 i2s_pdata = kzalloc(sizeof(struct i2s_platform_data) * 2, GFP_KERNEL);
318 if (i2s_pdata == NULL) {
319 kfree(adev->acp.acp_res);
320 kfree(adev->acp.acp_cell);
321 return -ENOMEM;
322 }
323
324 i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
325 i2s_pdata[0].cap = DWC_I2S_PLAY;
326 i2s_pdata[0].snd_rates = SNDRV_PCM_RATE_8000_96000;
327 i2s_pdata[0].i2s_reg_comp1 = ACP_I2S_COMP1_PLAY_REG_OFFSET;
328 i2s_pdata[0].i2s_reg_comp2 = ACP_I2S_COMP2_PLAY_REG_OFFSET;
329
330 i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
331 DW_I2S_QUIRK_COMP_PARAM1;
332 i2s_pdata[1].cap = DWC_I2S_RECORD;
333 i2s_pdata[1].snd_rates = SNDRV_PCM_RATE_8000_96000;
334 i2s_pdata[1].i2s_reg_comp1 = ACP_I2S_COMP1_CAP_REG_OFFSET;
335 i2s_pdata[1].i2s_reg_comp2 = ACP_I2S_COMP2_CAP_REG_OFFSET;
336
337 adev->acp.acp_res[0].name = "acp2x_dma";
338 adev->acp.acp_res[0].flags = IORESOURCE_MEM;
339 adev->acp.acp_res[0].start = acp_base;
340 adev->acp.acp_res[0].end = acp_base + ACP_DMA_REGS_END;
341
342 adev->acp.acp_res[1].name = "acp2x_dw_i2s_play";
343 adev->acp.acp_res[1].flags = IORESOURCE_MEM;
344 adev->acp.acp_res[1].start = acp_base + ACP_I2S_PLAY_REGS_START;
345 adev->acp.acp_res[1].end = acp_base + ACP_I2S_PLAY_REGS_END;
346
347 adev->acp.acp_res[2].name = "acp2x_dw_i2s_cap";
348 adev->acp.acp_res[2].flags = IORESOURCE_MEM;
349 adev->acp.acp_res[2].start = acp_base + ACP_I2S_CAP_REGS_START;
350 adev->acp.acp_res[2].end = acp_base + ACP_I2S_CAP_REGS_END;
351
352 adev->acp.acp_res[3].name = "acp2x_dma_irq";
353 adev->acp.acp_res[3].flags = IORESOURCE_IRQ;
354 adev->acp.acp_res[3].start = amdgpu_irq_create_mapping(adev, 162);
355 adev->acp.acp_res[3].end = adev->acp.acp_res[3].start;
356
357 adev->acp.acp_cell[0].name = "acp_audio_dma";
358 adev->acp.acp_cell[0].num_resources = 4;
359 adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0];
360
361 adev->acp.acp_cell[1].name = "designware-i2s";
362 adev->acp.acp_cell[1].num_resources = 1;
363 adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
364 adev->acp.acp_cell[1].platform_data = &i2s_pdata[0];
365 adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);
366
367 adev->acp.acp_cell[2].name = "designware-i2s";
368 adev->acp.acp_cell[2].num_resources = 1;
369 adev->acp.acp_cell[2].resources = &adev->acp.acp_res[2];
370 adev->acp.acp_cell[2].platform_data = &i2s_pdata[1];
371 adev->acp.acp_cell[2].pdata_size = sizeof(struct i2s_platform_data);
372
373 r = mfd_add_hotplug_devices(adev->acp.parent, adev->acp.acp_cell,
374 ACP_DEVS);
375 if (r)
376 return r;
377
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530378 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
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400387 return 0;
388}
389
390/**
391 * acp_hw_fini - stop the hardware block
392 *
393 * @adev: amdgpu_device pointer
394 *
395 */
396static int acp_hw_fini(void *handle)
397{
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530398 int i, ret;
399 struct device *dev;
400
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400401 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
402
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530403 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
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400411 mfd_remove_devices(adev->acp.parent);
412 kfree(adev->acp.acp_res);
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530413 kfree(adev->acp.acp_genpd);
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400414 kfree(adev->acp.acp_cell);
415
416 return 0;
417}
418
419static int acp_suspend(void *handle)
420{
421 return 0;
422}
423
424static int acp_resume(void *handle)
425{
Maruthi Srinivas Bayyavarapu250303212015-11-23 21:07:30 +0530426 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 }
Maruthi Bayyavarapua8fe58c2015-09-22 17:05:20 -0400445 return 0;
446}
447
448static int acp_early_init(void *handle)
449{
450 return 0;
451}
452
453static bool acp_is_idle(void *handle)
454{
455 return true;
456}
457
458static int acp_wait_for_idle(void *handle)
459{
460 return 0;
461}
462
463static int acp_soft_reset(void *handle)
464{
465 return 0;
466}
467
468static void acp_print_status(void *handle)
469{
470 struct amdgpu_device *adev = (struct amdgpu_device *)handle;
471
472 dev_info(adev->dev, "ACP STATUS\n");
473}
474
475static int acp_set_clockgating_state(void *handle,
476 enum amd_clockgating_state state)
477{
478 return 0;
479}
480
481static int acp_set_powergating_state(void *handle,
482 enum amd_powergating_state state)
483{
484 return 0;
485}
486
487const struct amd_ip_funcs acp_ip_funcs = {
488 .early_init = acp_early_init,
489 .late_init = NULL,
490 .sw_init = acp_sw_init,
491 .sw_fini = acp_sw_fini,
492 .hw_init = acp_hw_init,
493 .hw_fini = acp_hw_fini,
494 .suspend = acp_suspend,
495 .resume = acp_resume,
496 .is_idle = acp_is_idle,
497 .wait_for_idle = acp_wait_for_idle,
498 .soft_reset = acp_soft_reset,
499 .print_status = acp_print_status,
500 .set_clockgating_state = acp_set_clockgating_state,
501 .set_powergating_state = acp_set_powergating_state,
502};