Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/bcm/bcm63xx-pcm-whistler.c
26444 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
// linux/sound/bcm/bcm63xx-pcm-whistler.c
3
// BCM63xx whistler pcm interface
4
// Copyright (c) 2020 Broadcom Corporation
5
// Author: Kevin-Ke Li <[email protected]>
6
7
#include <linux/dma-mapping.h>
8
#include <linux/io.h>
9
#include <linux/irq.h>
10
#include <linux/module.h>
11
#include <sound/pcm_params.h>
12
#include <linux/regmap.h>
13
#include <linux/of_device.h>
14
#include <sound/soc.h>
15
#include "bcm63xx-i2s.h"
16
17
18
struct i2s_dma_desc {
19
unsigned char *dma_area;
20
dma_addr_t dma_addr;
21
unsigned int dma_len;
22
};
23
24
struct bcm63xx_runtime_data {
25
int dma_len;
26
dma_addr_t dma_addr;
27
dma_addr_t dma_addr_next;
28
};
29
30
static const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
31
.info = SNDRV_PCM_INFO_MMAP |
32
SNDRV_PCM_INFO_MMAP_VALID |
33
SNDRV_PCM_INFO_INTERLEAVED |
34
SNDRV_PCM_INFO_PAUSE |
35
SNDRV_PCM_INFO_RESUME,
36
.formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
37
.period_bytes_max = 8192 - 32,
38
.periods_min = 1,
39
.periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
40
.buffer_bytes_max = 128 * 1024,
41
.fifo_size = 32,
42
};
43
44
static int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
45
struct snd_pcm_substream *substream,
46
struct snd_pcm_hw_params *params)
47
{
48
struct i2s_dma_desc *dma_desc;
49
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
50
51
dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
52
if (!dma_desc)
53
return -ENOMEM;
54
55
snd_soc_dai_set_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream, dma_desc);
56
57
return 0;
58
}
59
60
static int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
61
struct snd_pcm_substream *substream)
62
{
63
struct i2s_dma_desc *dma_desc;
64
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
65
66
dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
67
kfree(dma_desc);
68
69
return 0;
70
}
71
72
static int bcm63xx_pcm_trigger(struct snd_soc_component *component,
73
struct snd_pcm_substream *substream, int cmd)
74
{
75
int ret = 0;
76
struct snd_soc_pcm_runtime *rtd;
77
struct bcm_i2s_priv *i2s_priv;
78
struct regmap *regmap_i2s;
79
80
rtd = snd_soc_substream_to_rtd(substream);
81
i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
82
regmap_i2s = i2s_priv->regmap_i2s;
83
84
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
85
switch (cmd) {
86
case SNDRV_PCM_TRIGGER_START:
87
regmap_update_bits(regmap_i2s,
88
I2S_TX_IRQ_EN,
89
I2S_TX_DESC_OFF_INTR_EN,
90
I2S_TX_DESC_OFF_INTR_EN);
91
regmap_update_bits(regmap_i2s,
92
I2S_TX_CFG,
93
I2S_TX_ENABLE_MASK,
94
I2S_TX_ENABLE);
95
break;
96
case SNDRV_PCM_TRIGGER_STOP:
97
case SNDRV_PCM_TRIGGER_SUSPEND:
98
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
99
regmap_write(regmap_i2s,
100
I2S_TX_IRQ_EN,
101
0);
102
regmap_update_bits(regmap_i2s,
103
I2S_TX_CFG,
104
I2S_TX_ENABLE_MASK,
105
0);
106
break;
107
default:
108
ret = -EINVAL;
109
}
110
} else {
111
switch (cmd) {
112
case SNDRV_PCM_TRIGGER_START:
113
regmap_update_bits(regmap_i2s,
114
I2S_RX_IRQ_EN,
115
I2S_RX_DESC_OFF_INTR_EN_MSK,
116
I2S_RX_DESC_OFF_INTR_EN);
117
regmap_update_bits(regmap_i2s,
118
I2S_RX_CFG,
119
I2S_RX_ENABLE_MASK,
120
I2S_RX_ENABLE);
121
break;
122
case SNDRV_PCM_TRIGGER_STOP:
123
case SNDRV_PCM_TRIGGER_SUSPEND:
124
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
125
regmap_update_bits(regmap_i2s,
126
I2S_RX_IRQ_EN,
127
I2S_RX_DESC_OFF_INTR_EN_MSK,
128
0);
129
regmap_update_bits(regmap_i2s,
130
I2S_RX_CFG,
131
I2S_RX_ENABLE_MASK,
132
0);
133
break;
134
default:
135
ret = -EINVAL;
136
}
137
}
138
return ret;
139
}
140
141
static int bcm63xx_pcm_prepare(struct snd_soc_component *component,
142
struct snd_pcm_substream *substream)
143
{
144
struct i2s_dma_desc *dma_desc;
145
struct regmap *regmap_i2s;
146
struct bcm_i2s_priv *i2s_priv;
147
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
148
struct snd_pcm_runtime *runtime = substream->runtime;
149
uint32_t regaddr_desclen, regaddr_descaddr;
150
151
dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
152
dma_desc->dma_len = snd_pcm_lib_period_bytes(substream);
153
dma_desc->dma_addr = runtime->dma_addr;
154
dma_desc->dma_area = runtime->dma_area;
155
156
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
157
regaddr_desclen = I2S_TX_DESC_IFF_LEN;
158
regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
159
} else {
160
regaddr_desclen = I2S_RX_DESC_IFF_LEN;
161
regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
162
}
163
164
i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
165
regmap_i2s = i2s_priv->regmap_i2s;
166
167
regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
168
regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
169
170
return 0;
171
}
172
173
static snd_pcm_uframes_t
174
bcm63xx_pcm_pointer(struct snd_soc_component *component,
175
struct snd_pcm_substream *substream)
176
{
177
snd_pcm_uframes_t x;
178
struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
179
180
if (!prtd->dma_addr_next)
181
prtd->dma_addr_next = substream->runtime->dma_addr;
182
183
x = bytes_to_frames(substream->runtime,
184
prtd->dma_addr_next - substream->runtime->dma_addr);
185
186
return x == substream->runtime->buffer_size ? 0 : x;
187
}
188
189
static int bcm63xx_pcm_open(struct snd_soc_component *component,
190
struct snd_pcm_substream *substream)
191
{
192
int ret = 0;
193
struct snd_pcm_runtime *runtime = substream->runtime;
194
struct bcm63xx_runtime_data *prtd;
195
196
runtime->hw = bcm63xx_pcm_hardware;
197
ret = snd_pcm_hw_constraint_step(runtime, 0,
198
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
199
if (ret)
200
goto out;
201
202
ret = snd_pcm_hw_constraint_step(runtime, 0,
203
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
204
if (ret)
205
goto out;
206
207
ret = snd_pcm_hw_constraint_integer(runtime,
208
SNDRV_PCM_HW_PARAM_PERIODS);
209
if (ret < 0)
210
goto out;
211
212
ret = -ENOMEM;
213
prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
214
if (!prtd)
215
goto out;
216
217
runtime->private_data = prtd;
218
return 0;
219
out:
220
return ret;
221
}
222
223
static int bcm63xx_pcm_close(struct snd_soc_component *component,
224
struct snd_pcm_substream *substream)
225
{
226
struct snd_pcm_runtime *runtime = substream->runtime;
227
struct bcm63xx_runtime_data *prtd = runtime->private_data;
228
229
kfree(prtd);
230
return 0;
231
}
232
233
static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
234
{
235
unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
236
struct bcm63xx_runtime_data *prtd;
237
struct snd_pcm_substream *substream;
238
struct snd_pcm_runtime *runtime;
239
struct regmap *regmap_i2s;
240
struct i2s_dma_desc *dma_desc;
241
struct snd_soc_pcm_runtime *rtd;
242
struct bcm_i2s_priv *i2s_priv;
243
244
i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
245
regmap_i2s = i2s_priv->regmap_i2s;
246
247
/* rx */
248
regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
249
250
if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
251
substream = i2s_priv->capture_substream;
252
runtime = substream->runtime;
253
rtd = snd_soc_substream_to_rtd(substream);
254
prtd = runtime->private_data;
255
dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
256
257
offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
258
I2S_RX_DESC_OFF_LEVEL_SHIFT;
259
bool val_read = false;
260
while (offlevel) {
261
regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
262
regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
263
val_read = true;
264
offlevel--;
265
}
266
if (val_read)
267
prtd->dma_addr_next = val_1 + val_2;
268
269
ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
270
I2S_RX_DESC_IFF_LEVEL_SHIFT;
271
272
availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
273
while (availdepth) {
274
dma_desc->dma_addr +=
275
snd_pcm_lib_period_bytes(substream);
276
dma_desc->dma_area +=
277
snd_pcm_lib_period_bytes(substream);
278
if (dma_desc->dma_addr - runtime->dma_addr >=
279
runtime->dma_bytes) {
280
dma_desc->dma_addr = runtime->dma_addr;
281
dma_desc->dma_area = runtime->dma_area;
282
}
283
284
prtd->dma_addr = dma_desc->dma_addr;
285
regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
286
snd_pcm_lib_period_bytes(substream));
287
regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
288
dma_desc->dma_addr);
289
availdepth--;
290
}
291
292
snd_pcm_period_elapsed(substream);
293
294
/* Clear interrupt by writing 0 */
295
regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
296
I2S_RX_INTR_MASK, 0);
297
}
298
299
/* tx */
300
regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
301
302
if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
303
substream = i2s_priv->play_substream;
304
runtime = substream->runtime;
305
rtd = snd_soc_substream_to_rtd(substream);
306
prtd = runtime->private_data;
307
dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
308
309
offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
310
I2S_TX_DESC_OFF_LEVEL_SHIFT;
311
while (offlevel) {
312
regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
313
regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN, &val_2);
314
prtd->dma_addr_next = val_1 + val_2;
315
offlevel--;
316
}
317
318
ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
319
I2S_TX_DESC_IFF_LEVEL_SHIFT;
320
availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
321
322
while (availdepth) {
323
dma_desc->dma_addr +=
324
snd_pcm_lib_period_bytes(substream);
325
dma_desc->dma_area +=
326
snd_pcm_lib_period_bytes(substream);
327
328
if (dma_desc->dma_addr - runtime->dma_addr >=
329
runtime->dma_bytes) {
330
dma_desc->dma_addr = runtime->dma_addr;
331
dma_desc->dma_area = runtime->dma_area;
332
}
333
334
prtd->dma_addr = dma_desc->dma_addr;
335
regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
336
snd_pcm_lib_period_bytes(substream));
337
regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
338
dma_desc->dma_addr);
339
availdepth--;
340
}
341
342
snd_pcm_period_elapsed(substream);
343
344
/* Clear interrupt by writing 0 */
345
regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
346
I2S_TX_INTR_MASK, 0);
347
}
348
349
return IRQ_HANDLED;
350
}
351
352
static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
353
struct snd_soc_pcm_runtime *rtd)
354
{
355
struct snd_pcm *pcm = rtd->pcm;
356
struct bcm_i2s_priv *i2s_priv;
357
int ret;
358
359
i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
360
361
of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
362
363
ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
364
if (ret)
365
return ret;
366
367
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
368
i2s_priv->play_substream =
369
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
370
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
371
i2s_priv->capture_substream =
372
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
373
374
return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
375
pcm->card->dev,
376
bcm63xx_pcm_hardware.buffer_bytes_max);
377
}
378
379
static const struct snd_soc_component_driver bcm63xx_soc_platform = {
380
.open = bcm63xx_pcm_open,
381
.close = bcm63xx_pcm_close,
382
.hw_params = bcm63xx_pcm_hw_params,
383
.hw_free = bcm63xx_pcm_hw_free,
384
.prepare = bcm63xx_pcm_prepare,
385
.trigger = bcm63xx_pcm_trigger,
386
.pointer = bcm63xx_pcm_pointer,
387
.pcm_construct = bcm63xx_soc_pcm_new,
388
};
389
390
int bcm63xx_soc_platform_probe(struct platform_device *pdev,
391
struct bcm_i2s_priv *i2s_priv)
392
{
393
int ret;
394
395
ret = platform_get_irq(pdev, 0);
396
if (ret < 0)
397
return ret;
398
399
ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
400
irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
401
if (ret) {
402
dev_err(&pdev->dev,
403
"i2s_init: failed to request interrupt.ret=%d\n", ret);
404
return ret;
405
}
406
407
return devm_snd_soc_register_component(&pdev->dev,
408
&bcm63xx_soc_platform, NULL, 0);
409
}
410
411
int bcm63xx_soc_platform_remove(struct platform_device *pdev)
412
{
413
return 0;
414
}
415
416
MODULE_AUTHOR("Kevin,Li <[email protected]>");
417
MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
418
MODULE_LICENSE("GPL v2");
419
420