Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/amd/vangogh/acp5x-i2s.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 <sound/pcm_params.h>
12
#include <sound/soc.h>
13
#include <sound/soc-dai.h>
14
#include <linux/dma-mapping.h>
15
16
#include "acp5x.h"
17
18
#define DRV_NAME "acp5x_i2s_playcap"
19
20
static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
21
unsigned int fmt)
22
{
23
struct i2s_dev_data *adata;
24
int mode;
25
26
adata = snd_soc_dai_get_drvdata(cpu_dai);
27
mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
28
switch (mode) {
29
case SND_SOC_DAIFMT_I2S:
30
adata->tdm_mode = TDM_DISABLE;
31
break;
32
case SND_SOC_DAIFMT_DSP_A:
33
adata->tdm_mode = TDM_ENABLE;
34
break;
35
default:
36
return -EINVAL;
37
}
38
mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
39
switch (mode) {
40
case SND_SOC_DAIFMT_BP_FP:
41
adata->master_mode = I2S_MASTER_MODE_ENABLE;
42
break;
43
case SND_SOC_DAIFMT_BC_FC:
44
adata->master_mode = I2S_MASTER_MODE_DISABLE;
45
break;
46
}
47
return 0;
48
}
49
50
static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
51
u32 tx_mask, u32 rx_mask,
52
int slots, int slot_width)
53
{
54
struct i2s_dev_data *adata;
55
u32 frm_len;
56
u16 slot_len;
57
58
adata = snd_soc_dai_get_drvdata(cpu_dai);
59
60
/* These values are as per Hardware Spec */
61
switch (slot_width) {
62
case SLOT_WIDTH_8:
63
slot_len = 8;
64
break;
65
case SLOT_WIDTH_16:
66
slot_len = 16;
67
break;
68
case SLOT_WIDTH_24:
69
slot_len = 24;
70
break;
71
case SLOT_WIDTH_32:
72
slot_len = 0;
73
break;
74
default:
75
return -EINVAL;
76
}
77
frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
78
adata->tdm_fmt = frm_len;
79
return 0;
80
}
81
82
static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
83
struct snd_pcm_hw_params *params,
84
struct snd_soc_dai *dai)
85
{
86
struct i2s_stream_instance *rtd;
87
struct snd_soc_pcm_runtime *prtd;
88
struct snd_soc_card *card;
89
struct acp5x_platform_info *pinfo;
90
struct i2s_dev_data *adata;
91
92
u32 val;
93
u32 reg_val, frmt_reg;
94
u32 lrclk_div_val, bclk_div_val;
95
96
lrclk_div_val = 0;
97
bclk_div_val = 0;
98
prtd = snd_soc_substream_to_rtd(substream);
99
rtd = substream->runtime->private_data;
100
card = prtd->card;
101
adata = snd_soc_dai_get_drvdata(dai);
102
pinfo = snd_soc_card_get_drvdata(card);
103
if (pinfo) {
104
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
105
rtd->i2s_instance = pinfo->play_i2s_instance;
106
else
107
rtd->i2s_instance = pinfo->cap_i2s_instance;
108
}
109
110
/* These values are as per Hardware Spec */
111
switch (params_format(params)) {
112
case SNDRV_PCM_FORMAT_U8:
113
case SNDRV_PCM_FORMAT_S8:
114
rtd->xfer_resolution = 0x0;
115
break;
116
case SNDRV_PCM_FORMAT_S16_LE:
117
rtd->xfer_resolution = 0x02;
118
break;
119
case SNDRV_PCM_FORMAT_S24_LE:
120
rtd->xfer_resolution = 0x04;
121
break;
122
case SNDRV_PCM_FORMAT_S32_LE:
123
rtd->xfer_resolution = 0x05;
124
break;
125
default:
126
return -EINVAL;
127
}
128
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
129
switch (rtd->i2s_instance) {
130
case I2S_HS_INSTANCE:
131
reg_val = ACP_HSTDM_ITER;
132
frmt_reg = ACP_HSTDM_TXFRMT;
133
break;
134
case I2S_SP_INSTANCE:
135
default:
136
reg_val = ACP_I2STDM_ITER;
137
frmt_reg = ACP_I2STDM_TXFRMT;
138
}
139
} else {
140
switch (rtd->i2s_instance) {
141
case I2S_HS_INSTANCE:
142
reg_val = ACP_HSTDM_IRER;
143
frmt_reg = ACP_HSTDM_RXFRMT;
144
break;
145
case I2S_SP_INSTANCE:
146
default:
147
reg_val = ACP_I2STDM_IRER;
148
frmt_reg = ACP_I2STDM_RXFRMT;
149
}
150
}
151
if (adata->tdm_mode) {
152
val = acp_readl(rtd->acp5x_base + reg_val);
153
acp_writel(val | 0x2, rtd->acp5x_base + reg_val);
154
acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);
155
}
156
val = acp_readl(rtd->acp5x_base + reg_val);
157
val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;
158
val = val | (rtd->xfer_resolution << 3);
159
acp_writel(val, rtd->acp5x_base + reg_val);
160
161
if (adata->master_mode) {
162
switch (params_format(params)) {
163
case SNDRV_PCM_FORMAT_S16_LE:
164
switch (params_rate(params)) {
165
case 8000:
166
bclk_div_val = 768;
167
break;
168
case 16000:
169
bclk_div_val = 384;
170
break;
171
case 24000:
172
bclk_div_val = 256;
173
break;
174
case 32000:
175
bclk_div_val = 192;
176
break;
177
case 44100:
178
case 48000:
179
bclk_div_val = 128;
180
break;
181
case 88200:
182
case 96000:
183
bclk_div_val = 64;
184
break;
185
case 192000:
186
bclk_div_val = 32;
187
break;
188
default:
189
return -EINVAL;
190
}
191
lrclk_div_val = 32;
192
break;
193
case SNDRV_PCM_FORMAT_S32_LE:
194
switch (params_rate(params)) {
195
case 8000:
196
bclk_div_val = 384;
197
break;
198
case 16000:
199
bclk_div_val = 192;
200
break;
201
case 24000:
202
bclk_div_val = 128;
203
break;
204
case 32000:
205
bclk_div_val = 96;
206
break;
207
case 44100:
208
case 48000:
209
bclk_div_val = 64;
210
break;
211
case 88200:
212
case 96000:
213
bclk_div_val = 32;
214
break;
215
case 192000:
216
bclk_div_val = 16;
217
break;
218
default:
219
return -EINVAL;
220
}
221
lrclk_div_val = 64;
222
break;
223
default:
224
return -EINVAL;
225
}
226
rtd->lrclk_div = lrclk_div_val;
227
rtd->bclk_div = bclk_div_val;
228
}
229
return 0;
230
}
231
232
static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
233
int cmd, struct snd_soc_dai *dai)
234
{
235
struct i2s_stream_instance *rtd;
236
struct i2s_dev_data *adata;
237
u32 ret, val, period_bytes, reg_val, ier_val, water_val;
238
u32 buf_size, buf_reg;
239
240
adata = snd_soc_dai_get_drvdata(dai);
241
rtd = substream->runtime->private_data;
242
period_bytes = frames_to_bytes(substream->runtime,
243
substream->runtime->period_size);
244
buf_size = frames_to_bytes(substream->runtime,
245
substream->runtime->buffer_size);
246
switch (cmd) {
247
case SNDRV_PCM_TRIGGER_START:
248
case SNDRV_PCM_TRIGGER_RESUME:
249
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250
rtd->bytescount = acp_get_byte_count(rtd,
251
substream->stream);
252
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
253
switch (rtd->i2s_instance) {
254
case I2S_HS_INSTANCE:
255
water_val =
256
ACP_HS_TX_INTR_WATERMARK_SIZE;
257
reg_val = ACP_HSTDM_ITER;
258
ier_val = ACP_HSTDM_IER;
259
buf_reg = ACP_HS_TX_RINGBUFSIZE;
260
break;
261
case I2S_SP_INSTANCE:
262
default:
263
water_val =
264
ACP_I2S_TX_INTR_WATERMARK_SIZE;
265
reg_val = ACP_I2STDM_ITER;
266
ier_val = ACP_I2STDM_IER;
267
buf_reg = ACP_I2S_TX_RINGBUFSIZE;
268
}
269
} else {
270
switch (rtd->i2s_instance) {
271
case I2S_HS_INSTANCE:
272
water_val =
273
ACP_HS_RX_INTR_WATERMARK_SIZE;
274
reg_val = ACP_HSTDM_IRER;
275
ier_val = ACP_HSTDM_IER;
276
buf_reg = ACP_HS_RX_RINGBUFSIZE;
277
break;
278
case I2S_SP_INSTANCE:
279
default:
280
water_val =
281
ACP_I2S_RX_INTR_WATERMARK_SIZE;
282
reg_val = ACP_I2STDM_IRER;
283
ier_val = ACP_I2STDM_IER;
284
buf_reg = ACP_I2S_RX_RINGBUFSIZE;
285
}
286
}
287
acp_writel(period_bytes, rtd->acp5x_base + water_val);
288
acp_writel(buf_size, rtd->acp5x_base + buf_reg);
289
if (adata->master_mode)
290
acp5x_set_i2s_clk(adata, rtd);
291
val = acp_readl(rtd->acp5x_base + reg_val);
292
val = val | BIT(0);
293
acp_writel(val, rtd->acp5x_base + reg_val);
294
acp_writel(1, rtd->acp5x_base + ier_val);
295
ret = 0;
296
break;
297
case SNDRV_PCM_TRIGGER_STOP:
298
case SNDRV_PCM_TRIGGER_SUSPEND:
299
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
300
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
301
switch (rtd->i2s_instance) {
302
case I2S_HS_INSTANCE:
303
reg_val = ACP_HSTDM_ITER;
304
break;
305
case I2S_SP_INSTANCE:
306
default:
307
reg_val = ACP_I2STDM_ITER;
308
}
309
310
} else {
311
switch (rtd->i2s_instance) {
312
case I2S_HS_INSTANCE:
313
reg_val = ACP_HSTDM_IRER;
314
break;
315
case I2S_SP_INSTANCE:
316
default:
317
reg_val = ACP_I2STDM_IRER;
318
}
319
}
320
val = acp_readl(rtd->acp5x_base + reg_val);
321
val = val & ~BIT(0);
322
acp_writel(val, rtd->acp5x_base + reg_val);
323
324
if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&
325
!(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))
326
acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);
327
if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&
328
!(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))
329
acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);
330
ret = 0;
331
break;
332
default:
333
ret = -EINVAL;
334
break;
335
}
336
return ret;
337
}
338
339
static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
340
.hw_params = acp5x_i2s_hwparams,
341
.trigger = acp5x_i2s_trigger,
342
.set_fmt = acp5x_i2s_set_fmt,
343
.set_tdm_slot = acp5x_i2s_set_tdm_slot,
344
};
345
346
static const struct snd_soc_component_driver acp5x_dai_component = {
347
.name = "acp5x-i2s",
348
.legacy_dai_naming = 1,
349
};
350
351
static struct snd_soc_dai_driver acp5x_i2s_dai = {
352
.playback = {
353
.rates = SNDRV_PCM_RATE_8000_96000,
354
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
355
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
356
.channels_min = 2,
357
.channels_max = 2,
358
.rate_min = 8000,
359
.rate_max = 96000,
360
},
361
.capture = {
362
.rates = SNDRV_PCM_RATE_8000_96000,
363
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
364
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
365
.channels_min = 2,
366
.channels_max = 2,
367
.rate_min = 8000,
368
.rate_max = 96000,
369
},
370
.ops = &acp5x_i2s_dai_ops,
371
};
372
373
static int acp5x_dai_probe(struct platform_device *pdev)
374
{
375
struct resource *res;
376
struct i2s_dev_data *adata;
377
int ret;
378
379
adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
380
GFP_KERNEL);
381
if (!adata)
382
return -ENOMEM;
383
384
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
385
if (!res) {
386
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
387
return -ENOMEM;
388
}
389
adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
390
resource_size(res));
391
if (!adata->acp5x_base)
392
return -ENOMEM;
393
394
adata->master_mode = I2S_MASTER_MODE_ENABLE;
395
dev_set_drvdata(&pdev->dev, adata);
396
ret = devm_snd_soc_register_component(&pdev->dev,
397
&acp5x_dai_component,
398
&acp5x_i2s_dai, 1);
399
if (ret)
400
dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
401
return ret;
402
}
403
404
static struct platform_driver acp5x_dai_driver = {
405
.probe = acp5x_dai_probe,
406
.driver = {
407
.name = "acp5x_i2s_playcap",
408
},
409
};
410
411
module_platform_driver(acp5x_dai_driver);
412
413
MODULE_AUTHOR("[email protected]");
414
MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");
415
MODULE_ALIAS("platform:" DRV_NAME);
416
MODULE_LICENSE("GPL v2");
417
418