Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/amd/vangogh/acp5x-pcm-dma.c
26490 views
1
// SPDX-License-Identifier: GPL-2.0+
2
//
3
// AMD ALSA SoC PCM Driver
4
//
5
// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
6
7
#include <linux/platform_device.h>
8
#include <linux/module.h>
9
#include <linux/err.h>
10
#include <linux/io.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>
16
17
#include "acp5x.h"
18
19
#define DRV_NAME "acp5x_i2s_dma"
20
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,
28
.channels_min = 2,
29
.channels_max = 2,
30
.rates = SNDRV_PCM_RATE_8000_96000,
31
.rate_min = 8000,
32
.rate_max = 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,
38
};
39
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,
47
.channels_min = 2,
48
.channels_max = 2,
49
.rates = SNDRV_PCM_RATE_8000_96000,
50
.rate_min = 8000,
51
.rate_max = 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,
57
};
58
59
static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
60
{
61
struct i2s_dev_data *vg_i2s_data;
62
u16 irq_flag;
63
u32 val;
64
65
vg_i2s_data = dev_id;
66
if (!vg_i2s_data)
67
return IRQ_NONE;
68
69
irq_flag = 0;
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);
75
irq_flag = 1;
76
}
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);
81
irq_flag = 1;
82
}
83
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);
88
irq_flag = 1;
89
}
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);
94
irq_flag = 1;
95
}
96
97
if (irq_flag)
98
return IRQ_HANDLED;
99
else
100
return IRQ_NONE;
101
}
102
103
static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction)
104
{
105
u16 page_idx;
106
u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
107
u32 reg_dma_size, reg_fifo_size;
108
dma_addr_t addr;
109
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;
115
break;
116
case I2S_SP_INSTANCE:
117
default:
118
val = ACP_SRAM_SP_PB_PTE_OFFSET;
119
}
120
} else {
121
switch (rtd->i2s_instance) {
122
case I2S_HS_INSTANCE:
123
val = ACP_SRAM_HS_CP_PTE_OFFSET;
124
break;
125
case I2S_SP_INSTANCE:
126
default:
127
val = ACP_SRAM_SP_CP_PTE_OFFSET;
128
}
129
}
130
/* Group Enable */
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);
135
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);
140
141
acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val);
142
high |= BIT(31);
143
acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4);
144
/* Move to next physically contiguous page */
145
val += 8;
146
addr += PAGE_SIZE;
147
}
148
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);
159
break;
160
161
case I2S_SP_INSTANCE:
162
default:
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);
170
}
171
} else {
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);
181
break;
182
183
case I2S_SP_INSTANCE:
184
default:
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);
192
}
193
}
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);
200
}
201
202
static int acp5x_dma_open(struct snd_soc_component *component,
203
struct snd_pcm_substream *substream)
204
{
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;
209
int ret;
210
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);
215
216
i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
217
if (!i2s_data)
218
return -ENOMEM;
219
220
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
221
runtime->hw = acp5x_pcm_hardware_playback;
222
else
223
runtime->hw = acp5x_pcm_hardware_capture;
224
225
ret = snd_pcm_hw_constraint_integer(runtime,
226
SNDRV_PCM_HW_PARAM_PERIODS);
227
if (ret < 0) {
228
dev_err(component->dev, "set integer constraint failed\n");
229
kfree(i2s_data);
230
return ret;
231
}
232
i2s_data->acp5x_base = adata->acp5x_base;
233
runtime->private_data = i2s_data;
234
return ret;
235
}
236
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)
240
{
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;
246
u64 size;
247
248
prtd = snd_soc_substream_to_rtd(substream);
249
card = prtd->card;
250
pinfo = snd_soc_card_get_drvdata(card);
251
adata = dev_get_drvdata(component->dev);
252
rtd = substream->runtime->private_data;
253
254
if (!rtd)
255
return -EINVAL;
256
257
if (pinfo) {
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;
263
break;
264
case I2S_SP_INSTANCE:
265
default:
266
adata->i2ssp_play_stream = substream;
267
}
268
} else {
269
rtd->i2s_instance = pinfo->cap_i2s_instance;
270
switch (rtd->i2s_instance) {
271
case I2S_HS_INSTANCE:
272
adata->capture_stream = substream;
273
break;
274
case I2S_SP_INSTANCE:
275
default:
276
adata->i2ssp_capture_stream = substream;
277
}
278
}
279
} else {
280
dev_err(component->dev, "pinfo failed\n");
281
return -EINVAL;
282
}
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);
287
return 0;
288
}
289
290
static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component,
291
struct snd_pcm_substream *substream)
292
{
293
struct i2s_stream_instance *rtd;
294
u32 pos;
295
u32 buffersize;
296
u64 bytescount;
297
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);
306
}
307
308
static int acp5x_dma_new(struct snd_soc_component *component,
309
struct snd_soc_pcm_runtime *rtd)
310
{
311
struct device *parent = component->dev->parent;
312
313
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
314
parent, MIN_BUFFER, MAX_BUFFER);
315
return 0;
316
}
317
318
static int acp5x_dma_close(struct snd_soc_component *component,
319
struct snd_pcm_substream *substream)
320
{
321
struct snd_soc_pcm_runtime *prtd;
322
struct i2s_dev_data *adata;
323
struct i2s_stream_instance *ins;
324
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;
329
if (!ins)
330
return -EINVAL;
331
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
332
switch (ins->i2s_instance) {
333
case I2S_HS_INSTANCE:
334
adata->play_stream = NULL;
335
break;
336
case I2S_SP_INSTANCE:
337
default:
338
adata->i2ssp_play_stream = NULL;
339
}
340
} else {
341
switch (ins->i2s_instance) {
342
case I2S_HS_INSTANCE:
343
adata->capture_stream = NULL;
344
break;
345
case I2S_SP_INSTANCE:
346
default:
347
adata->i2ssp_capture_stream = NULL;
348
}
349
}
350
kfree(ins);
351
return 0;
352
}
353
354
static const struct snd_soc_component_driver acp5x_i2s_component = {
355
.name = DRV_NAME,
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,
361
};
362
363
static int acp5x_audio_probe(struct platform_device *pdev)
364
{
365
struct resource *res;
366
struct i2s_dev_data *adata;
367
unsigned int irqflags;
368
int status;
369
370
if (!pdev->dev.platform_data) {
371
dev_err(&pdev->dev, "platform_data not retrieved\n");
372
return -ENODEV;
373
}
374
irqflags = *((unsigned int *)(pdev->dev.platform_data));
375
376
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
377
if (!res) {
378
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
379
return -ENODEV;
380
}
381
382
adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
383
if (!adata)
384
return -ENOMEM;
385
386
adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
387
resource_size(res));
388
if (!adata->acp5x_base)
389
return -ENOMEM;
390
391
status = platform_get_irq(pdev, 0);
392
if (status < 0)
393
return status;
394
adata->i2s_irq = status;
395
396
dev_set_drvdata(&pdev->dev, adata);
397
status = devm_snd_soc_register_component(&pdev->dev,
398
&acp5x_i2s_component,
399
NULL, 0);
400
if (status) {
401
dev_err(&pdev->dev, "Fail to register acp i2s component\n");
402
return status;
403
}
404
status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
405
irqflags, "ACP5x_I2S_IRQ", adata);
406
if (status) {
407
dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n");
408
return status;
409
}
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);
415
return 0;
416
}
417
418
static void acp5x_audio_remove(struct platform_device *pdev)
419
{
420
pm_runtime_disable(&pdev->dev);
421
}
422
423
static int acp5x_pcm_resume(struct device *dev)
424
{
425
struct i2s_dev_data *adata;
426
struct i2s_stream_instance *rtd;
427
u32 val;
428
429
adata = dev_get_drvdata(dev);
430
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);
439
}
440
}
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);
449
}
450
}
451
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);
460
}
461
}
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);
470
}
471
}
472
acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
473
return 0;
474
}
475
476
static int acp5x_pcm_suspend(struct device *dev)
477
{
478
struct i2s_dev_data *adata;
479
480
adata = dev_get_drvdata(dev);
481
acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
482
return 0;
483
}
484
485
static int acp5x_pcm_runtime_resume(struct device *dev)
486
{
487
struct i2s_dev_data *adata;
488
489
adata = dev_get_drvdata(dev);
490
acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
491
return 0;
492
}
493
494
static const struct dev_pm_ops acp5x_pm_ops = {
495
RUNTIME_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_runtime_resume, NULL)
496
SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)
497
};
498
499
static struct platform_driver acp5x_dma_driver = {
500
.probe = acp5x_audio_probe,
501
.remove = acp5x_audio_remove,
502
.driver = {
503
.name = "acp5x_i2s_dma",
504
.pm = &acp5x_pm_ops,
505
},
506
};
507
508
module_platform_driver(acp5x_dma_driver);
509
510
MODULE_AUTHOR("[email protected]");
511
MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");
512
MODULE_LICENSE("GPL v2");
513
MODULE_ALIAS("platform:" DRV_NAME);
514
515