Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/amd/ps/ps-pdm-dma.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* AMD ALSA SoC common PDM Driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
4
*
5
* Copyright 2022, 2025 Advanced Micro Devices, Inc.
6
*/
7
8
#include <linux/platform_device.h>
9
#include <linux/module.h>
10
#include <linux/bitfield.h>
11
#include <linux/err.h>
12
#include <linux/io.h>
13
#include <sound/pcm_params.h>
14
#include <sound/soc.h>
15
#include <sound/soc-dai.h>
16
#include <linux/pm_runtime.h>
17
18
#include "acp63.h"
19
20
#define DRV_NAME "acp_ps_pdm_dma"
21
22
static int pdm_gain = 3;
23
module_param(pdm_gain, int, 0644);
24
MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
25
26
static const struct snd_pcm_hardware acp63_pdm_hardware_capture = {
27
.info = SNDRV_PCM_INFO_INTERLEAVED |
28
SNDRV_PCM_INFO_BLOCK_TRANSFER |
29
SNDRV_PCM_INFO_MMAP |
30
SNDRV_PCM_INFO_MMAP_VALID |
31
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
32
.formats = SNDRV_PCM_FMTBIT_S32_LE,
33
.channels_min = 2,
34
.channels_max = 2,
35
.rates = SNDRV_PCM_RATE_48000,
36
.rate_min = 48000,
37
.rate_max = 48000,
38
.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
39
.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
40
.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
41
.periods_min = CAPTURE_MIN_NUM_PERIODS,
42
.periods_max = CAPTURE_MAX_NUM_PERIODS,
43
};
44
45
static void acp63_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
46
u32 watermark_size, void __iomem *acp_base)
47
{
48
writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
49
writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
50
writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
51
writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
52
}
53
54
static void acp63_enable_pdm_clock(void __iomem *acp_base)
55
{
56
u32 pdm_clk_enable, pdm_ctrl;
57
58
pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
59
pdm_ctrl = 0x00;
60
61
writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
62
pdm_ctrl = readl(acp_base + ACP_WOV_MISC_CTRL);
63
pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
64
pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
65
writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
66
}
67
68
static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata)
69
{
70
u32 ext_int_ctrl;
71
72
mutex_lock(adata->acp_lock);
73
ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
74
ext_int_ctrl |= PDM_DMA_INTR_MASK;
75
writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
76
mutex_unlock(adata->acp_lock);
77
}
78
79
static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata)
80
{
81
u32 ext_int_ctrl;
82
83
mutex_lock(adata->acp_lock);
84
ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
85
ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
86
writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
87
mutex_unlock(adata->acp_lock);
88
}
89
90
static bool acp63_check_pdm_dma_status(void __iomem *acp_base)
91
{
92
bool pdm_dma_status;
93
u32 pdm_enable, pdm_dma_enable;
94
95
pdm_dma_status = false;
96
pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE);
97
pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
98
if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
99
pdm_dma_status = true;
100
101
return pdm_dma_status;
102
}
103
104
static int acp63_start_pdm_dma(void __iomem *acp_base)
105
{
106
u32 pdm_enable;
107
u32 pdm_dma_enable;
108
int timeout;
109
110
pdm_enable = 0x01;
111
pdm_dma_enable = 0x01;
112
113
acp63_enable_pdm_clock(acp_base);
114
writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
115
writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
116
timeout = 0;
117
while (++timeout < ACP_COUNTER) {
118
pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
119
if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
120
return 0;
121
udelay(DELAY_US);
122
}
123
return -ETIMEDOUT;
124
}
125
126
static int acp63_stop_pdm_dma(void __iomem *acp_base)
127
{
128
u32 pdm_enable, pdm_dma_enable;
129
int timeout;
130
131
pdm_enable = 0x00;
132
pdm_dma_enable = 0x00;
133
134
pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE);
135
pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
136
if (pdm_dma_enable & 0x01) {
137
pdm_dma_enable = 0x02;
138
writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
139
timeout = 0;
140
while (++timeout < ACP_COUNTER) {
141
pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
142
if ((pdm_dma_enable & 0x02) == 0x00)
143
break;
144
udelay(DELAY_US);
145
}
146
if (timeout == ACP_COUNTER)
147
return -ETIMEDOUT;
148
}
149
if (pdm_enable == ACP_PDM_ENABLE) {
150
pdm_enable = ACP_PDM_DISABLE;
151
writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
152
}
153
writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
154
return 0;
155
}
156
157
static void acp63_config_dma(struct pdm_stream_instance *rtd, int direction)
158
{
159
u16 page_idx;
160
u32 low, high, val;
161
dma_addr_t addr;
162
163
addr = rtd->dma_addr;
164
val = PDM_PTE_OFFSET;
165
166
/* Group Enable */
167
writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
168
writel(PAGE_SIZE_4K_ENABLE, rtd->acp63_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
169
for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
170
/* Load the low address of page int ACP SRAM through SRBM */
171
low = lower_32_bits(addr);
172
high = upper_32_bits(addr);
173
174
writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val);
175
high |= BIT(31);
176
writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4);
177
val += 8;
178
addr += PAGE_SIZE;
179
}
180
}
181
182
static int acp63_pdm_dma_open(struct snd_soc_component *component,
183
struct snd_pcm_substream *substream)
184
{
185
struct snd_pcm_runtime *runtime;
186
struct pdm_dev_data *adata;
187
struct pdm_stream_instance *pdm_data;
188
int ret;
189
190
runtime = substream->runtime;
191
adata = dev_get_drvdata(component->dev);
192
pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
193
if (!pdm_data)
194
return -EINVAL;
195
196
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
197
runtime->hw = acp63_pdm_hardware_capture;
198
199
ret = snd_pcm_hw_constraint_integer(runtime,
200
SNDRV_PCM_HW_PARAM_PERIODS);
201
if (ret < 0) {
202
dev_err(component->dev, "set integer constraint failed\n");
203
kfree(pdm_data);
204
return ret;
205
}
206
207
acp63_enable_pdm_interrupts(adata);
208
209
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
210
adata->capture_stream = substream;
211
212
pdm_data->acp63_base = adata->acp63_base;
213
runtime->private_data = pdm_data;
214
return ret;
215
}
216
217
static int acp63_pdm_dma_hw_params(struct snd_soc_component *component,
218
struct snd_pcm_substream *substream,
219
struct snd_pcm_hw_params *params)
220
{
221
struct pdm_stream_instance *rtd;
222
size_t size, period_bytes;
223
224
rtd = substream->runtime->private_data;
225
if (!rtd)
226
return -EINVAL;
227
size = params_buffer_bytes(params);
228
period_bytes = params_period_bytes(params);
229
rtd->dma_addr = substream->runtime->dma_addr;
230
rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
231
acp63_config_dma(rtd, substream->stream);
232
acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
233
period_bytes, rtd->acp63_base);
234
return 0;
235
}
236
237
static u64 acp63_pdm_get_byte_count(struct pdm_stream_instance *rtd,
238
int direction)
239
{
240
u32 high, low;
241
u64 byte_count;
242
243
high = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
244
byte_count = high;
245
low = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
246
byte_count = (byte_count << 32) | low;
247
return byte_count;
248
}
249
250
static snd_pcm_uframes_t acp63_pdm_dma_pointer(struct snd_soc_component *comp,
251
struct snd_pcm_substream *stream)
252
{
253
struct pdm_stream_instance *rtd;
254
u32 pos, buffersize;
255
u64 bytescount;
256
257
rtd = stream->runtime->private_data;
258
buffersize = frames_to_bytes(stream->runtime,
259
stream->runtime->buffer_size);
260
bytescount = acp63_pdm_get_byte_count(rtd, stream->stream);
261
if (bytescount > rtd->bytescount)
262
bytescount -= rtd->bytescount;
263
pos = do_div(bytescount, buffersize);
264
return bytes_to_frames(stream->runtime, pos);
265
}
266
267
static int acp63_pdm_dma_new(struct snd_soc_component *component,
268
struct snd_soc_pcm_runtime *rtd)
269
{
270
struct device *parent = component->dev->parent;
271
272
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
273
parent, MIN_BUFFER, MAX_BUFFER);
274
return 0;
275
}
276
277
static int acp63_pdm_dma_close(struct snd_soc_component *component,
278
struct snd_pcm_substream *substream)
279
{
280
struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
281
struct snd_pcm_runtime *runtime = substream->runtime;
282
283
acp63_disable_pdm_interrupts(adata);
284
adata->capture_stream = NULL;
285
kfree(runtime->private_data);
286
return 0;
287
}
288
289
static int acp63_pdm_dai_trigger(struct snd_pcm_substream *substream,
290
int cmd, struct snd_soc_dai *dai)
291
{
292
struct pdm_stream_instance *rtd;
293
int ret;
294
bool pdm_status;
295
unsigned int ch_mask;
296
297
rtd = substream->runtime->private_data;
298
ret = 0;
299
switch (substream->runtime->channels) {
300
case TWO_CH:
301
ch_mask = 0x00;
302
break;
303
default:
304
return -EINVAL;
305
}
306
switch (cmd) {
307
case SNDRV_PCM_TRIGGER_START:
308
case SNDRV_PCM_TRIGGER_RESUME:
309
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
310
writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS);
311
writel(PDM_DECIMATION_FACTOR, rtd->acp63_base + ACP_WOV_PDM_DECIMATION_FACTOR);
312
rtd->bytescount = acp63_pdm_get_byte_count(rtd, substream->stream);
313
pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
314
if (!pdm_status)
315
ret = acp63_start_pdm_dma(rtd->acp63_base);
316
break;
317
case SNDRV_PCM_TRIGGER_STOP:
318
case SNDRV_PCM_TRIGGER_SUSPEND:
319
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
320
pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
321
if (pdm_status)
322
ret = acp63_stop_pdm_dma(rtd->acp63_base);
323
break;
324
default:
325
ret = -EINVAL;
326
break;
327
}
328
return ret;
329
}
330
331
static const struct snd_soc_dai_ops acp63_pdm_dai_ops = {
332
.trigger = acp63_pdm_dai_trigger,
333
};
334
335
static struct snd_soc_dai_driver acp63_pdm_dai_driver = {
336
.name = "acp_ps_pdm_dma.0",
337
.capture = {
338
.rates = SNDRV_PCM_RATE_48000,
339
.formats = SNDRV_PCM_FMTBIT_S32_LE,
340
.channels_min = 2,
341
.channels_max = 2,
342
.rate_min = 48000,
343
.rate_max = 48000,
344
},
345
.ops = &acp63_pdm_dai_ops,
346
};
347
348
static const struct snd_soc_component_driver acp63_pdm_component = {
349
.name = DRV_NAME,
350
.open = acp63_pdm_dma_open,
351
.close = acp63_pdm_dma_close,
352
.hw_params = acp63_pdm_dma_hw_params,
353
.pointer = acp63_pdm_dma_pointer,
354
.pcm_construct = acp63_pdm_dma_new,
355
};
356
357
static int acp63_pdm_audio_probe(struct platform_device *pdev)
358
{
359
struct resource *res;
360
struct pdm_dev_data *adata;
361
struct acp63_dev_data *acp_data;
362
struct device *parent;
363
int status;
364
365
parent = pdev->dev.parent;
366
acp_data = dev_get_drvdata(parent);
367
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
368
if (!res) {
369
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
370
return -ENODEV;
371
}
372
373
adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
374
if (!adata)
375
return -ENOMEM;
376
377
adata->acp63_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
378
if (!adata->acp63_base)
379
return -ENOMEM;
380
381
adata->capture_stream = NULL;
382
adata->acp_lock = &acp_data->acp_lock;
383
dev_set_drvdata(&pdev->dev, adata);
384
status = devm_snd_soc_register_component(&pdev->dev,
385
&acp63_pdm_component,
386
&acp63_pdm_dai_driver, 1);
387
if (status) {
388
dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
389
390
return -ENODEV;
391
}
392
pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
393
pm_runtime_use_autosuspend(&pdev->dev);
394
pm_runtime_mark_last_busy(&pdev->dev);
395
pm_runtime_set_active(&pdev->dev);
396
pm_runtime_enable(&pdev->dev);
397
return 0;
398
}
399
400
static void acp63_pdm_audio_remove(struct platform_device *pdev)
401
{
402
pm_runtime_disable(&pdev->dev);
403
}
404
405
static int acp63_pdm_resume(struct device *dev)
406
{
407
struct pdm_dev_data *adata;
408
struct snd_pcm_runtime *runtime;
409
struct pdm_stream_instance *rtd;
410
u32 period_bytes, buffer_len;
411
412
adata = dev_get_drvdata(dev);
413
if (adata->capture_stream && adata->capture_stream->runtime) {
414
runtime = adata->capture_stream->runtime;
415
rtd = runtime->private_data;
416
period_bytes = frames_to_bytes(runtime, runtime->period_size);
417
buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
418
acp63_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
419
acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
420
period_bytes, adata->acp63_base);
421
}
422
acp63_enable_pdm_interrupts(adata);
423
return 0;
424
}
425
426
static int acp63_pdm_suspend(struct device *dev)
427
{
428
struct pdm_dev_data *adata;
429
430
adata = dev_get_drvdata(dev);
431
acp63_disable_pdm_interrupts(adata);
432
return 0;
433
}
434
435
static int acp63_pdm_runtime_resume(struct device *dev)
436
{
437
struct pdm_dev_data *adata;
438
439
adata = dev_get_drvdata(dev);
440
acp63_enable_pdm_interrupts(adata);
441
return 0;
442
}
443
444
static const struct dev_pm_ops acp63_pdm_pm_ops = {
445
RUNTIME_PM_OPS(acp63_pdm_suspend, acp63_pdm_runtime_resume, NULL)
446
SYSTEM_SLEEP_PM_OPS(acp63_pdm_suspend, acp63_pdm_resume)
447
};
448
449
static struct platform_driver acp63_pdm_dma_driver = {
450
.probe = acp63_pdm_audio_probe,
451
.remove = acp63_pdm_audio_remove,
452
.driver = {
453
.name = "acp_ps_pdm_dma",
454
.pm = pm_ptr(&acp63_pdm_pm_ops),
455
},
456
};
457
458
module_platform_driver(acp63_pdm_dma_driver);
459
460
MODULE_AUTHOR("[email protected]");
461
MODULE_DESCRIPTION("AMD common PDM Driver for ACP6.3, ACP7,0 & ACP7.1 platforms");
462
MODULE_LICENSE("GPL v2");
463
MODULE_ALIAS("platform:" DRV_NAME);
464
465