diff options
author | millerliang <millerliang@google.com> | 2023-09-06 14:50:47 +0800 |
---|---|---|
committer | Miller Liang <millerliang@google.com> | 2023-09-20 02:04:28 +0000 |
commit | 09fa9ec757b637967c9eed486274a9ba9ed8a3e9 (patch) | |
tree | 64692e5402e5dff095e4a912bb2e394d5e48ae43 | |
parent | 897e404fa79ddeeb5a156f40d3bd689d33de5db7 (diff) | |
download | aoc-09fa9ec757b637967c9eed486274a9ba9ed8a3e9.tar.gz |
alsa: check the consumed data size in irq process functions
The ALSA pcm lib driver assumes one period data was consumed in the
snd_pcm_update_hw_ptr0 function when updating the hw_ptr.
Update the hw_ptr if the consumed data size is equal to or
bigger than one period size.
Bug: 295546940
Test: Local test
Change-Id: I668c4272dbccb08fe7c46105b3c74b3a64bdd2d5
Signed-off-by: millerliang <millerliang@google.com>
-rw-r--r-- | alsa/aoc_alsa.h | 4 | ||||
-rw-r--r-- | alsa/aoc_alsa_incall.c | 16 | ||||
-rw-r--r-- | alsa/aoc_alsa_pcm.c | 49 | ||||
-rw-r--r-- | alsa/aoc_alsa_voip.c | 17 |
4 files changed, 62 insertions, 24 deletions
diff --git a/alsa/aoc_alsa.h b/alsa/aoc_alsa.h index a9c30ab..3cc3da9 100644 --- a/alsa/aoc_alsa.h +++ b/alsa/aoc_alsa.h @@ -305,6 +305,9 @@ struct aoc_alsa_stream { unsigned int period_size; unsigned int buffer_size; unsigned int pos; + unsigned int prev_pos; + unsigned int pos_delta; + unsigned long prev_buffer_cnt; unsigned long hw_ptr_base; /* read/write pointers in ring buffer */ unsigned long prev_consumed; int n_overflow; @@ -326,6 +329,7 @@ void aoc_timer_restart(struct aoc_alsa_stream *alsa_stream); void aoc_timer_stop(struct aoc_alsa_stream *alsa_stream); void aoc_timer_stop_sync(struct aoc_alsa_stream *alsa_stream); void aoc_pcm_period_work_handler(struct work_struct *work); +bool aoc_pcm_update_pos(struct aoc_alsa_stream *alsa_stream, unsigned long consumed); int snd_aoc_new_ctl(struct aoc_chip *chip); int snd_aoc_new_pcm(struct aoc_chip *chip); diff --git a/alsa/aoc_alsa_incall.c b/alsa/aoc_alsa_incall.c index 56a1ac7..eb8be56 100644 --- a/alsa/aoc_alsa_incall.c +++ b/alsa/aoc_alsa_incall.c @@ -77,14 +77,10 @@ static enum hrtimer_restart aoc_incall_hifi_irq_process(struct aoc_alsa_stream * } alsa_stream->prev_consumed = consumed; - /* Update the pcm pointer */ - if (unlikely(alsa_stream->n_overflow)) { - alsa_stream->pos = (consumed + 0x100000000 * alsa_stream->n_overflow - - alsa_stream->hw_ptr_base) % - alsa_stream->buffer_size; - } else { - alsa_stream->pos = (consumed - alsa_stream->hw_ptr_base) % alsa_stream->buffer_size; - } + if (!aoc_pcm_update_pos(alsa_stream, consumed)) + return HRTIMER_RESTART; + + alsa_stream->prev_pos = alsa_stream->pos; /* Do not queue a work if the cancel_work is active */ if (atomic_read(&alsa_stream->cancel_work_active) > 0 @@ -216,6 +212,7 @@ static int snd_aoc_pcm_open(struct snd_soc_component *component, aoc_ring_bytes_written(dev->service, AOC_UP); alsa_stream->prev_consumed = alsa_stream->hw_ptr_base; alsa_stream->n_overflow = 0; + alsa_stream->prev_buffer_cnt = 0; err = aoc_audio_open(alsa_stream); if (err != 0) { @@ -409,11 +406,14 @@ static int snd_aoc_pcm_prepare(struct snd_soc_component *component, alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); alsa_stream->pos = 0; + alsa_stream->prev_pos = 0; + alsa_stream->pos_delta = 0; alsa_stream->hw_ptr_base = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? aoc_ring_bytes_read(dev->service, AOC_DOWN) : aoc_ring_bytes_written(dev->service, AOC_UP); alsa_stream->prev_consumed = alsa_stream->hw_ptr_base; alsa_stream->n_overflow = 0; + alsa_stream->prev_buffer_cnt = 0; dev_dbg(component->dev, "pcm prepare: hw_ptr_base = %lu\n", alsa_stream->hw_ptr_base); diff --git a/alsa/aoc_alsa_pcm.c b/alsa/aoc_alsa_pcm.c index 1654ca0..14fbf12 100644 --- a/alsa/aoc_alsa_pcm.c +++ b/alsa/aoc_alsa_pcm.c @@ -133,6 +133,39 @@ void aoc_timer_stop_sync(struct aoc_alsa_stream *alsa_stream) } } +bool aoc_pcm_update_pos(struct aoc_alsa_stream *alsa_stream, unsigned long consumed) +{ + unsigned long buffer_cnt; + + /* Update the pcm pointer */ + if (unlikely(alsa_stream->n_overflow)) { + alsa_stream->pos = (consumed + 0x100000000 * alsa_stream->n_overflow - + alsa_stream->hw_ptr_base) % + alsa_stream->buffer_size; + buffer_cnt = (consumed + 0x100000000 * alsa_stream->n_overflow - + alsa_stream->hw_ptr_base) / alsa_stream->buffer_size; + } else { + alsa_stream->pos = (consumed - alsa_stream->hw_ptr_base) % alsa_stream->buffer_size; + buffer_cnt = (consumed - alsa_stream->hw_ptr_base) / alsa_stream->buffer_size; + } + + /* Update hw_ptr if the consumed data is equal to or bigger than one period */ + if (buffer_cnt == alsa_stream->prev_buffer_cnt) + alsa_stream->pos_delta = alsa_stream->pos - alsa_stream->prev_pos; + else { + alsa_stream->pos_delta = (alsa_stream->pos + alsa_stream->buffer_size * + (buffer_cnt - alsa_stream->prev_buffer_cnt)) - + alsa_stream->prev_pos; + if ((buffer_cnt - alsa_stream->prev_buffer_cnt) != 1) + pr_warn("idx(%d): buffer_cnt %ld, prev_buffer_cnt %ld, pos_delta = %d\n", + alsa_stream->idx, buffer_cnt, alsa_stream->prev_buffer_cnt, + alsa_stream->pos_delta); + alsa_stream->prev_buffer_cnt = buffer_cnt; + } + + return (alsa_stream->pos_delta >= alsa_stream->period_size) ? true : false; +} + /* Hardware definition * TODO: different pcm may have different hardware setup, * considering deep buffer and compressed offload buffer @@ -198,14 +231,10 @@ static enum hrtimer_restart aoc_pcm_irq_process(struct aoc_alsa_stream *alsa_str } alsa_stream->prev_consumed = consumed; - /* Update the pcm pointer */ - if (unlikely(alsa_stream->n_overflow)) { - alsa_stream->pos = (consumed + 0x100000000 * alsa_stream->n_overflow - - alsa_stream->hw_ptr_base) % - alsa_stream->buffer_size; - } else { - alsa_stream->pos = (consumed - alsa_stream->hw_ptr_base) % alsa_stream->buffer_size; - } + if (!aoc_pcm_update_pos(alsa_stream, consumed)) + return HRTIMER_RESTART; + + alsa_stream->prev_pos = alsa_stream->pos; /* Do not queue a work if the cancel_work is active */ if (atomic_read(&alsa_stream->cancel_work_active) > 0 || alsa_stream->pcm_period_wq == NULL) @@ -343,6 +372,7 @@ static int snd_aoc_pcm_open(struct snd_soc_component *component, aoc_ring_bytes_written(dev->service, AOC_UP); alsa_stream->prev_consumed = alsa_stream->hw_ptr_base; alsa_stream->n_overflow = 0; + alsa_stream->prev_buffer_cnt = 0; err = aoc_audio_open(alsa_stream); if (err != 0) { @@ -558,11 +588,14 @@ static int snd_aoc_pcm_prepare(struct snd_soc_component *component, alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); alsa_stream->pos = 0; + alsa_stream->prev_pos = 0; + alsa_stream->pos_delta = 0; alsa_stream->hw_ptr_base = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? aoc_ring_bytes_read(dev->service, AOC_DOWN) : aoc_ring_bytes_written(dev->service, AOC_UP); alsa_stream->prev_consumed = alsa_stream->hw_ptr_base; alsa_stream->n_overflow = 0; + alsa_stream->prev_buffer_cnt = 0; pr_debug("pcm prepare: hw_ptr_base = %lu\n", alsa_stream->hw_ptr_base); diff --git a/alsa/aoc_alsa_voip.c b/alsa/aoc_alsa_voip.c index f6200dd..f9f85ba 100644 --- a/alsa/aoc_alsa_voip.c +++ b/alsa/aoc_alsa_voip.c @@ -76,14 +76,10 @@ static enum hrtimer_restart aoc_voip_irq_process(struct aoc_alsa_stream *alsa_st } alsa_stream->prev_consumed = consumed; - /* Update the pcm pointer */ - if (unlikely(alsa_stream->n_overflow)) { - alsa_stream->pos = (consumed + 0x100000000 * alsa_stream->n_overflow - - alsa_stream->hw_ptr_base) % - alsa_stream->buffer_size; - } else { - alsa_stream->pos = (consumed - alsa_stream->hw_ptr_base) % alsa_stream->buffer_size; - } + if (!aoc_pcm_update_pos(alsa_stream, consumed)) + return HRTIMER_RESTART; + + alsa_stream->prev_pos = alsa_stream->pos; /* Do not queue a work if the cancel_work is active */ if (atomic_read(&alsa_stream->cancel_work_active) > 0 @@ -212,6 +208,7 @@ static int snd_aoc_pcm_open(struct snd_soc_component *component, aoc_ring_bytes_written(dev->service, AOC_UP); alsa_stream->prev_consumed = alsa_stream->hw_ptr_base; alsa_stream->n_overflow = 0; + alsa_stream->prev_buffer_cnt = 0; err = aoc_audio_open(alsa_stream); if (err != 0) { @@ -400,11 +397,15 @@ static int snd_aoc_pcm_prepare(struct snd_soc_component *component, alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); alsa_stream->pos = 0; + alsa_stream->prev_pos = 0; + alsa_stream->pos_delta = 0; + alsa_stream->hw_ptr_base = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? aoc_ring_bytes_written(dev->service, AOC_DOWN) : aoc_ring_bytes_written(dev->service, AOC_UP); alsa_stream->prev_consumed = alsa_stream->hw_ptr_base; alsa_stream->n_overflow = 0; + alsa_stream->prev_buffer_cnt = 0; pr_notice("Start voip call\n"); err = prepare_voipcall(alsa_stream); |