diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 3a1be41df096..bf1f872a09f4 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -18,6 +18,7 @@ #include "q6apm.h" #define DRV_NAME "q6apm-dai" +#define POS_BUFFER_BYTES 4096 #define PLAYBACK_MIN_NUM_PERIODS 2 #define PLAYBACK_MAX_NUM_PERIODS 8 @@ -62,8 +63,12 @@ struct q6apm_dai_rtd { struct snd_codec codec; struct snd_compr_params codec_param; struct snd_dma_buffer dma_buffer; + struct sh_mem_pull_push_mode_position_buffer *pos_buffer; + uint32_t last_pos_index; phys_addr_t phys; + phys_addr_t pos_phys; unsigned int pcm_size; + unsigned int push_pull_size; unsigned int pcm_count; unsigned int periods; uint64_t bytes_sent; @@ -128,6 +133,9 @@ static void event_handler(uint32_t opcode, uint32_t token, void *payload, void * struct snd_pcm_substream *substream = prtd->substream; switch (opcode) { + case APM_CLIENT_EVENT_WATERMARK_EVENT: + snd_pcm_period_elapsed(substream); + break; case APM_CLIENT_EVENT_CMD_EOS_DONE: prtd->state = Q6APM_STREAM_STOPPED; break; @@ -234,24 +242,47 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, q6apm_free_fragments(prtd->graph, substream->stream); } + prtd->last_pos_index = 0; prtd->pcm_count = snd_pcm_lib_period_bytes(substream); - /* rate and channels are sent to audio driver */ - ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); - if (ret < 0) { - dev_err(dev, "%s: q6apm_open_write failed\n", __func__); - return ret; + if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) { + if (prtd->pcm_size != prtd->push_pull_size) { + ret = q6apm_push_pull_config(prtd->graph, prtd->phys, prtd->pos_phys, + prtd->pcm_size); + if (ret < 0) { + dev_err(dev, "Push/Pull config failed rc = %d\n", ret); + return ret; + } + + ret = q6apm_register_watermark_event(prtd->graph, + prtd->pcm_size / prtd->periods, + prtd->periods); + if (ret < 0) { + dev_err(dev, "WaterMark event config failed rc = %d\n", ret); + return ret; + } + prtd->push_pull_size = prtd->pcm_size; + } + } else { + ret = q6apm_alloc_fragments(prtd->graph, substream->stream, prtd->phys, + (prtd->pcm_size / prtd->periods), prtd->periods); + if (ret < 0) { + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + return ret; + } + } ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg); - if (ret < 0) - dev_err(dev, "%s: CMD Format block failed\n", __func__); - - ret = q6apm_alloc_fragments(prtd->graph, substream->stream, prtd->phys, - (prtd->pcm_size / prtd->periods), prtd->periods); - if (ret < 0) { - dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); - return -ENOMEM; + dev_err(dev, "%s: CMD Format block failed\n", __func__); + return ret; + } + + /* rate and channels are sent to audio driver */ + ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); + if (ret < 0) { + dev_err(dev, "Failed to set media format %d\n", ret); + return ret; } ret = q6apm_graph_prepare(prtd->graph); @@ -265,13 +296,13 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, dev_err(dev, "Failed to Start Graph %d\n", ret); return ret; } - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - int i; - /* Queue the buffers for Capture ONLY after graph is started */ - for (i = 0; i < runtime->periods; i++) - q6apm_read(prtd->graph); - + if (!q6apm_is_graph_in_push_pull_mode(prtd->graph)) { + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + int i; + /* Queue the buffers for Capture ONLY after graph is started */ + for (i = 0; i < runtime->periods; i++) + q6apm_read(prtd->graph); + } } /* Now that graph as been prepared and started update the internal state accordingly */ @@ -286,6 +317,9 @@ static int q6apm_dai_ack(struct snd_soc_component *component, struct snd_pcm_sub struct q6apm_dai_rtd *prtd = runtime->private_data; int i, ret = 0, avail_periods; + if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) + return 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { avail_periods = (runtime->control->appl_ptr - prtd->queue_ptr)/runtime->period_size; for (i = 0; i < avail_periods; i++) { @@ -317,6 +351,7 @@ static int q6apm_dai_trigger(struct snd_soc_component *component, /* TODO support be handled via SoftPause Module */ prtd->state = Q6APM_STREAM_STOPPED; prtd->queue_ptr = 0; + prtd->last_pos_index = 0; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -402,6 +437,14 @@ static int q6apm_dai_open(struct snd_soc_component *component, else prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32); + if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) { + void *pos_buffer; + + prtd->pos_phys = prtd->phys + BUFFER_BYTES_MAX; + pos_buffer = (void *)(substream->dma_buffer.area + BUFFER_BYTES_MAX); + prtd->pos_buffer = (struct sh_mem_pull_push_mode_position_buffer *)(pos_buffer); + } + return 0; err: kfree(prtd); @@ -436,6 +479,25 @@ static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component, struct q6apm_dai_rtd *prtd = runtime->private_data; snd_pcm_uframes_t ptr; + if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) { + int retries = 10; + uint32_t index, fc1, fc2; + + /* index is valid if frame_counter does not change while reading. */ + do { + fc1 = READ_ONCE(prtd->pos_buffer->frame_counter); + index = READ_ONCE(prtd->pos_buffer->index); + fc2 = READ_ONCE(prtd->pos_buffer->frame_counter); + } while (fc1 != fc2 && --retries); + + if (fc1 != fc2) + index = prtd->last_pos_index; + else + prtd->last_pos_index = index; + + ptr = bytes_to_frames(runtime, index); + return ptr; + } ptr = q6apm_get_hw_pointer(prtd->graph, substream->stream) * runtime->period_size; if (ptr) return ptr - 1; @@ -468,7 +530,8 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component, } static int q6apm_dai_memory_map(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int graph_id) + struct snd_pcm_substream *substream, + int graph_id, bool is_push_pull) { struct q6apm_dai_data *pdata; struct device *dev = component->dev; @@ -490,6 +553,19 @@ static int q6apm_dai_memory_map(struct snd_soc_component *component, if (ret < 0) dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + if (is_push_pull) { + if (pdata->sid < 0) + phys = substream->dma_buffer.addr + BUFFER_BYTES_MAX; + else + phys = (substream->dma_buffer.addr + BUFFER_BYTES_MAX) | (pdata->sid << 32); + + ret = q6apm_map_pos_buffer(dev, graph_id, phys, POS_BUFFER_BYTES); + if (ret < 0) + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + } else { + + } + return ret; } @@ -504,25 +580,30 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc */ int size = BUFFER_BYTES_MAX + PAGE_SIZE; int graph_id, ret; - struct snd_pcm_substream *substream; + bool is_push_pull; + struct snd_pcm_substream *substream = NULL; graph_id = cpu_dai->driver->id; - ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); - if (ret) - return ret; - /* Note: DSP backend dais are uni-directional ONLY(either playback or capture) */ - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - ret = q6apm_dai_memory_map(component, substream, graph_id); + else if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + + + if (substream) { + is_push_pull = q6apm_is_graph_in_push_pull_mode_from_id(component->dev, + graph_id, + substream->stream); + if (is_push_pull) + size += POS_BUFFER_BYTES; + + ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); if (ret) return ret; - } - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - ret = q6apm_dai_memory_map(component, substream, graph_id); + ret = q6apm_dai_memory_map(component, substream, graph_id, is_push_pull); if (ret) return ret; } @@ -547,6 +628,9 @@ static void q6apm_dai_memory_unmap(struct snd_soc_component *component, graph_id = cpu_dai->driver->id; q6apm_unmap_memory_fixed_region(component->dev, graph_id); + + if (q6apm_is_graph_in_push_pull_mode_from_id(component->dev, graph_id, substream->stream)) + q6apm_unmap_pos_buffer(component->dev, graph_id); } static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm)