Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/atmel/atmel-pcm-pdc.c
26436 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
4
*
5
* Copyright (C) 2005 SAN People
6
* Copyright (C) 2008 Atmel
7
*
8
* Authors: Sedji Gaouaou <[email protected]>
9
*
10
* Based on at91-pcm. by:
11
* Frank Mandarino <[email protected]>
12
* Copyright 2006 Endrelia Technologies Inc.
13
*
14
* Based on pxa2xx-pcm.c by:
15
*
16
* Author: Nicolas Pitre
17
* Created: Nov 30, 2004
18
* Copyright: (C) 2004 MontaVista Software, Inc.
19
*/
20
21
#include <linux/module.h>
22
#include <linux/init.h>
23
#include <linux/platform_device.h>
24
#include <linux/slab.h>
25
#include <linux/dma-mapping.h>
26
#include <linux/atmel_pdc.h>
27
#include <linux/atmel-ssc.h>
28
29
#include <sound/core.h>
30
#include <sound/pcm.h>
31
#include <sound/pcm_params.h>
32
#include <sound/soc.h>
33
34
#include "atmel-pcm.h"
35
36
37
static int atmel_pcm_new(struct snd_soc_component *component,
38
struct snd_soc_pcm_runtime *rtd)
39
{
40
struct snd_card *card = rtd->card->snd_card;
41
int ret;
42
43
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
44
if (ret)
45
return ret;
46
47
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
48
card->dev, ATMEL_SSC_DMABUF_SIZE,
49
ATMEL_SSC_DMABUF_SIZE);
50
51
return 0;
52
}
53
54
/*--------------------------------------------------------------------------*\
55
* Hardware definition
56
\*--------------------------------------------------------------------------*/
57
/* TODO: These values were taken from the AT91 platform driver, check
58
* them against real values for AT32
59
*/
60
static const struct snd_pcm_hardware atmel_pcm_hardware = {
61
.info = SNDRV_PCM_INFO_MMAP |
62
SNDRV_PCM_INFO_MMAP_VALID |
63
SNDRV_PCM_INFO_INTERLEAVED |
64
SNDRV_PCM_INFO_PAUSE,
65
.period_bytes_min = 32,
66
.period_bytes_max = 8192,
67
.periods_min = 2,
68
.periods_max = 1024,
69
.buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE,
70
};
71
72
73
/*--------------------------------------------------------------------------*\
74
* Data types
75
\*--------------------------------------------------------------------------*/
76
struct atmel_runtime_data {
77
struct atmel_pcm_dma_params *params;
78
dma_addr_t dma_buffer; /* physical address of dma buffer */
79
dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
80
size_t period_size;
81
82
dma_addr_t period_ptr; /* physical address of next period */
83
};
84
85
/*--------------------------------------------------------------------------*\
86
* ISR
87
\*--------------------------------------------------------------------------*/
88
static void atmel_pcm_dma_irq(u32 ssc_sr,
89
struct snd_pcm_substream *substream)
90
{
91
struct atmel_runtime_data *prtd = substream->runtime->private_data;
92
struct atmel_pcm_dma_params *params = prtd->params;
93
static int count;
94
95
count++;
96
97
if (ssc_sr & params->mask->ssc_endbuf) {
98
pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
99
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
100
? "underrun" : "overrun",
101
params->name, ssc_sr, count);
102
103
/* re-start the PDC */
104
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
105
params->mask->pdc_disable);
106
prtd->period_ptr += prtd->period_size;
107
if (prtd->period_ptr >= prtd->dma_buffer_end)
108
prtd->period_ptr = prtd->dma_buffer;
109
110
ssc_writex(params->ssc->regs, params->pdc->xpr,
111
prtd->period_ptr);
112
ssc_writex(params->ssc->regs, params->pdc->xcr,
113
prtd->period_size / params->pdc_xfer_size);
114
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
115
params->mask->pdc_enable);
116
}
117
118
if (ssc_sr & params->mask->ssc_endx) {
119
/* Load the PDC next pointer and counter registers */
120
prtd->period_ptr += prtd->period_size;
121
if (prtd->period_ptr >= prtd->dma_buffer_end)
122
prtd->period_ptr = prtd->dma_buffer;
123
124
ssc_writex(params->ssc->regs, params->pdc->xnpr,
125
prtd->period_ptr);
126
ssc_writex(params->ssc->regs, params->pdc->xncr,
127
prtd->period_size / params->pdc_xfer_size);
128
}
129
130
snd_pcm_period_elapsed(substream);
131
}
132
133
134
/*--------------------------------------------------------------------------*\
135
* PCM operations
136
\*--------------------------------------------------------------------------*/
137
static int atmel_pcm_hw_params(struct snd_soc_component *component,
138
struct snd_pcm_substream *substream,
139
struct snd_pcm_hw_params *params)
140
{
141
struct snd_pcm_runtime *runtime = substream->runtime;
142
struct atmel_runtime_data *prtd = runtime->private_data;
143
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
144
145
/* this may get called several times by oss emulation
146
* with different params */
147
148
prtd->params = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
149
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
150
151
prtd->dma_buffer = runtime->dma_addr;
152
prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
153
prtd->period_size = params_period_bytes(params);
154
155
pr_debug("atmel-pcm: "
156
"hw_params: DMA for %s initialized "
157
"(dma_bytes=%zu, period_size=%zu)\n",
158
prtd->params->name,
159
runtime->dma_bytes,
160
prtd->period_size);
161
return 0;
162
}
163
164
static int atmel_pcm_hw_free(struct snd_soc_component *component,
165
struct snd_pcm_substream *substream)
166
{
167
struct atmel_runtime_data *prtd = substream->runtime->private_data;
168
struct atmel_pcm_dma_params *params = prtd->params;
169
170
if (params != NULL) {
171
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
172
params->mask->pdc_disable);
173
prtd->params->dma_intr_handler = NULL;
174
}
175
176
return 0;
177
}
178
179
static int atmel_pcm_prepare(struct snd_soc_component *component,
180
struct snd_pcm_substream *substream)
181
{
182
struct atmel_runtime_data *prtd = substream->runtime->private_data;
183
struct atmel_pcm_dma_params *params = prtd->params;
184
185
ssc_writex(params->ssc->regs, SSC_IDR,
186
params->mask->ssc_endx | params->mask->ssc_endbuf);
187
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
188
params->mask->pdc_disable);
189
return 0;
190
}
191
192
static int atmel_pcm_trigger(struct snd_soc_component *component,
193
struct snd_pcm_substream *substream, int cmd)
194
{
195
struct snd_pcm_runtime *rtd = substream->runtime;
196
struct atmel_runtime_data *prtd = rtd->private_data;
197
struct atmel_pcm_dma_params *params = prtd->params;
198
int ret = 0;
199
200
pr_debug("atmel-pcm:buffer_size = %ld,"
201
"dma_area = %p, dma_bytes = %zu\n",
202
rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
203
204
switch (cmd) {
205
case SNDRV_PCM_TRIGGER_START:
206
prtd->period_ptr = prtd->dma_buffer;
207
208
ssc_writex(params->ssc->regs, params->pdc->xpr,
209
prtd->period_ptr);
210
ssc_writex(params->ssc->regs, params->pdc->xcr,
211
prtd->period_size / params->pdc_xfer_size);
212
213
prtd->period_ptr += prtd->period_size;
214
ssc_writex(params->ssc->regs, params->pdc->xnpr,
215
prtd->period_ptr);
216
ssc_writex(params->ssc->regs, params->pdc->xncr,
217
prtd->period_size / params->pdc_xfer_size);
218
219
pr_debug("atmel-pcm: trigger: "
220
"period_ptr=%lx, xpr=%u, "
221
"xcr=%u, xnpr=%u, xncr=%u\n",
222
(unsigned long)prtd->period_ptr,
223
ssc_readx(params->ssc->regs, params->pdc->xpr),
224
ssc_readx(params->ssc->regs, params->pdc->xcr),
225
ssc_readx(params->ssc->regs, params->pdc->xnpr),
226
ssc_readx(params->ssc->regs, params->pdc->xncr));
227
228
ssc_writex(params->ssc->regs, SSC_IER,
229
params->mask->ssc_endx | params->mask->ssc_endbuf);
230
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
231
params->mask->pdc_enable);
232
233
pr_debug("sr=%u imr=%u\n",
234
ssc_readx(params->ssc->regs, SSC_SR),
235
ssc_readx(params->ssc->regs, SSC_IER));
236
break; /* SNDRV_PCM_TRIGGER_START */
237
238
case SNDRV_PCM_TRIGGER_STOP:
239
case SNDRV_PCM_TRIGGER_SUSPEND:
240
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
241
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
242
params->mask->pdc_disable);
243
break;
244
245
case SNDRV_PCM_TRIGGER_RESUME:
246
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
247
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
248
params->mask->pdc_enable);
249
break;
250
251
default:
252
ret = -EINVAL;
253
}
254
255
return ret;
256
}
257
258
static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
259
struct snd_pcm_substream *substream)
260
{
261
struct snd_pcm_runtime *runtime = substream->runtime;
262
struct atmel_runtime_data *prtd = runtime->private_data;
263
struct atmel_pcm_dma_params *params = prtd->params;
264
dma_addr_t ptr;
265
snd_pcm_uframes_t x;
266
267
ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
268
x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
269
270
if (x == runtime->buffer_size)
271
x = 0;
272
273
return x;
274
}
275
276
static int atmel_pcm_open(struct snd_soc_component *component,
277
struct snd_pcm_substream *substream)
278
{
279
struct snd_pcm_runtime *runtime = substream->runtime;
280
struct atmel_runtime_data *prtd;
281
int ret = 0;
282
283
snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
284
285
/* ensure that buffer size is a multiple of period size */
286
ret = snd_pcm_hw_constraint_integer(runtime,
287
SNDRV_PCM_HW_PARAM_PERIODS);
288
if (ret < 0)
289
goto out;
290
291
prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
292
if (prtd == NULL) {
293
ret = -ENOMEM;
294
goto out;
295
}
296
runtime->private_data = prtd;
297
298
out:
299
return ret;
300
}
301
302
static int atmel_pcm_close(struct snd_soc_component *component,
303
struct snd_pcm_substream *substream)
304
{
305
struct atmel_runtime_data *prtd = substream->runtime->private_data;
306
307
kfree(prtd);
308
return 0;
309
}
310
311
static const struct snd_soc_component_driver atmel_soc_platform = {
312
.open = atmel_pcm_open,
313
.close = atmel_pcm_close,
314
.hw_params = atmel_pcm_hw_params,
315
.hw_free = atmel_pcm_hw_free,
316
.prepare = atmel_pcm_prepare,
317
.trigger = atmel_pcm_trigger,
318
.pointer = atmel_pcm_pointer,
319
.pcm_construct = atmel_pcm_new,
320
};
321
322
int atmel_pcm_pdc_platform_register(struct device *dev)
323
{
324
return devm_snd_soc_register_component(dev, &atmel_soc_platform,
325
NULL, 0);
326
}
327
EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
328
329
MODULE_AUTHOR("Sedji Gaouaou <[email protected]>");
330
MODULE_DESCRIPTION("Atmel PCM module");
331
MODULE_LICENSE("GPL");
332
333