Skip to content

Commit bad49c2

Browse files
author
Javier Martinez Canillas
committed
ALSA: bcm2835: add memory-mapped I/O mode for audio stream
ALSA supports two transfers methods for PCM playback: Read/Write transfer where samples are writtern to the device using standard read and write functions and Direct Read/Write transfers where samples can be written directly to a mapped memory area and the driver is signaled once this has been done. The bcm2835 driver only supported Read/Write transfer method so this patch adds mmap support to the driver. The ARM CPU is not able to directly address the audio device hardware buffer so audio samples are sent to the device using a message passing interface (vchiq). Since hardware buffers can't be directly mapped to user-space memory, an intermediate buffer (using the PCM indirect API) is needed to store the audio samples and push them to the device through videocore. Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
1 parent 24f64ad commit bad49c2

File tree

2 files changed

+45
-26
lines changed

2 files changed

+45
-26
lines changed

sound/arm/bcm2835-pcm.c

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
/* hardware definition */
2121
static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
22-
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER),
22+
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
23+
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
2324
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
2425
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
2526
.rate_min = 8000,
@@ -251,6 +252,12 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
251252

252253
audio_info(" .. IN\n");
253254

255+
memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
256+
257+
alsa_stream->pcm_indirect.hw_buffer_size =
258+
alsa_stream->pcm_indirect.sw_buffer_size =
259+
snd_pcm_lib_buffer_bytes(substream);
260+
254261
alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream);
255262
alsa_stream->period_size = snd_pcm_lib_period_bytes(substream);
256263
alsa_stream->pos = 0;
@@ -263,6 +270,32 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
263270
return 0;
264271
}
265272

273+
static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream,
274+
struct snd_pcm_indirect *rec, size_t bytes)
275+
{
276+
struct snd_pcm_runtime *runtime = substream->runtime;
277+
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
278+
void *src = (void *)(substream->runtime->dma_area + rec->sw_data);
279+
int err;
280+
281+
err = bcm2835_audio_write(alsa_stream, bytes, src);
282+
if (err)
283+
audio_error(" Failed to transfer to alsa device (%d)\n", err);
284+
285+
}
286+
287+
static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream)
288+
{
289+
struct snd_pcm_runtime *runtime = substream->runtime;
290+
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
291+
struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect;
292+
293+
pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max;
294+
snd_pcm_indirect_playback_transfer(substream, pcm_indirect,
295+
snd_bcm2835_pcm_transfer);
296+
return 0;
297+
}
298+
266299
/* trigger callback */
267300
static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
268301
{
@@ -279,6 +312,11 @@ static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
279312
if (!alsa_stream->running) {
280313
err = bcm2835_audio_start(alsa_stream);
281314
if (err == 0) {
315+
alsa_stream->pcm_indirect.hw_io =
316+
alsa_stream->pcm_indirect.hw_data =
317+
bytes_to_frames(runtime,
318+
alsa_stream->pos);
319+
substream->ops->ack(substream);
282320
alsa_stream->running = 1;
283321
alsa_stream->draining = 1;
284322
} else {
@@ -327,30 +365,9 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream)
327365
alsa_stream->pos);
328366

329367
audio_info(" .. OUT\n");
330-
return bytes_to_frames(runtime, alsa_stream->pos);
331-
}
332-
333-
static int snd_bcm2835_pcm_copy(struct snd_pcm_substream *substream,
334-
int channel, snd_pcm_uframes_t pos, void *src,
335-
snd_pcm_uframes_t count)
336-
{
337-
int ret;
338-
struct snd_pcm_runtime *runtime = substream->runtime;
339-
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
340-
341-
audio_info(" .. IN\n");
342-
audio_debug("copy.......... (%d) hwptr=%d appl=%d pos=%d\n",
343-
frames_to_bytes(runtime, count), frames_to_bytes(runtime,
344-
runtime->
345-
status->
346-
hw_ptr),
347-
frames_to_bytes(runtime, runtime->control->appl_ptr),
348-
alsa_stream->pos);
349-
ret =
350-
bcm2835_audio_write(alsa_stream, frames_to_bytes(runtime, count),
351-
src);
352-
audio_info(" .. OUT\n");
353-
return ret;
368+
return snd_pcm_indirect_playback_pointer(substream,
369+
&alsa_stream->pcm_indirect,
370+
alsa_stream->pos);
354371
}
355372

356373
static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream,
@@ -372,7 +389,7 @@ static struct snd_pcm_ops snd_bcm2835_playback_ops = {
372389
.prepare = snd_bcm2835_pcm_prepare,
373390
.trigger = snd_bcm2835_pcm_trigger,
374391
.pointer = snd_bcm2835_pcm_pointer,
375-
.copy = snd_bcm2835_pcm_copy,
392+
.ack = snd_bcm2835_pcm_ack,
376393
};
377394

378395
/* create a pcm device */

sound/arm/bcm2835.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <sound/initval.h>
2424
#include <sound/pcm.h>
2525
#include <sound/pcm_params.h>
26+
#include <sound/pcm-indirect.h>
2627
#include <linux/workqueue.h>
2728

2829
/*
@@ -110,6 +111,7 @@ typedef struct bcm2835_chip {
110111
typedef struct bcm2835_alsa_stream {
111112
bcm2835_chip_t *chip;
112113
struct snd_pcm_substream *substream;
114+
struct snd_pcm_indirect pcm_indirect;
113115

114116
struct semaphore buffers_update_sem;
115117
struct semaphore control_sem;

0 commit comments

Comments
 (0)