Skip to content

Commit cc80b44

Browse files
committed
ASoC: q6apm: fix under runs and fragment sizes
Merge series from srinivas.kandagatla@linaro.org: On Qualcomm Audioreach setup, some of the audio artifacts are seen in both recording and playback. These patches fix issues by 1. Adjusting the fragment size that dsp can service. 2. schedule available playback buffers in time for dsp to not hit under runs 3. remove some of the manual calculations done to get hardware pointer. With these patches, am able to see significant Audio quality improvements. I have few more patches to optimize the dsp drivers, but for now am keeping this series simple to address the underruns and overruns issues noticed in pipewire setup. Any testing would be appreciated. Please note that on pipewire min-latency has to be set to 512 which reflects the DSP latency requirements of 10ms. You might see audio artifacts like glitches if you try to play audio below 256 latency.
2 parents 1ebd494 + a93dad6 commit cc80b44

File tree

3 files changed

+52
-29
lines changed

3 files changed

+52
-29
lines changed

sound/soc/qcom/qdsp6/q6apm-dai.c

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
#define PLAYBACK_MIN_PERIOD_SIZE 128
2525
#define CAPTURE_MIN_NUM_PERIODS 2
2626
#define CAPTURE_MAX_NUM_PERIODS 8
27-
#define CAPTURE_MAX_PERIOD_SIZE 4096
28-
#define CAPTURE_MIN_PERIOD_SIZE 320
27+
#define CAPTURE_MAX_PERIOD_SIZE 65536
28+
#define CAPTURE_MIN_PERIOD_SIZE 6144
2929
#define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)
3030
#define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)
3131
#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
@@ -64,12 +64,12 @@ struct q6apm_dai_rtd {
6464
phys_addr_t phys;
6565
unsigned int pcm_size;
6666
unsigned int pcm_count;
67-
unsigned int pos; /* Buffer position */
6867
unsigned int periods;
6968
unsigned int bytes_sent;
7069
unsigned int bytes_received;
7170
unsigned int copied_total;
7271
uint16_t bits_per_sample;
72+
snd_pcm_uframes_t queue_ptr;
7373
bool next_track;
7474
enum stream_state state;
7575
struct q6apm_graph *graph;
@@ -123,25 +123,16 @@ static void event_handler(uint32_t opcode, uint32_t token, void *payload, void *
123123
{
124124
struct q6apm_dai_rtd *prtd = priv;
125125
struct snd_pcm_substream *substream = prtd->substream;
126-
unsigned long flags;
127126

128127
switch (opcode) {
129128
case APM_CLIENT_EVENT_CMD_EOS_DONE:
130129
prtd->state = Q6APM_STREAM_STOPPED;
131130
break;
132131
case APM_CLIENT_EVENT_DATA_WRITE_DONE:
133-
spin_lock_irqsave(&prtd->lock, flags);
134-
prtd->pos += prtd->pcm_count;
135-
spin_unlock_irqrestore(&prtd->lock, flags);
136132
snd_pcm_period_elapsed(substream);
137-
if (prtd->state == Q6APM_STREAM_RUNNING)
138-
q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
139133

140134
break;
141135
case APM_CLIENT_EVENT_DATA_READ_DONE:
142-
spin_lock_irqsave(&prtd->lock, flags);
143-
prtd->pos += prtd->pcm_count;
144-
spin_unlock_irqrestore(&prtd->lock, flags);
145136
snd_pcm_period_elapsed(substream);
146137
if (prtd->state == Q6APM_STREAM_RUNNING)
147138
q6apm_read(prtd->graph);
@@ -248,7 +239,6 @@ static int q6apm_dai_prepare(struct snd_soc_component *component,
248239
}
249240

250241
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
251-
prtd->pos = 0;
252242
/* rate and channels are sent to audio driver */
253243
ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
254244
if (ret < 0) {
@@ -294,6 +284,27 @@ static int q6apm_dai_prepare(struct snd_soc_component *component,
294284
return 0;
295285
}
296286

287+
static int q6apm_dai_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream)
288+
{
289+
struct snd_pcm_runtime *runtime = substream->runtime;
290+
struct q6apm_dai_rtd *prtd = runtime->private_data;
291+
int i, ret = 0, avail_periods;
292+
293+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
294+
avail_periods = (runtime->control->appl_ptr - prtd->queue_ptr)/runtime->period_size;
295+
for (i = 0; i < avail_periods; i++) {
296+
ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP);
297+
if (ret < 0) {
298+
dev_err(component->dev, "Error queuing playback buffer %d\n", ret);
299+
return ret;
300+
}
301+
prtd->queue_ptr += runtime->period_size;
302+
}
303+
}
304+
305+
return ret;
306+
}
307+
297308
static int q6apm_dai_trigger(struct snd_soc_component *component,
298309
struct snd_pcm_substream *substream, int cmd)
299310
{
@@ -305,9 +316,6 @@ static int q6apm_dai_trigger(struct snd_soc_component *component,
305316
case SNDRV_PCM_TRIGGER_START:
306317
case SNDRV_PCM_TRIGGER_RESUME:
307318
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
308-
/* start writing buffers for playback only as we already queued capture buffers */
309-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
310-
ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
311319
break;
312320
case SNDRV_PCM_TRIGGER_STOP:
313321
/* TODO support be handled via SoftPause Module */
@@ -377,13 +385,14 @@ static int q6apm_dai_open(struct snd_soc_component *component,
377385
}
378386
}
379387

380-
ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
388+
/* setup 10ms latency to accommodate DSP restrictions */
389+
ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 480);
381390
if (ret < 0) {
382391
dev_err(dev, "constraint for period bytes step ret = %d\n", ret);
383392
goto err;
384393
}
385394

386-
ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
395+
ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 480);
387396
if (ret < 0) {
388397
dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret);
389398
goto err;
@@ -428,16 +437,12 @@ static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,
428437
struct snd_pcm_runtime *runtime = substream->runtime;
429438
struct q6apm_dai_rtd *prtd = runtime->private_data;
430439
snd_pcm_uframes_t ptr;
431-
unsigned long flags;
432440

433-
spin_lock_irqsave(&prtd->lock, flags);
434-
if (prtd->pos == prtd->pcm_size)
435-
prtd->pos = 0;
436-
437-
ptr = bytes_to_frames(runtime, prtd->pos);
438-
spin_unlock_irqrestore(&prtd->lock, flags);
441+
ptr = q6apm_get_hw_pointer(prtd->graph, substream->stream) * runtime->period_size;
442+
if (ptr)
443+
return ptr - 1;
439444

440-
return ptr;
445+
return 0;
441446
}
442447

443448
static int q6apm_dai_hw_params(struct snd_soc_component *component,
@@ -652,8 +657,6 @@ static int q6apm_dai_compr_set_params(struct snd_soc_component *component,
652657
prtd->pcm_size = runtime->fragments * runtime->fragment_size;
653658
prtd->bits_per_sample = 16;
654659

655-
prtd->pos = 0;
656-
657660
if (prtd->next_track != true) {
658661
memcpy(&prtd->codec, codec, sizeof(*codec));
659662

@@ -836,6 +839,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = {
836839
.hw_params = q6apm_dai_hw_params,
837840
.pointer = q6apm_dai_pointer,
838841
.trigger = q6apm_dai_trigger,
842+
.ack = q6apm_dai_ack,
839843
.compress_ops = &q6apm_dai_compress_ops,
840844
.use_dai_pcm_id = true,
841845
};

sound/soc/qcom/qdsp6/q6apm.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,19 @@ int q6apm_read(struct q6apm_graph *graph)
494494
}
495495
EXPORT_SYMBOL_GPL(q6apm_read);
496496

497+
int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir)
498+
{
499+
struct audioreach_graph_data *data;
500+
501+
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
502+
data = &graph->rx_data;
503+
else
504+
data = &graph->tx_data;
505+
506+
return (int)atomic_read(&data->hw_ptr);
507+
}
508+
EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer);
509+
497510
static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
498511
{
499512
struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
@@ -520,7 +533,8 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
520533
done = data->payload;
521534
phys = graph->rx_data.buf[token].phys;
522535
mutex_unlock(&graph->lock);
523-
536+
/* token numbering starts at 0 */
537+
atomic_set(&graph->rx_data.hw_ptr, token + 1);
524538
if (lower_32_bits(phys) == done->buf_addr_lsw &&
525539
upper_32_bits(phys) == done->buf_addr_msw) {
526540
graph->result.opcode = hdr->opcode;
@@ -553,6 +567,8 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
553567
rd_done = data->payload;
554568
phys = graph->tx_data.buf[hdr->token].phys;
555569
mutex_unlock(&graph->lock);
570+
/* token numbering starts at 0 */
571+
atomic_set(&graph->tx_data.hw_ptr, hdr->token + 1);
556572

557573
if (upper_32_bits(phys) == rd_done->buf_addr_msw &&
558574
lower_32_bits(phys) == rd_done->buf_addr_lsw) {

sound/soc/qcom/qdsp6/q6apm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#ifndef __Q6APM_H__
33
#define __Q6APM_H__
44
#include <linux/types.h>
5+
#include <linux/atomic.h>
56
#include <linux/slab.h>
67
#include <linux/wait.h>
78
#include <linux/kernel.h>
@@ -77,6 +78,7 @@ struct audioreach_graph_data {
7778
uint32_t num_periods;
7879
uint32_t dsp_buf;
7980
uint32_t mem_map_handle;
81+
atomic_t hw_ptr;
8082
};
8183

8284
struct audioreach_graph {
@@ -150,4 +152,5 @@ int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph,
150152
int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
151153
int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
152154
int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id);
155+
int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir);
153156
#endif /* __APM_GRAPH_ */

0 commit comments

Comments
 (0)