#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
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;
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;
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)
+ if (ret < 0) {
dev_err(dev, "%s: CMD Format block failed\n", __func__);
+ return ret;
+ }
- ret = q6apm_alloc_fragments(prtd->graph, substream->stream, prtd->phys,
- (prtd->pcm_size / prtd->periods), prtd->periods);
-
+ /* rate and channels are sent to audio driver */
+ ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
if (ret < 0) {
- dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret);
- return -ENOMEM;
+ dev_err(dev, "Failed to set media format %d\n", ret);
+ return ret;
}
ret = q6apm_graph_prepare(prtd->graph);
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 */
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++) {
/* 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:
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);
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;
}
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;
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;
}
*/
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;
}
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)