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) 2023 Advanced Micro Devices, Inc. |
7 | // |
8 | // Authors: Syed Saba Kareem <Syed.SabaKareem@amd.com> |
9 | // |
10 | |
11 | /* |
12 | * Common file to be used by amd platforms |
13 | */ |
14 | |
15 | #include "amd.h" |
16 | #include <linux/pci.h> |
17 | #include <linux/export.h> |
18 | |
19 | #define ACP_RENOIR_PDM_ADDR 0x02 |
20 | #define ACP_REMBRANDT_PDM_ADDR 0x03 |
21 | #define ACP63_PDM_ADDR 0x02 |
22 | #define ACP70_PDM_ADDR 0x02 |
23 | |
24 | void acp_enable_interrupts(struct acp_dev_data *adata) |
25 | { |
26 | struct acp_resource *rsrc = adata->rsrc; |
27 | u32 ext_intr_ctrl; |
28 | |
29 | writel(val: 0x01, ACP_EXTERNAL_INTR_ENB(adata)); |
30 | ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); |
31 | ext_intr_ctrl |= ACP_ERROR_MASK; |
32 | writel(val: ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); |
33 | } |
34 | EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, SND_SOC_ACP_COMMON); |
35 | |
36 | void acp_disable_interrupts(struct acp_dev_data *adata) |
37 | { |
38 | struct acp_resource *rsrc = adata->rsrc; |
39 | |
40 | writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); |
41 | writel(val: 0x00, ACP_EXTERNAL_INTR_ENB(adata)); |
42 | } |
43 | EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, SND_SOC_ACP_COMMON); |
44 | |
45 | static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream, |
46 | struct snd_soc_dai *dai) |
47 | { |
48 | struct snd_pcm_runtime *runtime = substream->runtime; |
49 | struct acp_stream *stream = runtime->private_data; |
50 | struct device *dev = dai->component->dev; |
51 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
52 | |
53 | u32 physical_addr, pdm_size, period_bytes; |
54 | |
55 | period_bytes = frames_to_bytes(runtime, size: runtime->period_size); |
56 | pdm_size = frames_to_bytes(runtime, size: runtime->buffer_size); |
57 | physical_addr = stream->reg_offset + MEM_WINDOW_START; |
58 | |
59 | /* Init ACP PDM Ring buffer */ |
60 | writel(val: physical_addr, addr: adata->acp_base + ACP_WOV_RX_RINGBUFADDR); |
61 | writel(val: pdm_size, addr: adata->acp_base + ACP_WOV_RX_RINGBUFSIZE); |
62 | writel(val: period_bytes, addr: adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); |
63 | writel(val: 0x01, addr: adata->acp_base + ACPAXI2AXI_ATU_CTRL); |
64 | } |
65 | |
66 | static void set_acp_pdm_clk(struct snd_pcm_substream *substream, |
67 | struct snd_soc_dai *dai) |
68 | { |
69 | struct device *dev = dai->component->dev; |
70 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
71 | unsigned int pdm_ctrl; |
72 | |
73 | /* Enable default ACP PDM clk */ |
74 | writel(PDM_CLK_FREQ_MASK, addr: adata->acp_base + ACP_WOV_CLK_CTRL); |
75 | pdm_ctrl = readl(addr: adata->acp_base + ACP_WOV_MISC_CTRL); |
76 | pdm_ctrl |= PDM_MISC_CTRL_MASK; |
77 | writel(val: pdm_ctrl, addr: adata->acp_base + ACP_WOV_MISC_CTRL); |
78 | set_acp_pdm_ring_buffer(substream, dai); |
79 | } |
80 | |
81 | void restore_acp_pdm_params(struct snd_pcm_substream *substream, |
82 | struct acp_dev_data *adata) |
83 | { |
84 | struct snd_soc_dai *dai; |
85 | struct snd_soc_pcm_runtime *soc_runtime; |
86 | u32 ext_int_ctrl; |
87 | |
88 | soc_runtime = snd_soc_substream_to_rtd(substream); |
89 | dai = snd_soc_rtd_to_cpu(soc_runtime, 0); |
90 | /* Programming channel mask and sampling rate */ |
91 | writel(val: adata->ch_mask, addr: adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); |
92 | writel(PDM_DEC_64, addr: adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); |
93 | |
94 | /* Enabling ACP Pdm interuppts */ |
95 | ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); |
96 | ext_int_ctrl |= PDM_DMA_INTR_MASK; |
97 | writel(val: ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); |
98 | set_acp_pdm_clk(substream, dai); |
99 | } |
100 | EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, SND_SOC_ACP_COMMON); |
101 | |
102 | static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, |
103 | struct snd_soc_dai *dai) |
104 | { |
105 | struct device *dev = dai->component->dev; |
106 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
107 | struct acp_resource *rsrc = adata->rsrc; |
108 | struct acp_stream *stream = substream->runtime->private_data; |
109 | u32 reg_dma_size, reg_fifo_size, reg_fifo_addr; |
110 | u32 phy_addr, acp_fifo_addr, ext_int_ctrl; |
111 | unsigned int dir = substream->stream; |
112 | |
113 | switch (dai->driver->id) { |
114 | case I2S_SP_INSTANCE: |
115 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
116 | reg_dma_size = ACP_I2S_TX_DMA_SIZE; |
117 | acp_fifo_addr = rsrc->sram_pte_offset + |
118 | SP_PB_FIFO_ADDR_OFFSET; |
119 | reg_fifo_addr = ACP_I2S_TX_FIFOADDR; |
120 | reg_fifo_size = ACP_I2S_TX_FIFOSIZE; |
121 | phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; |
122 | writel(val: phy_addr, addr: adata->acp_base + ACP_I2S_TX_RINGBUFADDR); |
123 | } else { |
124 | reg_dma_size = ACP_I2S_RX_DMA_SIZE; |
125 | acp_fifo_addr = rsrc->sram_pte_offset + |
126 | SP_CAPT_FIFO_ADDR_OFFSET; |
127 | reg_fifo_addr = ACP_I2S_RX_FIFOADDR; |
128 | reg_fifo_size = ACP_I2S_RX_FIFOSIZE; |
129 | phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; |
130 | writel(val: phy_addr, addr: adata->acp_base + ACP_I2S_RX_RINGBUFADDR); |
131 | } |
132 | break; |
133 | case I2S_BT_INSTANCE: |
134 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
135 | reg_dma_size = ACP_BT_TX_DMA_SIZE; |
136 | acp_fifo_addr = rsrc->sram_pte_offset + |
137 | BT_PB_FIFO_ADDR_OFFSET; |
138 | reg_fifo_addr = ACP_BT_TX_FIFOADDR; |
139 | reg_fifo_size = ACP_BT_TX_FIFOSIZE; |
140 | phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; |
141 | writel(val: phy_addr, addr: adata->acp_base + ACP_BT_TX_RINGBUFADDR); |
142 | } else { |
143 | reg_dma_size = ACP_BT_RX_DMA_SIZE; |
144 | acp_fifo_addr = rsrc->sram_pte_offset + |
145 | BT_CAPT_FIFO_ADDR_OFFSET; |
146 | reg_fifo_addr = ACP_BT_RX_FIFOADDR; |
147 | reg_fifo_size = ACP_BT_RX_FIFOSIZE; |
148 | phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; |
149 | writel(val: phy_addr, addr: adata->acp_base + ACP_BT_RX_RINGBUFADDR); |
150 | } |
151 | break; |
152 | case I2S_HS_INSTANCE: |
153 | if (dir == SNDRV_PCM_STREAM_PLAYBACK) { |
154 | reg_dma_size = ACP_HS_TX_DMA_SIZE; |
155 | acp_fifo_addr = rsrc->sram_pte_offset + |
156 | HS_PB_FIFO_ADDR_OFFSET; |
157 | reg_fifo_addr = ACP_HS_TX_FIFOADDR; |
158 | reg_fifo_size = ACP_HS_TX_FIFOSIZE; |
159 | phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; |
160 | writel(val: phy_addr, addr: adata->acp_base + ACP_HS_TX_RINGBUFADDR); |
161 | } else { |
162 | reg_dma_size = ACP_HS_RX_DMA_SIZE; |
163 | acp_fifo_addr = rsrc->sram_pte_offset + |
164 | HS_CAPT_FIFO_ADDR_OFFSET; |
165 | reg_fifo_addr = ACP_HS_RX_FIFOADDR; |
166 | reg_fifo_size = ACP_HS_RX_FIFOSIZE; |
167 | phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; |
168 | writel(val: phy_addr, addr: adata->acp_base + ACP_HS_RX_RINGBUFADDR); |
169 | } |
170 | break; |
171 | default: |
172 | dev_err(dev, "Invalid dai id %x\n" , dai->driver->id); |
173 | return -EINVAL; |
174 | } |
175 | |
176 | writel(DMA_SIZE, addr: adata->acp_base + reg_dma_size); |
177 | writel(val: acp_fifo_addr, addr: adata->acp_base + reg_fifo_addr); |
178 | writel(FIFO_SIZE, addr: adata->acp_base + reg_fifo_size); |
179 | |
180 | ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); |
181 | ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) | |
182 | BIT(BT_RX_THRESHOLD(rsrc->offset)) | |
183 | BIT(I2S_TX_THRESHOLD(rsrc->offset)) | |
184 | BIT(BT_TX_THRESHOLD(rsrc->offset)) | |
185 | BIT(HS_RX_THRESHOLD(rsrc->offset)) | |
186 | BIT(HS_TX_THRESHOLD(rsrc->offset)); |
187 | |
188 | writel(val: ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); |
189 | return 0; |
190 | } |
191 | |
192 | int restore_acp_i2s_params(struct snd_pcm_substream *substream, |
193 | struct acp_dev_data *adata, |
194 | struct acp_stream *stream) |
195 | { |
196 | struct snd_soc_dai *dai; |
197 | struct snd_soc_pcm_runtime *soc_runtime; |
198 | u32 tdm_fmt, reg_val, fmt_reg, val; |
199 | |
200 | soc_runtime = snd_soc_substream_to_rtd(substream); |
201 | dai = snd_soc_rtd_to_cpu(soc_runtime, 0); |
202 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
203 | tdm_fmt = adata->tdm_tx_fmt[stream->dai_id - 1]; |
204 | switch (stream->dai_id) { |
205 | case I2S_BT_INSTANCE: |
206 | reg_val = ACP_BTTDM_ITER; |
207 | fmt_reg = ACP_BTTDM_TXFRMT; |
208 | break; |
209 | case I2S_SP_INSTANCE: |
210 | reg_val = ACP_I2STDM_ITER; |
211 | fmt_reg = ACP_I2STDM_TXFRMT; |
212 | break; |
213 | case I2S_HS_INSTANCE: |
214 | reg_val = ACP_HSTDM_ITER; |
215 | fmt_reg = ACP_HSTDM_TXFRMT; |
216 | break; |
217 | default: |
218 | pr_err("Invalid dai id %x\n" , stream->dai_id); |
219 | return -EINVAL; |
220 | } |
221 | val = adata->xfer_tx_resolution[stream->dai_id - 1] << 3; |
222 | } else { |
223 | tdm_fmt = adata->tdm_rx_fmt[stream->dai_id - 1]; |
224 | switch (stream->dai_id) { |
225 | case I2S_BT_INSTANCE: |
226 | reg_val = ACP_BTTDM_IRER; |
227 | fmt_reg = ACP_BTTDM_RXFRMT; |
228 | break; |
229 | case I2S_SP_INSTANCE: |
230 | reg_val = ACP_I2STDM_IRER; |
231 | fmt_reg = ACP_I2STDM_RXFRMT; |
232 | break; |
233 | case I2S_HS_INSTANCE: |
234 | reg_val = ACP_HSTDM_IRER; |
235 | fmt_reg = ACP_HSTDM_RXFRMT; |
236 | break; |
237 | default: |
238 | pr_err("Invalid dai id %x\n" , stream->dai_id); |
239 | return -EINVAL; |
240 | } |
241 | val = adata->xfer_rx_resolution[stream->dai_id - 1] << 3; |
242 | } |
243 | writel(val, addr: adata->acp_base + reg_val); |
244 | if (adata->tdm_mode == TDM_ENABLE) { |
245 | writel(val: tdm_fmt, addr: adata->acp_base + fmt_reg); |
246 | val = readl(addr: adata->acp_base + reg_val); |
247 | writel(val: val | 0x2, addr: adata->acp_base + reg_val); |
248 | } |
249 | return set_acp_i2s_dma_fifo(substream, dai); |
250 | } |
251 | EXPORT_SYMBOL_NS_GPL(restore_acp_i2s_params, SND_SOC_ACP_COMMON); |
252 | |
253 | static int acp_power_on(struct acp_chip_info *chip) |
254 | { |
255 | u32 val, acp_pgfsm_stat_reg, acp_pgfsm_ctrl_reg; |
256 | void __iomem *base; |
257 | |
258 | base = chip->base; |
259 | switch (chip->acp_rev) { |
260 | case ACP3X_DEV: |
261 | acp_pgfsm_stat_reg = ACP_PGFSM_STATUS; |
262 | acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL; |
263 | break; |
264 | case ACP6X_DEV: |
265 | acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS; |
266 | acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL; |
267 | break; |
268 | case ACP63_DEV: |
269 | acp_pgfsm_stat_reg = ACP63_PGFSM_STATUS; |
270 | acp_pgfsm_ctrl_reg = ACP63_PGFSM_CONTROL; |
271 | break; |
272 | case ACP70_DEV: |
273 | acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS; |
274 | acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL; |
275 | break; |
276 | default: |
277 | return -EINVAL; |
278 | } |
279 | |
280 | val = readl(addr: base + acp_pgfsm_stat_reg); |
281 | if (val == ACP_POWERED_ON) |
282 | return 0; |
283 | |
284 | if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS) |
285 | writel(ACP_PGFSM_CNTL_POWER_ON_MASK, addr: base + acp_pgfsm_ctrl_reg); |
286 | |
287 | return readl_poll_timeout(base + acp_pgfsm_stat_reg, val, |
288 | !val, DELAY_US, ACP_TIMEOUT); |
289 | } |
290 | |
291 | static int acp_reset(void __iomem *base) |
292 | { |
293 | u32 val; |
294 | int ret; |
295 | |
296 | writel(val: 1, addr: base + ACP_SOFT_RESET); |
297 | ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK, |
298 | DELAY_US, ACP_TIMEOUT); |
299 | if (ret) |
300 | return ret; |
301 | |
302 | writel(val: 0, addr: base + ACP_SOFT_RESET); |
303 | return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); |
304 | } |
305 | |
306 | int acp_init(struct acp_chip_info *chip) |
307 | { |
308 | int ret; |
309 | |
310 | /* power on */ |
311 | ret = acp_power_on(chip); |
312 | if (ret) { |
313 | pr_err("ACP power on failed\n" ); |
314 | return ret; |
315 | } |
316 | writel(val: 0x01, addr: chip->base + ACP_CONTROL); |
317 | |
318 | /* Reset */ |
319 | ret = acp_reset(base: chip->base); |
320 | if (ret) { |
321 | pr_err("ACP reset failed\n" ); |
322 | return ret; |
323 | } |
324 | return 0; |
325 | } |
326 | EXPORT_SYMBOL_NS_GPL(acp_init, SND_SOC_ACP_COMMON); |
327 | |
328 | int acp_deinit(struct acp_chip_info *chip) |
329 | { |
330 | int ret; |
331 | |
332 | /* Reset */ |
333 | ret = acp_reset(base: chip->base); |
334 | if (ret) |
335 | return ret; |
336 | |
337 | if (chip->acp_rev != ACP70_DEV) |
338 | writel(val: 0, addr: chip->base + ACP_CONTROL); |
339 | return 0; |
340 | } |
341 | EXPORT_SYMBOL_NS_GPL(acp_deinit, SND_SOC_ACP_COMMON); |
342 | |
343 | int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) |
344 | { |
345 | pci_write_config_dword(dev, where: 0x60, val: smn_addr); |
346 | pci_write_config_dword(dev, where: 0x64, val: data); |
347 | return 0; |
348 | } |
349 | EXPORT_SYMBOL_NS_GPL(smn_write, SND_SOC_ACP_COMMON); |
350 | |
351 | int smn_read(struct pci_dev *dev, u32 smn_addr) |
352 | { |
353 | u32 data; |
354 | |
355 | pci_write_config_dword(dev, where: 0x60, val: smn_addr); |
356 | pci_read_config_dword(dev, where: 0x64, val: &data); |
357 | return data; |
358 | } |
359 | EXPORT_SYMBOL_NS_GPL(smn_read, SND_SOC_ACP_COMMON); |
360 | |
361 | int check_acp_pdm(struct pci_dev *pci, struct acp_chip_info *chip) |
362 | { |
363 | struct acpi_device *pdm_dev; |
364 | const union acpi_object *obj; |
365 | u32 pdm_addr, val; |
366 | |
367 | val = readl(addr: chip->base + ACP_PIN_CONFIG); |
368 | switch (val) { |
369 | case ACP_CONFIG_4: |
370 | case ACP_CONFIG_5: |
371 | case ACP_CONFIG_6: |
372 | case ACP_CONFIG_7: |
373 | case ACP_CONFIG_8: |
374 | case ACP_CONFIG_10: |
375 | case ACP_CONFIG_11: |
376 | case ACP_CONFIG_12: |
377 | case ACP_CONFIG_13: |
378 | case ACP_CONFIG_14: |
379 | break; |
380 | default: |
381 | return -EINVAL; |
382 | } |
383 | |
384 | switch (chip->acp_rev) { |
385 | case ACP3X_DEV: |
386 | pdm_addr = ACP_RENOIR_PDM_ADDR; |
387 | break; |
388 | case ACP6X_DEV: |
389 | pdm_addr = ACP_REMBRANDT_PDM_ADDR; |
390 | break; |
391 | case ACP63_DEV: |
392 | pdm_addr = ACP63_PDM_ADDR; |
393 | break; |
394 | case ACP70_DEV: |
395 | pdm_addr = ACP70_PDM_ADDR; |
396 | break; |
397 | default: |
398 | return -EINVAL; |
399 | } |
400 | |
401 | pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), address: pdm_addr, check_children: 0); |
402 | if (pdm_dev) { |
403 | if (!acpi_dev_get_property(adev: pdm_dev, name: "acp-audio-device-type" , |
404 | ACPI_TYPE_INTEGER, obj: &obj) && |
405 | obj->integer.value == pdm_addr) |
406 | return 0; |
407 | } |
408 | return -ENODEV; |
409 | } |
410 | EXPORT_SYMBOL_NS_GPL(check_acp_pdm, SND_SOC_ACP_COMMON); |
411 | |
412 | MODULE_LICENSE("Dual BSD/GPL" ); |
413 | |