xref: /openbmc/linux/sound/soc/sof/amd/acp-stream.c (revision 7ae5c03a)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license. When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2021 Advanced Micro Devices, Inc.
7 //
8 // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
9 
10 /*
11  * Hardware interface for generic AMD audio DSP ACP IP
12  */
13 
14 #include "../ops.h"
15 #include "acp-dsp-offset.h"
16 #include "acp.h"
17 
18 #define PTE_GRP1_OFFSET		0x00000000
19 #define PTE_GRP2_OFFSET		0x00800000
20 #define PTE_GRP3_OFFSET		0x01000000
21 #define PTE_GRP4_OFFSET		0x01800000
22 #define PTE_GRP5_OFFSET		0x02000000
23 #define PTE_GRP6_OFFSET		0x02800000
24 #define PTE_GRP7_OFFSET		0x03000000
25 #define PTE_GRP8_OFFSET		0x03800000
26 
27 int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream)
28 {
29 	unsigned int pte_reg, pte_size, phy_addr_offset, index;
30 	int stream_tag = stream->stream_tag;
31 	u32 low, high, offset, reg_val;
32 	dma_addr_t addr;
33 	int page_idx;
34 
35 	switch (stream_tag) {
36 	case 1:
37 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1;
38 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1;
39 		offset = offsetof(struct scratch_reg_conf, grp1_pte);
40 		stream->reg_offset = PTE_GRP1_OFFSET;
41 		break;
42 	case 2:
43 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2;
44 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2;
45 		offset = offsetof(struct scratch_reg_conf, grp2_pte);
46 		stream->reg_offset = PTE_GRP2_OFFSET;
47 		break;
48 	case 3:
49 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3;
50 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3;
51 		offset = offsetof(struct scratch_reg_conf, grp3_pte);
52 		stream->reg_offset = PTE_GRP3_OFFSET;
53 		break;
54 	case 4:
55 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4;
56 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4;
57 		offset = offsetof(struct scratch_reg_conf, grp4_pte);
58 		stream->reg_offset = PTE_GRP4_OFFSET;
59 		break;
60 	case 5:
61 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
62 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
63 		offset = offsetof(struct scratch_reg_conf, grp5_pte);
64 		stream->reg_offset = PTE_GRP5_OFFSET;
65 		break;
66 	case 6:
67 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6;
68 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6;
69 		offset = offsetof(struct scratch_reg_conf, grp6_pte);
70 		stream->reg_offset = PTE_GRP6_OFFSET;
71 		break;
72 	case 7:
73 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7;
74 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7;
75 		offset = offsetof(struct scratch_reg_conf, grp7_pte);
76 		stream->reg_offset = PTE_GRP7_OFFSET;
77 		break;
78 	case 8:
79 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8;
80 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8;
81 		offset = offsetof(struct scratch_reg_conf, grp8_pte);
82 		stream->reg_offset = PTE_GRP8_OFFSET;
83 		break;
84 	default:
85 		dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag);
86 		return -EINVAL;
87 	}
88 
89 	/* write phy_addr in scratch memory */
90 
91 	phy_addr_offset = offsetof(struct scratch_reg_conf, reg_offset);
92 	index = stream_tag - 1;
93 	phy_addr_offset = phy_addr_offset + index * 4;
94 
95 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 +
96 			  phy_addr_offset, stream->reg_offset);
97 
98 	/* Group Enable */
99 	reg_val = ACP_SRAM_PTE_OFFSET + offset;
100 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31));
101 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE);
102 
103 	for (page_idx = 0; page_idx < stream->num_pages; page_idx++) {
104 		addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE);
105 
106 		/* Load the low address of page int ACP SRAM through SRBM */
107 		low = lower_32_bits(addr);
108 		high = upper_32_bits(addr);
109 
110 		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
111 
112 		high |= BIT(31);
113 		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
114 		/* Move to next physically contiguous page */
115 		offset += 8;
116 	}
117 
118 	/* Flush ATU Cache after PTE Update */
119 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
120 
121 	return 0;
122 }
123 
124 struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag)
125 {
126 	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
127 	struct acp_dsp_stream *stream = adata->stream_buf;
128 	int i;
129 
130 	for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
131 		if (stream->active)
132 			continue;
133 
134 		/* return stream if tag not specified*/
135 		if (!tag) {
136 			stream->active = 1;
137 			return stream;
138 		}
139 
140 		/* check if this is the requested stream tag */
141 		if (stream->stream_tag == tag) {
142 			stream->active = 1;
143 			return stream;
144 		}
145 	}
146 
147 	dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag);
148 	return NULL;
149 }
150 EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON);
151 
152 int acp_dsp_stream_put(struct snd_sof_dev *sdev,
153 		       struct acp_dsp_stream *acp_stream)
154 {
155 	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
156 	struct acp_dsp_stream *stream = adata->stream_buf;
157 	int i;
158 
159 	/* Free an active stream */
160 	for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
161 		if (stream == acp_stream) {
162 			stream->active = 0;
163 			return 0;
164 		}
165 	}
166 
167 	dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag);
168 	return -EINVAL;
169 }
170 EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON);
171 
172 int acp_dsp_stream_init(struct snd_sof_dev *sdev)
173 {
174 	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
175 	int i;
176 
177 	for (i = 0; i < ACP_MAX_STREAM; i++) {
178 		adata->stream_buf[i].sdev = sdev;
179 		adata->stream_buf[i].active = 0;
180 		adata->stream_buf[i].stream_tag = i + 1;
181 	}
182 	return 0;
183 }
184 EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON);
185