1 // SPDX-License-Identifier: GPL-2.0+
3 // AMD ALSA SoC PCM Driver
5 // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
7 #include <linux/platform_device.h>
8 #include <linux/module.h>
11 #include <linux/pm_runtime.h>
12 #include <sound/pcm.h>
13 #include <sound/pcm_params.h>
14 #include <sound/soc.h>
15 #include <sound/soc-dai.h>
19 #define DRV_NAME "acp5x_i2s_dma"
21 static const struct snd_pcm_hardware acp5x_pcm_hardware_playback
= {
22 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
23 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
24 SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
|
25 SNDRV_PCM_INFO_PAUSE
| SNDRV_PCM_INFO_RESUME
,
26 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S8
|
27 SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S32_LE
,
30 .rates
= SNDRV_PCM_RATE_8000_96000
,
33 .buffer_bytes_max
= PLAYBACK_MAX_NUM_PERIODS
* PLAYBACK_MAX_PERIOD_SIZE
,
34 .period_bytes_min
= PLAYBACK_MIN_PERIOD_SIZE
,
35 .period_bytes_max
= PLAYBACK_MAX_PERIOD_SIZE
,
36 .periods_min
= PLAYBACK_MIN_NUM_PERIODS
,
37 .periods_max
= PLAYBACK_MAX_NUM_PERIODS
,
40 static const struct snd_pcm_hardware acp5x_pcm_hardware_capture
= {
41 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
42 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
43 SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
|
44 SNDRV_PCM_INFO_PAUSE
| SNDRV_PCM_INFO_RESUME
,
45 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S8
|
46 SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S32_LE
,
49 .rates
= SNDRV_PCM_RATE_8000_96000
,
52 .buffer_bytes_max
= CAPTURE_MAX_NUM_PERIODS
* CAPTURE_MAX_PERIOD_SIZE
,
53 .period_bytes_min
= CAPTURE_MIN_PERIOD_SIZE
,
54 .period_bytes_max
= CAPTURE_MAX_PERIOD_SIZE
,
55 .periods_min
= CAPTURE_MIN_NUM_PERIODS
,
56 .periods_max
= CAPTURE_MAX_NUM_PERIODS
,
59 static irqreturn_t
i2s_irq_handler(int irq
, void *dev_id
)
61 struct i2s_dev_data
*vg_i2s_data
;
70 val
= acp_readl(vg_i2s_data
->acp5x_base
+ ACP_EXTERNAL_INTR_STAT
);
71 if ((val
& BIT(HS_TX_THRESHOLD
)) && vg_i2s_data
->play_stream
) {
72 acp_writel(BIT(HS_TX_THRESHOLD
), vg_i2s_data
->acp5x_base
+
73 ACP_EXTERNAL_INTR_STAT
);
74 snd_pcm_period_elapsed(vg_i2s_data
->play_stream
);
77 if ((val
& BIT(I2S_TX_THRESHOLD
)) && vg_i2s_data
->i2ssp_play_stream
) {
78 acp_writel(BIT(I2S_TX_THRESHOLD
),
79 vg_i2s_data
->acp5x_base
+ ACP_EXTERNAL_INTR_STAT
);
80 snd_pcm_period_elapsed(vg_i2s_data
->i2ssp_play_stream
);
84 if ((val
& BIT(HS_RX_THRESHOLD
)) && vg_i2s_data
->capture_stream
) {
85 acp_writel(BIT(HS_RX_THRESHOLD
), vg_i2s_data
->acp5x_base
+
86 ACP_EXTERNAL_INTR_STAT
);
87 snd_pcm_period_elapsed(vg_i2s_data
->capture_stream
);
90 if ((val
& BIT(I2S_RX_THRESHOLD
)) && vg_i2s_data
->i2ssp_capture_stream
) {
91 acp_writel(BIT(I2S_RX_THRESHOLD
),
92 vg_i2s_data
->acp5x_base
+ ACP_EXTERNAL_INTR_STAT
);
93 snd_pcm_period_elapsed(vg_i2s_data
->i2ssp_capture_stream
);
103 static void config_acp5x_dma(struct i2s_stream_instance
*rtd
, int direction
)
106 u32 low
, high
, val
, acp_fifo_addr
, reg_fifo_addr
;
107 u32 reg_dma_size
, reg_fifo_size
;
110 addr
= rtd
->dma_addr
;
111 if (direction
== SNDRV_PCM_STREAM_PLAYBACK
) {
112 switch (rtd
->i2s_instance
) {
113 case I2S_HS_INSTANCE
:
114 val
= ACP_SRAM_HS_PB_PTE_OFFSET
;
116 case I2S_SP_INSTANCE
:
118 val
= ACP_SRAM_SP_PB_PTE_OFFSET
;
121 switch (rtd
->i2s_instance
) {
122 case I2S_HS_INSTANCE
:
123 val
= ACP_SRAM_HS_CP_PTE_OFFSET
;
125 case I2S_SP_INSTANCE
:
127 val
= ACP_SRAM_SP_CP_PTE_OFFSET
;
131 acp_writel(ACP_SRAM_PTE_OFFSET
| BIT(31), rtd
->acp5x_base
+
132 ACPAXI2AXI_ATU_BASE_ADDR_GRP_1
);
133 acp_writel(PAGE_SIZE_4K_ENABLE
, rtd
->acp5x_base
+
134 ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1
);
136 for (page_idx
= 0; page_idx
< rtd
->num_pages
; page_idx
++) {
137 /* Load the low address of page int ACP SRAM through SRBM */
138 low
= lower_32_bits(addr
);
139 high
= upper_32_bits(addr
);
141 acp_writel(low
, rtd
->acp5x_base
+ ACP_SCRATCH_REG_0
+ val
);
143 acp_writel(high
, rtd
->acp5x_base
+ ACP_SCRATCH_REG_0
+ val
+ 4);
144 /* Move to next physically contiguous page */
149 if (direction
== SNDRV_PCM_STREAM_PLAYBACK
) {
150 switch (rtd
->i2s_instance
) {
151 case I2S_HS_INSTANCE
:
152 reg_dma_size
= ACP_HS_TX_DMA_SIZE
;
153 acp_fifo_addr
= ACP_SRAM_PTE_OFFSET
+
154 HS_PB_FIFO_ADDR_OFFSET
;
155 reg_fifo_addr
= ACP_HS_TX_FIFOADDR
;
156 reg_fifo_size
= ACP_HS_TX_FIFOSIZE
;
157 acp_writel(I2S_HS_TX_MEM_WINDOW_START
,
158 rtd
->acp5x_base
+ ACP_HS_TX_RINGBUFADDR
);
161 case I2S_SP_INSTANCE
:
163 reg_dma_size
= ACP_I2S_TX_DMA_SIZE
;
164 acp_fifo_addr
= ACP_SRAM_PTE_OFFSET
+
165 SP_PB_FIFO_ADDR_OFFSET
;
166 reg_fifo_addr
= ACP_I2S_TX_FIFOADDR
;
167 reg_fifo_size
= ACP_I2S_TX_FIFOSIZE
;
168 acp_writel(I2S_SP_TX_MEM_WINDOW_START
,
169 rtd
->acp5x_base
+ ACP_I2S_TX_RINGBUFADDR
);
172 switch (rtd
->i2s_instance
) {
173 case I2S_HS_INSTANCE
:
174 reg_dma_size
= ACP_HS_RX_DMA_SIZE
;
175 acp_fifo_addr
= ACP_SRAM_PTE_OFFSET
+
176 HS_CAPT_FIFO_ADDR_OFFSET
;
177 reg_fifo_addr
= ACP_HS_RX_FIFOADDR
;
178 reg_fifo_size
= ACP_HS_RX_FIFOSIZE
;
179 acp_writel(I2S_HS_RX_MEM_WINDOW_START
,
180 rtd
->acp5x_base
+ ACP_HS_RX_RINGBUFADDR
);
183 case I2S_SP_INSTANCE
:
185 reg_dma_size
= ACP_I2S_RX_DMA_SIZE
;
186 acp_fifo_addr
= ACP_SRAM_PTE_OFFSET
+
187 SP_CAPT_FIFO_ADDR_OFFSET
;
188 reg_fifo_addr
= ACP_I2S_RX_FIFOADDR
;
189 reg_fifo_size
= ACP_I2S_RX_FIFOSIZE
;
190 acp_writel(I2S_SP_RX_MEM_WINDOW_START
,
191 rtd
->acp5x_base
+ ACP_I2S_RX_RINGBUFADDR
);
194 acp_writel(DMA_SIZE
, rtd
->acp5x_base
+ reg_dma_size
);
195 acp_writel(acp_fifo_addr
, rtd
->acp5x_base
+ reg_fifo_addr
);
196 acp_writel(FIFO_SIZE
, rtd
->acp5x_base
+ reg_fifo_size
);
197 acp_writel(BIT(I2S_RX_THRESHOLD
) | BIT(HS_RX_THRESHOLD
)
198 | BIT(I2S_TX_THRESHOLD
) | BIT(HS_TX_THRESHOLD
),
199 rtd
->acp5x_base
+ ACP_EXTERNAL_INTR_CNTL
);
202 static int acp5x_dma_open(struct snd_soc_component
*component
,
203 struct snd_pcm_substream
*substream
)
205 struct snd_pcm_runtime
*runtime
;
206 struct snd_soc_pcm_runtime
*prtd
;
207 struct i2s_dev_data
*adata
;
208 struct i2s_stream_instance
*i2s_data
;
211 runtime
= substream
->runtime
;
212 prtd
= snd_soc_substream_to_rtd(substream
);
213 component
= snd_soc_rtdcom_lookup(prtd
, DRV_NAME
);
214 adata
= dev_get_drvdata(component
->dev
);
216 i2s_data
= kzalloc(sizeof(*i2s_data
), GFP_KERNEL
);
220 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
221 runtime
->hw
= acp5x_pcm_hardware_playback
;
223 runtime
->hw
= acp5x_pcm_hardware_capture
;
225 ret
= snd_pcm_hw_constraint_integer(runtime
,
226 SNDRV_PCM_HW_PARAM_PERIODS
);
228 dev_err(component
->dev
, "set integer constraint failed\n");
232 i2s_data
->acp5x_base
= adata
->acp5x_base
;
233 runtime
->private_data
= i2s_data
;
237 static int acp5x_dma_hw_params(struct snd_soc_component
*component
,
238 struct snd_pcm_substream
*substream
,
239 struct snd_pcm_hw_params
*params
)
241 struct i2s_stream_instance
*rtd
;
242 struct snd_soc_pcm_runtime
*prtd
;
243 struct snd_soc_card
*card
;
244 struct acp5x_platform_info
*pinfo
;
245 struct i2s_dev_data
*adata
;
248 prtd
= snd_soc_substream_to_rtd(substream
);
250 pinfo
= snd_soc_card_get_drvdata(card
);
251 adata
= dev_get_drvdata(component
->dev
);
252 rtd
= substream
->runtime
->private_data
;
258 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
259 rtd
->i2s_instance
= pinfo
->play_i2s_instance
;
260 switch (rtd
->i2s_instance
) {
261 case I2S_HS_INSTANCE
:
262 adata
->play_stream
= substream
;
264 case I2S_SP_INSTANCE
:
266 adata
->i2ssp_play_stream
= substream
;
269 rtd
->i2s_instance
= pinfo
->cap_i2s_instance
;
270 switch (rtd
->i2s_instance
) {
271 case I2S_HS_INSTANCE
:
272 adata
->capture_stream
= substream
;
274 case I2S_SP_INSTANCE
:
276 adata
->i2ssp_capture_stream
= substream
;
280 dev_err(component
->dev
, "pinfo failed\n");
283 size
= params_buffer_bytes(params
);
284 rtd
->dma_addr
= substream
->runtime
->dma_addr
;
285 rtd
->num_pages
= (PAGE_ALIGN(size
) >> PAGE_SHIFT
);
286 config_acp5x_dma(rtd
, substream
->stream
);
290 static snd_pcm_uframes_t
acp5x_dma_pointer(struct snd_soc_component
*component
,
291 struct snd_pcm_substream
*substream
)
293 struct i2s_stream_instance
*rtd
;
298 rtd
= substream
->runtime
->private_data
;
299 buffersize
= frames_to_bytes(substream
->runtime
,
300 substream
->runtime
->buffer_size
);
301 bytescount
= acp_get_byte_count(rtd
, substream
->stream
);
302 if (bytescount
> rtd
->bytescount
)
303 bytescount
-= rtd
->bytescount
;
304 pos
= do_div(bytescount
, buffersize
);
305 return bytes_to_frames(substream
->runtime
, pos
);
308 static int acp5x_dma_new(struct snd_soc_component
*component
,
309 struct snd_soc_pcm_runtime
*rtd
)
311 struct device
*parent
= component
->dev
->parent
;
313 snd_pcm_set_managed_buffer_all(rtd
->pcm
, SNDRV_DMA_TYPE_DEV
,
314 parent
, MIN_BUFFER
, MAX_BUFFER
);
318 static int acp5x_dma_close(struct snd_soc_component
*component
,
319 struct snd_pcm_substream
*substream
)
321 struct snd_soc_pcm_runtime
*prtd
;
322 struct i2s_dev_data
*adata
;
323 struct i2s_stream_instance
*ins
;
325 prtd
= snd_soc_substream_to_rtd(substream
);
326 component
= snd_soc_rtdcom_lookup(prtd
, DRV_NAME
);
327 adata
= dev_get_drvdata(component
->dev
);
328 ins
= substream
->runtime
->private_data
;
331 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
332 switch (ins
->i2s_instance
) {
333 case I2S_HS_INSTANCE
:
334 adata
->play_stream
= NULL
;
336 case I2S_SP_INSTANCE
:
338 adata
->i2ssp_play_stream
= NULL
;
341 switch (ins
->i2s_instance
) {
342 case I2S_HS_INSTANCE
:
343 adata
->capture_stream
= NULL
;
345 case I2S_SP_INSTANCE
:
347 adata
->i2ssp_capture_stream
= NULL
;
354 static const struct snd_soc_component_driver acp5x_i2s_component
= {
356 .open
= acp5x_dma_open
,
357 .close
= acp5x_dma_close
,
358 .hw_params
= acp5x_dma_hw_params
,
359 .pointer
= acp5x_dma_pointer
,
360 .pcm_construct
= acp5x_dma_new
,
363 static int acp5x_audio_probe(struct platform_device
*pdev
)
365 struct resource
*res
;
366 struct i2s_dev_data
*adata
;
367 unsigned int irqflags
;
370 if (!pdev
->dev
.platform_data
) {
371 dev_err(&pdev
->dev
, "platform_data not retrieved\n");
374 irqflags
= *((unsigned int *)(pdev
->dev
.platform_data
));
376 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
378 dev_err(&pdev
->dev
, "IORESOURCE_MEM FAILED\n");
382 adata
= devm_kzalloc(&pdev
->dev
, sizeof(*adata
), GFP_KERNEL
);
386 adata
->acp5x_base
= devm_ioremap(&pdev
->dev
, res
->start
,
388 if (!adata
->acp5x_base
)
391 status
= platform_get_irq(pdev
, 0);
394 adata
->i2s_irq
= status
;
396 dev_set_drvdata(&pdev
->dev
, adata
);
397 status
= devm_snd_soc_register_component(&pdev
->dev
,
398 &acp5x_i2s_component
,
401 dev_err(&pdev
->dev
, "Fail to register acp i2s component\n");
404 status
= devm_request_irq(&pdev
->dev
, adata
->i2s_irq
, i2s_irq_handler
,
405 irqflags
, "ACP5x_I2S_IRQ", adata
);
407 dev_err(&pdev
->dev
, "ACP5x I2S IRQ request failed\n");
410 pm_runtime_set_autosuspend_delay(&pdev
->dev
, 2000);
411 pm_runtime_use_autosuspend(&pdev
->dev
);
412 pm_runtime_mark_last_busy(&pdev
->dev
);
413 pm_runtime_set_active(&pdev
->dev
);
414 pm_runtime_enable(&pdev
->dev
);
418 static void acp5x_audio_remove(struct platform_device
*pdev
)
420 pm_runtime_disable(&pdev
->dev
);
423 static int __maybe_unused
acp5x_pcm_resume(struct device
*dev
)
425 struct i2s_dev_data
*adata
;
426 struct i2s_stream_instance
*rtd
;
429 adata
= dev_get_drvdata(dev
);
431 if (adata
->play_stream
&& adata
->play_stream
->runtime
) {
432 rtd
= adata
->play_stream
->runtime
->private_data
;
433 config_acp5x_dma(rtd
, SNDRV_PCM_STREAM_PLAYBACK
);
434 acp_writel((rtd
->xfer_resolution
<< 3), rtd
->acp5x_base
+ ACP_HSTDM_ITER
);
435 if (adata
->tdm_mode
== TDM_ENABLE
) {
436 acp_writel(adata
->tdm_fmt
, adata
->acp5x_base
+ ACP_HSTDM_TXFRMT
);
437 val
= acp_readl(adata
->acp5x_base
+ ACP_HSTDM_ITER
);
438 acp_writel(val
| 0x2, adata
->acp5x_base
+ ACP_HSTDM_ITER
);
441 if (adata
->i2ssp_play_stream
&& adata
->i2ssp_play_stream
->runtime
) {
442 rtd
= adata
->i2ssp_play_stream
->runtime
->private_data
;
443 config_acp5x_dma(rtd
, SNDRV_PCM_STREAM_PLAYBACK
);
444 acp_writel((rtd
->xfer_resolution
<< 3), rtd
->acp5x_base
+ ACP_I2STDM_ITER
);
445 if (adata
->tdm_mode
== TDM_ENABLE
) {
446 acp_writel(adata
->tdm_fmt
, adata
->acp5x_base
+ ACP_I2STDM_TXFRMT
);
447 val
= acp_readl(adata
->acp5x_base
+ ACP_I2STDM_ITER
);
448 acp_writel(val
| 0x2, adata
->acp5x_base
+ ACP_I2STDM_ITER
);
452 if (adata
->capture_stream
&& adata
->capture_stream
->runtime
) {
453 rtd
= adata
->capture_stream
->runtime
->private_data
;
454 config_acp5x_dma(rtd
, SNDRV_PCM_STREAM_CAPTURE
);
455 acp_writel((rtd
->xfer_resolution
<< 3), rtd
->acp5x_base
+ ACP_HSTDM_IRER
);
456 if (adata
->tdm_mode
== TDM_ENABLE
) {
457 acp_writel(adata
->tdm_fmt
, adata
->acp5x_base
+ ACP_HSTDM_RXFRMT
);
458 val
= acp_readl(adata
->acp5x_base
+ ACP_HSTDM_IRER
);
459 acp_writel(val
| 0x2, adata
->acp5x_base
+ ACP_HSTDM_IRER
);
462 if (adata
->i2ssp_capture_stream
&& adata
->i2ssp_capture_stream
->runtime
) {
463 rtd
= adata
->i2ssp_capture_stream
->runtime
->private_data
;
464 config_acp5x_dma(rtd
, SNDRV_PCM_STREAM_CAPTURE
);
465 acp_writel((rtd
->xfer_resolution
<< 3), rtd
->acp5x_base
+ ACP_I2STDM_IRER
);
466 if (adata
->tdm_mode
== TDM_ENABLE
) {
467 acp_writel(adata
->tdm_fmt
, adata
->acp5x_base
+ ACP_I2STDM_RXFRMT
);
468 val
= acp_readl(adata
->acp5x_base
+ ACP_I2STDM_IRER
);
469 acp_writel(val
| 0x2, adata
->acp5x_base
+ ACP_I2STDM_IRER
);
472 acp_writel(1, adata
->acp5x_base
+ ACP_EXTERNAL_INTR_ENB
);
476 static int __maybe_unused
acp5x_pcm_suspend(struct device
*dev
)
478 struct i2s_dev_data
*adata
;
480 adata
= dev_get_drvdata(dev
);
481 acp_writel(0, adata
->acp5x_base
+ ACP_EXTERNAL_INTR_ENB
);
485 static int __maybe_unused
acp5x_pcm_runtime_resume(struct device
*dev
)
487 struct i2s_dev_data
*adata
;
489 adata
= dev_get_drvdata(dev
);
490 acp_writel(1, adata
->acp5x_base
+ ACP_EXTERNAL_INTR_ENB
);
494 static const struct dev_pm_ops acp5x_pm_ops
= {
495 SET_RUNTIME_PM_OPS(acp5x_pcm_suspend
,
496 acp5x_pcm_runtime_resume
, NULL
)
497 SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend
, acp5x_pcm_resume
)
500 static struct platform_driver acp5x_dma_driver
= {
501 .probe
= acp5x_audio_probe
,
502 .remove
= acp5x_audio_remove
,
504 .name
= "acp5x_i2s_dma",
509 module_platform_driver(acp5x_dma_driver
);
511 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
512 MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");
513 MODULE_LICENSE("GPL v2");
514 MODULE_ALIAS("platform:" DRV_NAME
);