diff options
Diffstat (limited to 'sound/soc/qcom/qdsp6/q6asm-dai.c')
| -rw-r--r-- | sound/soc/qcom/qdsp6/q6asm-dai.c | 132 |
1 files changed, 77 insertions, 55 deletions
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 045100c94352..709b4f3318ff 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -14,6 +14,7 @@ #include <sound/pcm.h> #include <linux/spinlock.h> #include <sound/compress_driver.h> +#include <asm/div64.h> #include <asm/dma.h> #include <linux/dma-mapping.h> #include <sound/pcm_params.h> @@ -57,12 +58,12 @@ struct q6asm_dai_rtd { phys_addr_t phys; unsigned int pcm_size; unsigned int pcm_count; - unsigned int pcm_irq_pos; /* IRQ position */ unsigned int periods; - unsigned int bytes_sent; - unsigned int bytes_received; - unsigned int copied_total; + uint64_t bytes_sent; + uint64_t bytes_received; + uint64_t copied_total; uint16_t bits_per_sample; + snd_pcm_uframes_t queue_ptr; uint16_t source; /* Encoding source bit mask */ struct audio_client *audio_client; uint32_t next_track_stream_id; @@ -84,6 +85,7 @@ struct q6asm_dai_data { static const struct snd_pcm_hardware q6asm_dai_hardware_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), @@ -107,6 +109,7 @@ static const struct snd_pcm_hardware q6asm_dai_hardware_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -181,24 +184,15 @@ static void event_handler(uint32_t opcode, uint32_t token, switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - q6asm_write_async(prtd->audio_client, prtd->stream_id, - prtd->pcm_count, 0, 0, 0); break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: prtd->state = Q6ASM_STREAM_STOPPED; break; case ASM_CLIENT_EVENT_DATA_WRITE_DONE: { - prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); - if (prtd->state == Q6ASM_STREAM_RUNNING) - q6asm_write_async(prtd->audio_client, prtd->stream_id, - prtd->pcm_count, 0, 0, 0); - break; } case ASM_CLIENT_EVENT_DATA_READ_DONE: - prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) q6asm_read(prtd->audio_client, prtd->stream_id); @@ -230,15 +224,15 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, } prtd->pcm_count = snd_pcm_lib_period_bytes(substream); - prtd->pcm_irq_pos = 0; /* rate and channels are sent to audio driver */ - if (prtd->state) { + if (prtd->state == Q6ASM_STREAM_RUNNING) { /* clear the previous setup if any */ q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); q6routing_stream_close(soc_prtd->dai_link->id, substream->stream); + prtd->state = Q6ASM_STREAM_STOPPED; } ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client, @@ -309,6 +303,29 @@ open_err: return ret; } +static int q6asm_dai_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + int i, ret = 0, avail_periods; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && prtd->state == Q6ASM_STREAM_RUNNING) { + avail_periods = (runtime->control->appl_ptr - prtd->queue_ptr)/runtime->period_size; + for (i = 0; i < avail_periods; i++) { + ret = q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, 0); + + if (ret < 0) { + dev_err(component->dev, "Error queuing playback buffer %d\n", ret); + return ret; + } + prtd->queue_ptr += runtime->period_size; + } + } + + return ret; +} + static int q6asm_dai_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { @@ -402,13 +419,13 @@ static int q6asm_dai_open(struct snd_soc_component *component, } ret = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 480); if (ret < 0) { dev_err(dev, "constraint for period bytes step ret = %d\n", ret); } ret = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 480); if (ret < 0) { dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret); @@ -416,10 +433,13 @@ static int q6asm_dai_open(struct snd_soc_component *component, runtime->private_data = prtd; - snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback); - - runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max; - + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback); + runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max; + } else { + snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_capture); + runtime->dma_bytes = q6asm_dai_hardware_capture.buffer_bytes_max; + } if (pdata->sid < 0) prtd->phys = substream->dma_buffer.addr; @@ -458,11 +478,13 @@ static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; + snd_pcm_uframes_t ptr; - if (prtd->pcm_irq_pos >= prtd->pcm_size) - prtd->pcm_irq_pos = 0; + ptr = q6asm_get_hw_pointer(prtd->audio_client, substream->stream) * runtime->period_size; + if (ptr) + return ptr - 1; - return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); + return 0; } static int q6asm_dai_hw_params(struct snd_soc_component *component, @@ -492,15 +514,15 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, { struct q6asm_dai_rtd *prtd = priv; struct snd_compr_stream *substream = prtd->cstream; - unsigned long flags; u32 wflags = 0; uint64_t avail; uint32_t bytes_written, bytes_to_write; bool is_last_buffer = false; + guard(spinlock_irqsave)(&prtd->lock); + switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: - spin_lock_irqsave(&prtd->lock, flags); if (!prtd->bytes_sent) { q6asm_stream_remove_initial_silence(prtd->audio_client, prtd->stream_id, @@ -511,11 +533,9 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, prtd->bytes_sent += prtd->pcm_count; } - spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: - spin_lock_irqsave(&prtd->lock, flags); if (prtd->notify_on_drain) { if (substream->partial_drain) { /* @@ -538,20 +558,16 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, } else { prtd->state = Q6ASM_STREAM_STOPPED; } - spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_CLIENT_EVENT_DATA_WRITE_DONE: - spin_lock_irqsave(&prtd->lock, flags); bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT; prtd->copied_total += bytes_written; snd_compr_fragment_elapsed(substream); - if (prtd->state != Q6ASM_STREAM_RUNNING) { - spin_unlock_irqrestore(&prtd->lock, flags); + if (prtd->state != Q6ASM_STREAM_RUNNING) break; - } avail = prtd->bytes_received - prtd->bytes_sent; if (avail > prtd->pcm_count) { @@ -580,7 +596,6 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, CMD_EOS); - spin_unlock_irqrestore(&prtd->lock, flags); break; default: @@ -892,9 +907,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, if (ret < 0) { dev_err(dev, "q6asm_open_write failed\n"); - q6asm_audio_client_free(prtd->audio_client); - prtd->audio_client = NULL; - return ret; + goto open_err; } } @@ -903,7 +916,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, prtd->session_id, dir); if (ret) { dev_err(dev, "Stream reg failed ret:%d\n", ret); - return ret; + goto q6_err; } ret = __q6asm_dai_compr_set_codec_params(component, stream, @@ -911,7 +924,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, prtd->stream_id); if (ret) { dev_err(dev, "codec param setup failed ret:%d\n", ret); - return ret; + goto q6_err; } ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, @@ -920,12 +933,21 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, if (ret < 0) { dev_err(dev, "Buffer Mapping failed ret:%d\n", ret); - return -ENOMEM; + ret = -ENOMEM; + goto q6_err; } prtd->state = Q6ASM_STREAM_RUNNING; return 0; + +q6_err: + q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); + +open_err: + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return ret; } static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, @@ -1019,18 +1041,17 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component, static int q6asm_dai_compr_pointer(struct snd_soc_component *component, struct snd_compr_stream *stream, - struct snd_compr_tstamp *tstamp) + struct snd_compr_tstamp64 *tstamp) { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; - unsigned long flags; + uint64_t temp_copied_total; - spin_lock_irqsave(&prtd->lock, flags); + guard(spinlock_irqsave)(&prtd->lock); tstamp->copied_total = prtd->copied_total; - tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; - - spin_unlock_irqrestore(&prtd->lock, flags); + temp_copied_total = tstamp->copied_total; + tstamp->byte_offset = do_div(temp_copied_total, prtd->pcm_size); return 0; } @@ -1041,25 +1062,27 @@ static int q6asm_compr_copy(struct snd_soc_component *component, { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; - unsigned long flags; u32 wflags = 0; - int avail, bytes_in_flight = 0; + uint64_t avail, bytes_in_flight = 0; void *dstn; size_t copy; u32 app_pointer; - u32 bytes_received; + uint64_t bytes_received; + uint64_t temp_bytes_received; bytes_received = prtd->bytes_received; + temp_bytes_received = bytes_received; /** * Make sure that next track data pointer is aligned at 32 bit boundary * This is a Mandatory requirement from DSP data buffers alignment */ - if (prtd->next_track) + if (prtd->next_track) { bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); + temp_bytes_received = bytes_received; + } - app_pointer = bytes_received/prtd->pcm_size; - app_pointer = bytes_received - (app_pointer * prtd->pcm_size); + app_pointer = do_div(temp_bytes_received, prtd->pcm_size); dstn = prtd->dma_buffer.area + app_pointer; if (count < prtd->pcm_size - app_pointer) { @@ -1074,7 +1097,7 @@ static int q6asm_compr_copy(struct snd_soc_component *component, return -EFAULT; } - spin_lock_irqsave(&prtd->lock, flags); + guard(spinlock_irqsave)(&prtd->lock); bytes_in_flight = prtd->bytes_received - prtd->copied_total; @@ -1100,8 +1123,6 @@ static int q6asm_compr_copy(struct snd_soc_component *component, prtd->bytes_sent += bytes_to_write; } - spin_unlock_irqrestore(&prtd->lock, flags); - return count; } @@ -1201,6 +1222,7 @@ static const struct snd_soc_component_driver q6asm_fe_dai_component = { .close = q6asm_dai_close, .prepare = q6asm_dai_prepare, .trigger = q6asm_dai_trigger, + .ack = q6asm_dai_ack, .pointer = q6asm_dai_pointer, .pcm_construct = q6asm_dai_pcm_new, .compress_ops = &q6asm_dai_compress_ops, |
