Skip to content

Commit d7ca3a7

Browse files
tiwaigregkh
authored andcommitted
staging: bcm2835-audio: Operate non-atomic PCM ops
This is the most significant part in the patch series. The bcm2835-audio driver used to queue the commands to vc04 core via workqueue, but basically the whole accesses to vc04 core are done in the sleepable context, including the callback calls. In such a case, rewriting the code using non-atomic PCM ops will simplify the logic a lot. This patch does it: all workqueue are gone and each former-work implementation is now directly called from PCM ops like trigger and write transfer. Along with it, the DMA position updater, bcm2835_playback_fifo(), was also rewritten to use a simpler logic. Now it handles the XRUN and draining properly by calling snd_pcm_stop() conditionally. The current position is kept in atomic_t value so that it can be read concurrently from the pointer callback. Also, the bcm2835_audio_instance object is allocated at the beginning of bcm2835_audio_open(). This makes the resource management clearer. Signed-off-by: Takashi Iwai <[email protected]> Tested-by: Stefan Wahren <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0307363 commit d7ca3a7

File tree

3 files changed

+82
-245
lines changed

3 files changed

+82
-245
lines changed

drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
/* hardware definition */
1212
static const struct snd_pcm_hardware snd_bcm2835_playback_hw = {
1313
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
14-
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
14+
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
15+
SNDRV_PCM_INFO_DRAIN_TRIGGER),
1516
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
1617
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
1718
.rate_min = 8000,
@@ -27,7 +28,8 @@ static const struct snd_pcm_hardware snd_bcm2835_playback_hw = {
2728

2829
static const struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
2930
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
30-
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
31+
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
32+
SNDRV_PCM_INFO_DRAIN_TRIGGER),
3133
.formats = SNDRV_PCM_FMTBIT_S16_LE,
3234
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
3335
SNDRV_PCM_RATE_48000,
@@ -47,42 +49,34 @@ static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
4749
kfree(runtime->private_data);
4850
}
4951

50-
void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream)
52+
void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream,
53+
unsigned int bytes)
5154
{
52-
unsigned int consumed = 0;
53-
int new_period = 0;
54-
55-
audio_info("alsa_stream=%p substream=%p\n", alsa_stream,
56-
alsa_stream ? alsa_stream->substream : 0);
57-
58-
consumed = bcm2835_audio_retrieve_buffers(alsa_stream);
59-
60-
/* We get called only if playback was triggered, So, the number of buffers we retrieve in
61-
* each iteration are the buffers that have been played out already
62-
*/
63-
64-
if (alsa_stream->period_size) {
65-
if ((alsa_stream->pos / alsa_stream->period_size) !=
66-
((alsa_stream->pos + consumed) / alsa_stream->period_size))
67-
new_period = 1;
68-
}
69-
audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n",
70-
alsa_stream->pos,
71-
consumed,
72-
alsa_stream->buffer_size,
73-
(int) (alsa_stream->period_size * alsa_stream->substream->runtime->periods),
74-
frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr),
75-
new_period);
76-
if (alsa_stream->buffer_size) {
77-
alsa_stream->pos += consumed & ~(1 << 30);
78-
alsa_stream->pos %= alsa_stream->buffer_size;
55+
struct snd_pcm_substream *substream = alsa_stream->substream;
56+
unsigned int pos;
57+
58+
if (!alsa_stream->period_size)
59+
return;
60+
61+
if (bytes >= alsa_stream->buffer_size) {
62+
snd_pcm_stream_lock(substream);
63+
snd_pcm_stop(substream,
64+
alsa_stream->draining ?
65+
SNDRV_PCM_STATE_SETUP :
66+
SNDRV_PCM_STATE_XRUN);
67+
snd_pcm_stream_unlock(substream);
68+
return;
7969
}
8070

81-
if (alsa_stream->substream) {
82-
if (new_period)
83-
snd_pcm_period_elapsed(alsa_stream->substream);
84-
} else {
85-
audio_warning(" unexpected NULL substream\n");
71+
pos = atomic_read(&alsa_stream->pos);
72+
pos += bytes;
73+
pos %= alsa_stream->buffer_size;
74+
atomic_set(&alsa_stream->pos, pos);
75+
76+
alsa_stream->period_offset += bytes;
77+
if (alsa_stream->period_offset >= alsa_stream->period_size) {
78+
alsa_stream->period_offset %= alsa_stream->period_size;
79+
snd_pcm_period_elapsed(substream);
8680
}
8781
}
8882

@@ -246,7 +240,8 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
246240

247241
alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream);
248242
alsa_stream->period_size = snd_pcm_lib_period_bytes(substream);
249-
alsa_stream->pos = 0;
243+
atomic_set(&alsa_stream->pos, 0);
244+
alsa_stream->period_offset = 0;
250245
alsa_stream->draining = false;
251246

252247
return 0;
@@ -283,7 +278,7 @@ static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
283278
return bcm2835_audio_start(alsa_stream);
284279
case SNDRV_PCM_TRIGGER_DRAIN:
285280
alsa_stream->draining = true;
286-
return 0;
281+
return bcm2835_audio_drain(alsa_stream);
287282
case SNDRV_PCM_TRIGGER_STOP:
288283
return bcm2835_audio_stop(alsa_stream);
289284
default:
@@ -300,7 +295,7 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream)
300295

301296
return snd_pcm_indirect_playback_pointer(substream,
302297
&alsa_stream->pcm_indirect,
303-
alsa_stream->pos);
298+
atomic_read(&alsa_stream->pos));
304299
}
305300

306301
/* operators */
@@ -338,6 +333,7 @@ int snd_bcm2835_new_pcm(struct bcm2835_chip *chip, u32 numchannels)
338333
if (err < 0)
339334
return err;
340335
pcm->private_data = chip;
336+
pcm->nonatomic = true;
341337
strcpy(pcm->name, "bcm2835 ALSA");
342338
chip->pcm = pcm;
343339
chip->dest = AUDIO_DEST_AUTO;
@@ -367,6 +363,7 @@ int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip)
367363
return err;
368364

369365
pcm->private_data = chip;
366+
pcm->nonatomic = true;
370367
strcpy(pcm->name, "bcm2835 IEC958/HDMI");
371368
chip->pcm_spdif = pcm;
372369
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -395,6 +392,7 @@ int snd_bcm2835_new_simple_pcm(struct bcm2835_chip *chip,
395392
return err;
396393

397394
pcm->private_data = chip;
395+
pcm->nonatomic = true;
398396
strcpy(pcm->name, name);
399397
chip->pcm = pcm;
400398
chip->dest = route;

0 commit comments

Comments
 (0)