Loading sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +339 −80 Original line number Diff line number Diff line Loading @@ -44,6 +44,12 @@ #include "msm-pcm-routing-v2.h" #include "audio_ocmem.h" #define DSP_PP_BUFFERING_IN_MSEC 25 #define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150 #define MP3_OUTPUT_FRAME_SZ 1152 #define AAC_OUTPUT_FRAME_SZ 1024 #define DSP_NUM_OUTPUT_FRAME_BUFFERED 2 /* Default values used if user space does not set */ #define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) #define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) Loading @@ -54,6 +60,13 @@ const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0, COMPRESSED_LR_VOL_MAX_STEPS); struct msm_compr_gapless_state { bool set_next_stream_id; int32_t stream_opened[2]; uint32_t initial_samples_drop; uint32_t trailing_samples_drop; }; struct msm_compr_pdata { atomic_t audio_ocmem_req; struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; Loading @@ -75,6 +88,9 @@ struct msm_compr_audio { uint32_t byte_offset; uint32_t copied_total; uint32_t bytes_received; int32_t first_buffer; int32_t last_buffer; int32_t partial_drain_delay; uint16_t session_id; Loading @@ -85,14 +101,19 @@ struct msm_compr_audio { uint32_t cmd_interrupt; uint32_t drain_ready; struct msm_compr_gapless_state gapless_state; atomic_t start; atomic_t eos; atomic_t drain; atomic_t xrun; atomic_t close; atomic_t wait_on_close; wait_queue_head_t eos_wait; wait_queue_head_t drain_wait; wait_queue_head_t flush_wait; wait_queue_head_t close_wait; spinlock_t lock; }; Loading Loading @@ -143,6 +164,17 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) pr_debug("%s: bytes_received = %d copied_total = %d\n", __func__, prtd->bytes_received, prtd->copied_total); /* * FIXME: Initial and trailing silence removal API call to DSP results * to a glitch during the stream transition for gapless playback. * Add this when the issue is fixed from DSP. */ /* if (prtd->first_buffer) q6asm_send_meta_data(prtd->audio_client, prtd->gapless_state.initial_samples_drop, prtd->gapless_state.trailing_samples_drop); */ buffer_length = prtd->codec_param.buffer.fragment_size; bytes_available = prtd->bytes_received - prtd->copied_total; if (bytes_available < prtd->codec_param.buffer.fragment_size) Loading @@ -153,7 +185,10 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) pr_debug("wrap around situation, send partial data %d now", buffer_length); } if (buffer_length) param.paddr = prtd->buffer_paddr + prtd->byte_offset; else param.paddr = prtd->buffer_paddr; WARN(param.paddr % 32 != 0, "param.paddr %lx not multiple of 32", param.paddr); param.len = buffer_length; Loading @@ -162,11 +197,16 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) param.flags = NO_TIMESTAMP; param.uid = buffer_length; param.metadata_len = 0; param.last_buffer = prtd->last_buffer; pr_debug("%s: sending %d bytes to DSP byte_offset = %d\n", __func__, buffer_length, prtd->byte_offset); if (q6asm_async_write(prtd->audio_client, ¶m) < 0) if (q6asm_async_write(prtd->audio_client, ¶m) < 0) { pr_err("%s:q6asm_async_write failed\n", __func__); } else { if (prtd->first_buffer) prtd->first_buffer = 0; } return 0; } Loading @@ -176,9 +216,10 @@ static void compr_event_handler(uint32_t opcode, { struct msm_compr_audio *prtd = priv; struct snd_compr_stream *cstream = prtd->cstream; struct audio_client *ac = prtd->audio_client; uint32_t chan_mode = 0; uint32_t sample_rate = 0; int bytes_available; int bytes_available, stream_id; pr_debug("%s opcode =%08x\n", __func__, opcode); switch (opcode) { Loading Loading @@ -217,12 +258,19 @@ static void compr_event_handler(uint32_t opcode, pr_debug("WRITE_DONE Insufficient data to send. break out\n"); atomic_set(&prtd->xrun, 1); if (prtd->last_buffer) prtd->last_buffer = 0; if (atomic_read(&prtd->drain)) { pr_debug("wake up on drain"); pr_debug("wake up on drain\n"); prtd->drain_ready = 1; wake_up(&prtd->drain_wait); atomic_set(&prtd->drain, 0); } } else if ((bytes_available == cstream->runtime->fragment_size) && atomic_read(&prtd->drain)) { prtd->last_buffer = 1; msm_compr_send_buffer(prtd); prtd->last_buffer = 0; } else msm_compr_send_buffer(prtd); Loading @@ -230,12 +278,24 @@ static void compr_event_handler(uint32_t opcode, break; case ASM_DATA_EVENT_RENDERED_EOS: pr_debug("ASM_DATA_CMDRSP_EOS\n"); if (atomic_read(&prtd->eos)) { spin_lock(&prtd->lock); if (atomic_read(&prtd->eos) && !prtd->gapless_state.set_next_stream_id) { pr_debug("ASM_DATA_CMDRSP_EOS wake up\n"); prtd->cmd_ack = 1; wake_up(&prtd->eos_wait); } atomic_set(&prtd->eos, 0); stream_id = ac->stream_id^1; /*prev stream */ if (prtd->gapless_state.set_next_stream_id && prtd->gapless_state.stream_opened[stream_id]) { q6asm_stream_cmd_nowait(prtd->audio_client, CMD_CLOSE, stream_id); atomic_set(&prtd->close, 1); prtd->gapless_state.stream_opened[stream_id] = 0; prtd->gapless_state.set_next_stream_id = false; } spin_unlock(&prtd->lock); break; case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: { Loading Loading @@ -271,6 +331,21 @@ static void compr_event_handler(uint32_t opcode, prtd->cmd_ack = 1; wake_up(&prtd->flush_wait); break; case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: pr_debug("ASM_DATA_CMD_REMOVE_INITIAL_SILENCE\n"); break; case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: pr_debug("ASM_DATA_CMD_REMOVE_TRAILING_SILENCE\n"); break; case ASM_STREAM_CMD_CLOSE: pr_debug("ASM_DATA_CMD_CLOSE\n"); if (atomic_read(&prtd->close) && atomic_read(&prtd->wait_on_close)) { prtd->cmd_ack = 1; wake_up(&prtd->close_wait); } atomic_set(&prtd->close, 0); break; default: break; } Loading Loading @@ -302,7 +377,8 @@ static void populate_codec_list(struct msm_compr_audio *prtd) prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; } static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream) static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, int stream_id) { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; Loading @@ -319,8 +395,8 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream) aac_cfg.format = 0x03; aac_cfg.ch_cfg = prtd->num_channels; aac_cfg.sample_rate = prtd->sample_rate; ret = q6asm_media_format_block_aac(prtd->audio_client, &aac_cfg); ret = q6asm_stream_media_format_block_aac(prtd->audio_client, &aac_cfg, stream_id); if (ret < 0) pr_err("%s: CMD Format block failed\n", __func__); break; Loading @@ -338,6 +414,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; uint16_t bits_per_sample = 16; int dir = IN, ret = 0; struct audio_client *ac = prtd->audio_client; struct asm_softpause_params softpause = { .enable = SOFT_PAUSE_ENABLE, .period = SOFT_PAUSE_PERIOD, Loading @@ -351,16 +428,18 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) }; pr_debug("%s\n", __func__); ret = q6asm_open_write_v2(prtd->audio_client, prtd->codec, bits_per_sample); ret = q6asm_stream_open_write_v2(ac, prtd->codec, bits_per_sample, ac->stream_id, true/*gapless*/); if (ret < 0) { pr_err("%s: Session out open failed\n", __func__); return -ENOMEM; } prtd->gapless_state.stream_opened[ac->stream_id] = 1; pr_debug("%s be_id %d\n", __func__, soc_prtd->dai_link->be_id); msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, prtd->audio_client->perf_mode, ac->perf_mode, prtd->session_id, SNDRV_PCM_STREAM_PLAYBACK); Loading @@ -368,19 +447,17 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) if (ret < 0) pr_err("%s : Set Volume failed : %d", __func__, ret); ret = q6asm_set_softpause(prtd->audio_client, &softpause); ret = q6asm_set_softpause(ac, &softpause); if (ret < 0) pr_err("%s: Send SoftPause Param failed ret=%d\n", __func__, ret); ret = q6asm_set_softvolume(prtd->audio_client, &softvol); ret = q6asm_set_softvolume(ac, &softvol); if (ret < 0) pr_err("%s: Send SoftVolume Param failed ret=%d\n", __func__, ret); ret = q6asm_set_io_mode(prtd->audio_client, (COMPRESSED_IO | ASYNC_IO_MODE)); ret = q6asm_set_io_mode(ac, (COMPRESSED_IO | ASYNC_IO_MODE)); if (ret < 0) { pr_err("%s: Set IO mode failed\n", __func__); return -EINVAL; Loading @@ -391,8 +468,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) pr_debug("allocate %d buffers each of size %d\n", runtime->fragments, runtime->fragment_size); ret = q6asm_audio_client_buf_alloc_contiguous(dir, prtd->audio_client, ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac, runtime->fragment_size, runtime->fragments); if (ret < 0) { Loading @@ -404,12 +480,11 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) prtd->copied_total = 0; prtd->app_pointer = 0; prtd->bytes_received = 0; prtd->buffer = prtd->audio_client->port[dir].buf[0].data; prtd->buffer_paddr = prtd->audio_client->port[dir].buf[0].phys; prtd->buffer = ac->port[dir].buf[0].data; prtd->buffer_paddr = ac->port[dir].buf[0].phys; prtd->buffer_size = runtime->fragments * runtime->fragment_size; ret = msm_compr_send_media_format_block(cstream); ret = msm_compr_send_media_format_block(cstream, ac->stream_id); if (ret < 0) { pr_err("%s, failed to send media format block\n", __func__); } Loading Loading @@ -452,6 +527,10 @@ static int msm_compr_open(struct snd_compr_stream *cstream) prtd->sample_rate = 44100; prtd->num_channels = 2; prtd->drain_ready = 0; prtd->last_buffer = 0; prtd->first_buffer = 1; prtd->partial_drain_delay = 0; memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state)); spin_lock_init(&prtd->lock); Loading @@ -459,10 +538,13 @@ static int msm_compr_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 0); atomic_set(&prtd->close, 0); atomic_set(&prtd->wait_on_close, 0); init_waitqueue_head(&prtd->eos_wait); init_waitqueue_head(&prtd->drain_wait); init_waitqueue_head(&prtd->flush_wait); init_waitqueue_head(&prtd->close_wait); runtime->private_data = prtd; populate_codec_list(prtd); Loading @@ -488,7 +570,9 @@ static int msm_compr_free(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(soc_prtd->platform); int dir = IN, ret = 0; struct audio_client *ac = prtd->audio_client; int dir = IN, ret = 0, stream_id; unsigned long flags; pr_debug("%s\n", __func__); pdata->cstream[soc_prtd->dai_link->be_id] = NULL; Loading @@ -511,13 +595,34 @@ static int msm_compr_free(struct snd_compr_stream *cstream) if (!ret) pr_err("%s: CMD_EOS failed\n", __func__); } if (atomic_read(&prtd->close)) { prtd->cmd_ack = 0; atomic_set(&prtd->wait_on_close, 1); ret = wait_event_timeout(prtd->close_wait, prtd->cmd_ack, 5 * HZ); if (!ret) pr_err("%s: CMD_CLOSE failed\n", __func__); } q6asm_cmd(prtd->audio_client, CMD_CLOSE); spin_lock_irqsave(&prtd->lock, flags); stream_id = ac->stream_id; if (prtd->gapless_state.stream_opened[stream_id^1]) { spin_unlock_irqrestore(&prtd->lock, flags); q6asm_stream_cmd(ac, CMD_CLOSE, stream_id^1); spin_lock_irqsave(&prtd->lock, flags); } if (prtd->gapless_state.stream_opened[stream_id]) { spin_unlock_irqrestore(&prtd->lock, flags); q6asm_stream_cmd(ac, CMD_CLOSE, stream_id); spin_lock_irqsave(&prtd->lock, flags); } spin_unlock_irqrestore(&prtd->lock, flags); q6asm_audio_client_buf_free_contiguous(dir, prtd->audio_client); /* client buf alloc was with stream id 0, so free with the same */ ac->stream_id = 0; q6asm_audio_client_buf_free_contiguous(dir, ac); q6asm_audio_client_free(prtd->audio_client); q6asm_audio_client_free(ac); kfree(prtd); Loading @@ -530,7 +635,7 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; int ret = 0; int ret = 0, frame_sz = 0, delay_time_ms = 0; pr_debug("%s\n", __func__); Loading Loading @@ -570,12 +675,14 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, case SND_AUDIOCODEC_MP3: { pr_debug("SND_AUDIOCODEC_MP3\n"); prtd->codec = FORMAT_MP3; frame_sz = MP3_OUTPUT_FRAME_SZ; break; } case SND_AUDIOCODEC_AAC: { pr_debug("SND_AUDIOCODEC_AAC\n"); prtd->codec = FORMAT_MPEG4_AAC; frame_sz = AAC_OUTPUT_FRAME_SZ; break; } Loading @@ -584,11 +691,40 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, return -EINVAL; } delay_time_ms = ((DSP_NUM_OUTPUT_FRAME_BUFFERED * frame_sz * 1000) / prtd->sample_rate) + DSP_PP_BUFFERING_IN_MSEC; delay_time_ms = delay_time_ms > PARTIAL_DRAIN_ACK_EARLY_BY_MSEC ? delay_time_ms - PARTIAL_DRAIN_ACK_EARLY_BY_MSEC : 0; prtd->partial_drain_delay = delay_time_ms; ret = msm_compr_configure_dsp(cstream); return ret; } static int msm_compr_drain_buffer(struct msm_compr_audio *prtd, unsigned long *flags) { int rc = 0; atomic_set(&prtd->drain, 1); prtd->drain_ready = 0; spin_unlock_irqrestore(&prtd->lock, *flags); pr_debug("%s: wait for buffer to be drained\n", __func__); rc = wait_event_interruptible(prtd->drain_wait, prtd->drain_ready || prtd->cmd_interrupt || atomic_read(&prtd->xrun)); pr_debug("%s: out of buffer drain wait\n", __func__); spin_lock_irqsave(&prtd->lock, *flags); if (prtd->cmd_interrupt) { pr_debug("%s: buffer drain interrupted by flush)\n", __func__); rc = -EINTR; prtd->cmd_interrupt = 0; } return rc; } static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) { struct snd_compr_runtime *runtime = cstream->runtime; Loading @@ -597,9 +733,11 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(rtd->platform); uint32_t *volume = pdata->volume[rtd->dai_link->be_id]; struct audio_client *ac = prtd->audio_client; int rc = 0; int bytes_to_write; unsigned long flags; int stream_id; if (cstream->direction != SND_COMPRESS_PLAYBACK) { pr_err("%s: Unsupported stream type\n", __func__); Loading @@ -621,21 +759,37 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); spin_lock_irqsave(&prtd->lock, flags); stream_id = ac->stream_id; if (prtd->gapless_state.set_next_stream_id && prtd->first_buffer) { /* * Stream just switched for gapless, no buffers sent. * So seek needs to be applied to previous stream */ pr_debug("Seek previous stream as next stream hasn't started\n"); stream_id = stream_id^1; ac->stream_id = stream_id; prtd->first_buffer = 0; } atomic_set(&prtd->start, 0); if (atomic_read(&prtd->eos)) { pr_debug("%s: interrupt drain and eos wait queues", __func__); pr_debug("%s: interrupt eos wait queues", __func__); prtd->cmd_interrupt = 1; prtd->drain_ready = 1; wake_up(&prtd->drain_wait); wake_up(&prtd->eos_wait); atomic_set(&prtd->eos, 0); } if (atomic_read(&prtd->drain)) { pr_debug("%s: interrupt drain wait queues", __func__); prtd->cmd_interrupt = 1; prtd->drain_ready = 1; wake_up(&prtd->drain_wait); atomic_set(&prtd->drain, 0); } prtd->last_buffer = 0; pr_debug("issue CMD_FLUSH\n"); prtd->cmd_ack = 0; spin_unlock_irqrestore(&prtd->lock, flags); rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH); rc = q6asm_stream_cmd(prtd->audio_client, CMD_FLUSH, stream_id); if (rc < 0) { pr_err("%s: flush cmd failed rc=%d\n", __func__, rc); Loading Loading @@ -682,15 +836,15 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) spin_unlock_irqrestore(&prtd->lock, flags); break; } atomic_set(&prtd->eos, 1); if (prtd->bytes_received > prtd->copied_total) { atomic_set(&prtd->drain, 1); prtd->drain_ready = 0; spin_unlock_irqrestore(&prtd->lock, flags); pr_debug("%s: wait till all the data is sent to dsp\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); if (rc || !atomic_read(&prtd->start)) { rc = -EINTR; spin_unlock_irqrestore(&prtd->lock, flags); break; } /* * FIXME: Bug. * Write(32767) Loading @@ -699,13 +853,8 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) * sol1 : if (prtd->copied_total) then wait? * sol2 : (prtd->cmd_interrupt || prtd->drain_ready || atomic_read(xrun) */ rc = wait_event_interruptible(prtd->drain_wait, prtd->cmd_interrupt || prtd->drain_ready || atomic_read(&prtd->xrun)); spin_lock_irqsave(&prtd->lock, flags); if (!prtd->cmd_interrupt) { bytes_to_write = prtd->bytes_received - prtd->copied_total; bytes_to_write = prtd->bytes_received - prtd->copied_total; WARN(bytes_to_write > runtime->fragment_size, "last write %d cannot be > than fragment_size", bytes_to_write); Loading @@ -714,32 +863,99 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: send %d partial bytes at the end", __func__, bytes_to_write); atomic_set(&prtd->xrun, 0); prtd->last_buffer = 1; msm_compr_send_buffer(prtd); } } if ((cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) && (prtd->gapless_state.set_next_stream_id)) { /* wait for the last buffer to be returned */ if (prtd->last_buffer) { pr_debug("%s: last buffer drain\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); if (rc) { spin_unlock_irqrestore(&prtd->lock, flags); break; } } /* send EOS */ prtd->cmd_ack = 0; atomic_set(&prtd->eos, 1); q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); pr_info("PARTIAL DRAIN, do not wait for EOS ack\n"); /* send a zero length buffer */ atomic_set(&prtd->xrun, 0); msm_compr_send_buffer(prtd); /* wait for the zero length buffer to be returned */ pr_debug("%s: zero length buffer drain\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); if (rc) { spin_unlock_irqrestore(&prtd->lock, flags); break; } if (!atomic_read(&prtd->start) || prtd->cmd_interrupt) { pr_err("%s: stream is not started\n", __func__); /* sleep for additional duration partial drain */ atomic_set(&prtd->drain, 1); prtd->drain_ready = 0; pr_debug("%s, additional sleep: %d\n", __func__, prtd->partial_drain_delay); spin_unlock_irqrestore(&prtd->lock, flags); rc = wait_event_timeout(prtd->drain_wait, prtd->drain_ready || prtd->cmd_interrupt, msecs_to_jiffies(prtd->partial_drain_delay)); pr_debug("%s: out of additional wait for low sample rate\n", __func__); spin_lock_irqsave(&prtd->lock, flags); if (prtd->cmd_interrupt) { pr_debug("%s: additional wait interrupted by flush)\n", __func__); rc = -EINTR; prtd->cmd_interrupt = 0; spin_unlock_irqrestore(&prtd->lock, flags); break; } /* move to next stream and reset vars */ pr_debug("%s: Moving to next stream in gapless\n", __func__); ac->stream_id ^= 1; prtd->byte_offset = 0; prtd->app_pointer = 0; prtd->first_buffer = 1; prtd->last_buffer = 0; /* Don't reset these as these vars map to total_bytes_transferred and total_bytes_available directly, only total_bytes_transferred will be updated in the next avail() ioctl prtd->copied_total = 0; prtd->bytes_received = 0; */ atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 1); pr_debug("%s: issue CMD_RUN", __func__); q6asm_run_nowait(prtd->audio_client, 0, 0, 0); spin_unlock_irqrestore(&prtd->lock, flags); break; } /* moving to next stream failed, so reset the gapless state set next stream id for the same session so that the same stream can be used for gapless playback */ prtd->gapless_state.set_next_stream_id = false; pr_debug("%s: CMD_EOS\n", __func__); prtd->cmd_ack = 0; atomic_set(&prtd->eos, 1); q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); spin_unlock_irqrestore(&prtd->lock, flags); /* if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) { pr_err("PARTIAL DRAIN, do not wait for EOS ack"); break; } */ /* Wait indefinitely for DRAIN. Flush can also signal this*/ rc = wait_event_interruptible(prtd->eos_wait, Loading @@ -756,6 +972,11 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) /*FIXME : what if a flush comes while PC is here */ if (rc == 0 && (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN)) { /* * Failed to open second stream in DSP for gapless * so prepare the current stream in session * for gapless playback */ spin_lock_irqsave(&prtd->lock, flags); pr_debug("%s: issue CMD_PAUSE ", __func__); q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); Loading @@ -767,27 +988,55 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) prtd->cmd_ack, 1 * HZ / 4); spin_lock_irqsave(&prtd->lock, flags); prtd->byte_offset = 0; prtd->app_pointer = 0; /* Don't reset these as these vars map to total_bytes_transferred and total_bytes_available directly, only total_bytes_transferred will be updated in the next avail() ioctl /* Don't reset these as these vars map to total_bytes_transferred and total_bytes_available directly, only total_bytes_transferred will be updated in the next avail() ioctl prtd->copied_total = 0; prtd->bytes_received = 0; */ prtd->byte_offset = 0; prtd->app_pointer = 0; prtd->first_buffer = 1; prtd->last_buffer = 0; atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 1); pr_debug("%s: issue CMD_RESUME", __func__); q6asm_run_nowait(prtd->audio_client, 0, 0, 0); spin_unlock_irqrestore(&prtd->lock, flags); } pr_debug("%s: out of drain", __func__); prtd->cmd_interrupt = 0; break; case SND_COMPR_TRIGGER_NEXT_TRACK: pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__); spin_lock_irqsave(&prtd->lock, flags); rc = 0; stream_id = ac->stream_id^1; /*next stream in gapless*/ if (prtd->gapless_state.stream_opened[stream_id]) { pr_debug("next session is already in opened state\n"); spin_unlock_irqrestore(&prtd->lock, flags); break; } spin_unlock_irqrestore(&prtd->lock, flags); rc = q6asm_stream_open_write_v2(prtd->audio_client, prtd->codec, 16, stream_id, true /*gapless*/); if (rc < 0) { pr_err("%s: Session out open failed for gapless\n", __func__); break; } rc = msm_compr_send_media_format_block(cstream, stream_id); if (rc < 0) { pr_err("%s, failed to send media format block\n", __func__); break; } spin_lock_irqsave(&prtd->lock, flags); prtd->gapless_state.stream_opened[stream_id] = 1; prtd->gapless_state.set_next_stream_id = true; spin_unlock_irqrestore(&prtd->lock, flags); break; } Loading @@ -801,7 +1050,7 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream, struct msm_compr_audio *prtd = runtime->private_data; struct snd_compr_tstamp tstamp; uint64_t timestamp = 0; int rc = 0; int rc = 0, first_buffer; unsigned long flags; pr_debug("%s\n", __func__); Loading @@ -811,13 +1060,14 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream, tstamp.sampling_rate = prtd->sample_rate; tstamp.byte_offset = prtd->byte_offset; tstamp.copied_total = prtd->copied_total; first_buffer = prtd->first_buffer; spin_unlock_irqrestore(&prtd->lock, flags); /* Query timestamp from DSP if some data is with it. This prevents timeouts. */ if (prtd->copied_total) { if (!first_buffer) { rc = q6asm_get_session_time(prtd->audio_client, ×tamp); if (rc < 0) { pr_err("%s: Get Session Time return value =%lld\n", Loading Loading @@ -993,15 +1243,24 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, static int msm_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { struct msm_compr_audio *prtd; struct audio_client *ac; pr_debug("%s\n", __func__); if (!metadata || !cstream) return -EINVAL; prtd = cstream->runtime->private_data; if (!prtd && !prtd->audio_client) return -EINVAL; ac = prtd->audio_client; if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) { pr_debug("%s, got encoder padding %u", __func__, metadata->value[0]); prtd->gapless_state.trailing_samples_drop = metadata->value[0]; } else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY) { pr_debug("%s, got encoder delay %u", __func__, metadata->value[0]); prtd->gapless_state.initial_samples_drop = metadata->value[0]; } return 0; Loading Loading
sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +339 −80 Original line number Diff line number Diff line Loading @@ -44,6 +44,12 @@ #include "msm-pcm-routing-v2.h" #include "audio_ocmem.h" #define DSP_PP_BUFFERING_IN_MSEC 25 #define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150 #define MP3_OUTPUT_FRAME_SZ 1152 #define AAC_OUTPUT_FRAME_SZ 1024 #define DSP_NUM_OUTPUT_FRAME_BUFFERED 2 /* Default values used if user space does not set */ #define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) #define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) Loading @@ -54,6 +60,13 @@ const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0, COMPRESSED_LR_VOL_MAX_STEPS); struct msm_compr_gapless_state { bool set_next_stream_id; int32_t stream_opened[2]; uint32_t initial_samples_drop; uint32_t trailing_samples_drop; }; struct msm_compr_pdata { atomic_t audio_ocmem_req; struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; Loading @@ -75,6 +88,9 @@ struct msm_compr_audio { uint32_t byte_offset; uint32_t copied_total; uint32_t bytes_received; int32_t first_buffer; int32_t last_buffer; int32_t partial_drain_delay; uint16_t session_id; Loading @@ -85,14 +101,19 @@ struct msm_compr_audio { uint32_t cmd_interrupt; uint32_t drain_ready; struct msm_compr_gapless_state gapless_state; atomic_t start; atomic_t eos; atomic_t drain; atomic_t xrun; atomic_t close; atomic_t wait_on_close; wait_queue_head_t eos_wait; wait_queue_head_t drain_wait; wait_queue_head_t flush_wait; wait_queue_head_t close_wait; spinlock_t lock; }; Loading Loading @@ -143,6 +164,17 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) pr_debug("%s: bytes_received = %d copied_total = %d\n", __func__, prtd->bytes_received, prtd->copied_total); /* * FIXME: Initial and trailing silence removal API call to DSP results * to a glitch during the stream transition for gapless playback. * Add this when the issue is fixed from DSP. */ /* if (prtd->first_buffer) q6asm_send_meta_data(prtd->audio_client, prtd->gapless_state.initial_samples_drop, prtd->gapless_state.trailing_samples_drop); */ buffer_length = prtd->codec_param.buffer.fragment_size; bytes_available = prtd->bytes_received - prtd->copied_total; if (bytes_available < prtd->codec_param.buffer.fragment_size) Loading @@ -153,7 +185,10 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) pr_debug("wrap around situation, send partial data %d now", buffer_length); } if (buffer_length) param.paddr = prtd->buffer_paddr + prtd->byte_offset; else param.paddr = prtd->buffer_paddr; WARN(param.paddr % 32 != 0, "param.paddr %lx not multiple of 32", param.paddr); param.len = buffer_length; Loading @@ -162,11 +197,16 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd) param.flags = NO_TIMESTAMP; param.uid = buffer_length; param.metadata_len = 0; param.last_buffer = prtd->last_buffer; pr_debug("%s: sending %d bytes to DSP byte_offset = %d\n", __func__, buffer_length, prtd->byte_offset); if (q6asm_async_write(prtd->audio_client, ¶m) < 0) if (q6asm_async_write(prtd->audio_client, ¶m) < 0) { pr_err("%s:q6asm_async_write failed\n", __func__); } else { if (prtd->first_buffer) prtd->first_buffer = 0; } return 0; } Loading @@ -176,9 +216,10 @@ static void compr_event_handler(uint32_t opcode, { struct msm_compr_audio *prtd = priv; struct snd_compr_stream *cstream = prtd->cstream; struct audio_client *ac = prtd->audio_client; uint32_t chan_mode = 0; uint32_t sample_rate = 0; int bytes_available; int bytes_available, stream_id; pr_debug("%s opcode =%08x\n", __func__, opcode); switch (opcode) { Loading Loading @@ -217,12 +258,19 @@ static void compr_event_handler(uint32_t opcode, pr_debug("WRITE_DONE Insufficient data to send. break out\n"); atomic_set(&prtd->xrun, 1); if (prtd->last_buffer) prtd->last_buffer = 0; if (atomic_read(&prtd->drain)) { pr_debug("wake up on drain"); pr_debug("wake up on drain\n"); prtd->drain_ready = 1; wake_up(&prtd->drain_wait); atomic_set(&prtd->drain, 0); } } else if ((bytes_available == cstream->runtime->fragment_size) && atomic_read(&prtd->drain)) { prtd->last_buffer = 1; msm_compr_send_buffer(prtd); prtd->last_buffer = 0; } else msm_compr_send_buffer(prtd); Loading @@ -230,12 +278,24 @@ static void compr_event_handler(uint32_t opcode, break; case ASM_DATA_EVENT_RENDERED_EOS: pr_debug("ASM_DATA_CMDRSP_EOS\n"); if (atomic_read(&prtd->eos)) { spin_lock(&prtd->lock); if (atomic_read(&prtd->eos) && !prtd->gapless_state.set_next_stream_id) { pr_debug("ASM_DATA_CMDRSP_EOS wake up\n"); prtd->cmd_ack = 1; wake_up(&prtd->eos_wait); } atomic_set(&prtd->eos, 0); stream_id = ac->stream_id^1; /*prev stream */ if (prtd->gapless_state.set_next_stream_id && prtd->gapless_state.stream_opened[stream_id]) { q6asm_stream_cmd_nowait(prtd->audio_client, CMD_CLOSE, stream_id); atomic_set(&prtd->close, 1); prtd->gapless_state.stream_opened[stream_id] = 0; prtd->gapless_state.set_next_stream_id = false; } spin_unlock(&prtd->lock); break; case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: { Loading Loading @@ -271,6 +331,21 @@ static void compr_event_handler(uint32_t opcode, prtd->cmd_ack = 1; wake_up(&prtd->flush_wait); break; case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: pr_debug("ASM_DATA_CMD_REMOVE_INITIAL_SILENCE\n"); break; case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: pr_debug("ASM_DATA_CMD_REMOVE_TRAILING_SILENCE\n"); break; case ASM_STREAM_CMD_CLOSE: pr_debug("ASM_DATA_CMD_CLOSE\n"); if (atomic_read(&prtd->close) && atomic_read(&prtd->wait_on_close)) { prtd->cmd_ack = 1; wake_up(&prtd->close_wait); } atomic_set(&prtd->close, 0); break; default: break; } Loading Loading @@ -302,7 +377,8 @@ static void populate_codec_list(struct msm_compr_audio *prtd) prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; } static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream) static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, int stream_id) { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; Loading @@ -319,8 +395,8 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream) aac_cfg.format = 0x03; aac_cfg.ch_cfg = prtd->num_channels; aac_cfg.sample_rate = prtd->sample_rate; ret = q6asm_media_format_block_aac(prtd->audio_client, &aac_cfg); ret = q6asm_stream_media_format_block_aac(prtd->audio_client, &aac_cfg, stream_id); if (ret < 0) pr_err("%s: CMD Format block failed\n", __func__); break; Loading @@ -338,6 +414,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; uint16_t bits_per_sample = 16; int dir = IN, ret = 0; struct audio_client *ac = prtd->audio_client; struct asm_softpause_params softpause = { .enable = SOFT_PAUSE_ENABLE, .period = SOFT_PAUSE_PERIOD, Loading @@ -351,16 +428,18 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) }; pr_debug("%s\n", __func__); ret = q6asm_open_write_v2(prtd->audio_client, prtd->codec, bits_per_sample); ret = q6asm_stream_open_write_v2(ac, prtd->codec, bits_per_sample, ac->stream_id, true/*gapless*/); if (ret < 0) { pr_err("%s: Session out open failed\n", __func__); return -ENOMEM; } prtd->gapless_state.stream_opened[ac->stream_id] = 1; pr_debug("%s be_id %d\n", __func__, soc_prtd->dai_link->be_id); msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id, prtd->audio_client->perf_mode, ac->perf_mode, prtd->session_id, SNDRV_PCM_STREAM_PLAYBACK); Loading @@ -368,19 +447,17 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) if (ret < 0) pr_err("%s : Set Volume failed : %d", __func__, ret); ret = q6asm_set_softpause(prtd->audio_client, &softpause); ret = q6asm_set_softpause(ac, &softpause); if (ret < 0) pr_err("%s: Send SoftPause Param failed ret=%d\n", __func__, ret); ret = q6asm_set_softvolume(prtd->audio_client, &softvol); ret = q6asm_set_softvolume(ac, &softvol); if (ret < 0) pr_err("%s: Send SoftVolume Param failed ret=%d\n", __func__, ret); ret = q6asm_set_io_mode(prtd->audio_client, (COMPRESSED_IO | ASYNC_IO_MODE)); ret = q6asm_set_io_mode(ac, (COMPRESSED_IO | ASYNC_IO_MODE)); if (ret < 0) { pr_err("%s: Set IO mode failed\n", __func__); return -EINVAL; Loading @@ -391,8 +468,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) pr_debug("allocate %d buffers each of size %d\n", runtime->fragments, runtime->fragment_size); ret = q6asm_audio_client_buf_alloc_contiguous(dir, prtd->audio_client, ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac, runtime->fragment_size, runtime->fragments); if (ret < 0) { Loading @@ -404,12 +480,11 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) prtd->copied_total = 0; prtd->app_pointer = 0; prtd->bytes_received = 0; prtd->buffer = prtd->audio_client->port[dir].buf[0].data; prtd->buffer_paddr = prtd->audio_client->port[dir].buf[0].phys; prtd->buffer = ac->port[dir].buf[0].data; prtd->buffer_paddr = ac->port[dir].buf[0].phys; prtd->buffer_size = runtime->fragments * runtime->fragment_size; ret = msm_compr_send_media_format_block(cstream); ret = msm_compr_send_media_format_block(cstream, ac->stream_id); if (ret < 0) { pr_err("%s, failed to send media format block\n", __func__); } Loading Loading @@ -452,6 +527,10 @@ static int msm_compr_open(struct snd_compr_stream *cstream) prtd->sample_rate = 44100; prtd->num_channels = 2; prtd->drain_ready = 0; prtd->last_buffer = 0; prtd->first_buffer = 1; prtd->partial_drain_delay = 0; memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state)); spin_lock_init(&prtd->lock); Loading @@ -459,10 +538,13 @@ static int msm_compr_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 0); atomic_set(&prtd->close, 0); atomic_set(&prtd->wait_on_close, 0); init_waitqueue_head(&prtd->eos_wait); init_waitqueue_head(&prtd->drain_wait); init_waitqueue_head(&prtd->flush_wait); init_waitqueue_head(&prtd->close_wait); runtime->private_data = prtd; populate_codec_list(prtd); Loading @@ -488,7 +570,9 @@ static int msm_compr_free(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(soc_prtd->platform); int dir = IN, ret = 0; struct audio_client *ac = prtd->audio_client; int dir = IN, ret = 0, stream_id; unsigned long flags; pr_debug("%s\n", __func__); pdata->cstream[soc_prtd->dai_link->be_id] = NULL; Loading @@ -511,13 +595,34 @@ static int msm_compr_free(struct snd_compr_stream *cstream) if (!ret) pr_err("%s: CMD_EOS failed\n", __func__); } if (atomic_read(&prtd->close)) { prtd->cmd_ack = 0; atomic_set(&prtd->wait_on_close, 1); ret = wait_event_timeout(prtd->close_wait, prtd->cmd_ack, 5 * HZ); if (!ret) pr_err("%s: CMD_CLOSE failed\n", __func__); } q6asm_cmd(prtd->audio_client, CMD_CLOSE); spin_lock_irqsave(&prtd->lock, flags); stream_id = ac->stream_id; if (prtd->gapless_state.stream_opened[stream_id^1]) { spin_unlock_irqrestore(&prtd->lock, flags); q6asm_stream_cmd(ac, CMD_CLOSE, stream_id^1); spin_lock_irqsave(&prtd->lock, flags); } if (prtd->gapless_state.stream_opened[stream_id]) { spin_unlock_irqrestore(&prtd->lock, flags); q6asm_stream_cmd(ac, CMD_CLOSE, stream_id); spin_lock_irqsave(&prtd->lock, flags); } spin_unlock_irqrestore(&prtd->lock, flags); q6asm_audio_client_buf_free_contiguous(dir, prtd->audio_client); /* client buf alloc was with stream id 0, so free with the same */ ac->stream_id = 0; q6asm_audio_client_buf_free_contiguous(dir, ac); q6asm_audio_client_free(prtd->audio_client); q6asm_audio_client_free(ac); kfree(prtd); Loading @@ -530,7 +635,7 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, { struct snd_compr_runtime *runtime = cstream->runtime; struct msm_compr_audio *prtd = runtime->private_data; int ret = 0; int ret = 0, frame_sz = 0, delay_time_ms = 0; pr_debug("%s\n", __func__); Loading Loading @@ -570,12 +675,14 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, case SND_AUDIOCODEC_MP3: { pr_debug("SND_AUDIOCODEC_MP3\n"); prtd->codec = FORMAT_MP3; frame_sz = MP3_OUTPUT_FRAME_SZ; break; } case SND_AUDIOCODEC_AAC: { pr_debug("SND_AUDIOCODEC_AAC\n"); prtd->codec = FORMAT_MPEG4_AAC; frame_sz = AAC_OUTPUT_FRAME_SZ; break; } Loading @@ -584,11 +691,40 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, return -EINVAL; } delay_time_ms = ((DSP_NUM_OUTPUT_FRAME_BUFFERED * frame_sz * 1000) / prtd->sample_rate) + DSP_PP_BUFFERING_IN_MSEC; delay_time_ms = delay_time_ms > PARTIAL_DRAIN_ACK_EARLY_BY_MSEC ? delay_time_ms - PARTIAL_DRAIN_ACK_EARLY_BY_MSEC : 0; prtd->partial_drain_delay = delay_time_ms; ret = msm_compr_configure_dsp(cstream); return ret; } static int msm_compr_drain_buffer(struct msm_compr_audio *prtd, unsigned long *flags) { int rc = 0; atomic_set(&prtd->drain, 1); prtd->drain_ready = 0; spin_unlock_irqrestore(&prtd->lock, *flags); pr_debug("%s: wait for buffer to be drained\n", __func__); rc = wait_event_interruptible(prtd->drain_wait, prtd->drain_ready || prtd->cmd_interrupt || atomic_read(&prtd->xrun)); pr_debug("%s: out of buffer drain wait\n", __func__); spin_lock_irqsave(&prtd->lock, *flags); if (prtd->cmd_interrupt) { pr_debug("%s: buffer drain interrupted by flush)\n", __func__); rc = -EINTR; prtd->cmd_interrupt = 0; } return rc; } static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) { struct snd_compr_runtime *runtime = cstream->runtime; Loading @@ -597,9 +733,11 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(rtd->platform); uint32_t *volume = pdata->volume[rtd->dai_link->be_id]; struct audio_client *ac = prtd->audio_client; int rc = 0; int bytes_to_write; unsigned long flags; int stream_id; if (cstream->direction != SND_COMPRESS_PLAYBACK) { pr_err("%s: Unsupported stream type\n", __func__); Loading @@ -621,21 +759,37 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); spin_lock_irqsave(&prtd->lock, flags); stream_id = ac->stream_id; if (prtd->gapless_state.set_next_stream_id && prtd->first_buffer) { /* * Stream just switched for gapless, no buffers sent. * So seek needs to be applied to previous stream */ pr_debug("Seek previous stream as next stream hasn't started\n"); stream_id = stream_id^1; ac->stream_id = stream_id; prtd->first_buffer = 0; } atomic_set(&prtd->start, 0); if (atomic_read(&prtd->eos)) { pr_debug("%s: interrupt drain and eos wait queues", __func__); pr_debug("%s: interrupt eos wait queues", __func__); prtd->cmd_interrupt = 1; prtd->drain_ready = 1; wake_up(&prtd->drain_wait); wake_up(&prtd->eos_wait); atomic_set(&prtd->eos, 0); } if (atomic_read(&prtd->drain)) { pr_debug("%s: interrupt drain wait queues", __func__); prtd->cmd_interrupt = 1; prtd->drain_ready = 1; wake_up(&prtd->drain_wait); atomic_set(&prtd->drain, 0); } prtd->last_buffer = 0; pr_debug("issue CMD_FLUSH\n"); prtd->cmd_ack = 0; spin_unlock_irqrestore(&prtd->lock, flags); rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH); rc = q6asm_stream_cmd(prtd->audio_client, CMD_FLUSH, stream_id); if (rc < 0) { pr_err("%s: flush cmd failed rc=%d\n", __func__, rc); Loading Loading @@ -682,15 +836,15 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) spin_unlock_irqrestore(&prtd->lock, flags); break; } atomic_set(&prtd->eos, 1); if (prtd->bytes_received > prtd->copied_total) { atomic_set(&prtd->drain, 1); prtd->drain_ready = 0; spin_unlock_irqrestore(&prtd->lock, flags); pr_debug("%s: wait till all the data is sent to dsp\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); if (rc || !atomic_read(&prtd->start)) { rc = -EINTR; spin_unlock_irqrestore(&prtd->lock, flags); break; } /* * FIXME: Bug. * Write(32767) Loading @@ -699,13 +853,8 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) * sol1 : if (prtd->copied_total) then wait? * sol2 : (prtd->cmd_interrupt || prtd->drain_ready || atomic_read(xrun) */ rc = wait_event_interruptible(prtd->drain_wait, prtd->cmd_interrupt || prtd->drain_ready || atomic_read(&prtd->xrun)); spin_lock_irqsave(&prtd->lock, flags); if (!prtd->cmd_interrupt) { bytes_to_write = prtd->bytes_received - prtd->copied_total; bytes_to_write = prtd->bytes_received - prtd->copied_total; WARN(bytes_to_write > runtime->fragment_size, "last write %d cannot be > than fragment_size", bytes_to_write); Loading @@ -714,32 +863,99 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: send %d partial bytes at the end", __func__, bytes_to_write); atomic_set(&prtd->xrun, 0); prtd->last_buffer = 1; msm_compr_send_buffer(prtd); } } if ((cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) && (prtd->gapless_state.set_next_stream_id)) { /* wait for the last buffer to be returned */ if (prtd->last_buffer) { pr_debug("%s: last buffer drain\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); if (rc) { spin_unlock_irqrestore(&prtd->lock, flags); break; } } /* send EOS */ prtd->cmd_ack = 0; atomic_set(&prtd->eos, 1); q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); pr_info("PARTIAL DRAIN, do not wait for EOS ack\n"); /* send a zero length buffer */ atomic_set(&prtd->xrun, 0); msm_compr_send_buffer(prtd); /* wait for the zero length buffer to be returned */ pr_debug("%s: zero length buffer drain\n", __func__); rc = msm_compr_drain_buffer(prtd, &flags); if (rc) { spin_unlock_irqrestore(&prtd->lock, flags); break; } if (!atomic_read(&prtd->start) || prtd->cmd_interrupt) { pr_err("%s: stream is not started\n", __func__); /* sleep for additional duration partial drain */ atomic_set(&prtd->drain, 1); prtd->drain_ready = 0; pr_debug("%s, additional sleep: %d\n", __func__, prtd->partial_drain_delay); spin_unlock_irqrestore(&prtd->lock, flags); rc = wait_event_timeout(prtd->drain_wait, prtd->drain_ready || prtd->cmd_interrupt, msecs_to_jiffies(prtd->partial_drain_delay)); pr_debug("%s: out of additional wait for low sample rate\n", __func__); spin_lock_irqsave(&prtd->lock, flags); if (prtd->cmd_interrupt) { pr_debug("%s: additional wait interrupted by flush)\n", __func__); rc = -EINTR; prtd->cmd_interrupt = 0; spin_unlock_irqrestore(&prtd->lock, flags); break; } /* move to next stream and reset vars */ pr_debug("%s: Moving to next stream in gapless\n", __func__); ac->stream_id ^= 1; prtd->byte_offset = 0; prtd->app_pointer = 0; prtd->first_buffer = 1; prtd->last_buffer = 0; /* Don't reset these as these vars map to total_bytes_transferred and total_bytes_available directly, only total_bytes_transferred will be updated in the next avail() ioctl prtd->copied_total = 0; prtd->bytes_received = 0; */ atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 1); pr_debug("%s: issue CMD_RUN", __func__); q6asm_run_nowait(prtd->audio_client, 0, 0, 0); spin_unlock_irqrestore(&prtd->lock, flags); break; } /* moving to next stream failed, so reset the gapless state set next stream id for the same session so that the same stream can be used for gapless playback */ prtd->gapless_state.set_next_stream_id = false; pr_debug("%s: CMD_EOS\n", __func__); prtd->cmd_ack = 0; atomic_set(&prtd->eos, 1); q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); spin_unlock_irqrestore(&prtd->lock, flags); /* if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) { pr_err("PARTIAL DRAIN, do not wait for EOS ack"); break; } */ /* Wait indefinitely for DRAIN. Flush can also signal this*/ rc = wait_event_interruptible(prtd->eos_wait, Loading @@ -756,6 +972,11 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) /*FIXME : what if a flush comes while PC is here */ if (rc == 0 && (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN)) { /* * Failed to open second stream in DSP for gapless * so prepare the current stream in session * for gapless playback */ spin_lock_irqsave(&prtd->lock, flags); pr_debug("%s: issue CMD_PAUSE ", __func__); q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); Loading @@ -767,27 +988,55 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) prtd->cmd_ack, 1 * HZ / 4); spin_lock_irqsave(&prtd->lock, flags); prtd->byte_offset = 0; prtd->app_pointer = 0; /* Don't reset these as these vars map to total_bytes_transferred and total_bytes_available directly, only total_bytes_transferred will be updated in the next avail() ioctl /* Don't reset these as these vars map to total_bytes_transferred and total_bytes_available directly, only total_bytes_transferred will be updated in the next avail() ioctl prtd->copied_total = 0; prtd->bytes_received = 0; */ prtd->byte_offset = 0; prtd->app_pointer = 0; prtd->first_buffer = 1; prtd->last_buffer = 0; atomic_set(&prtd->drain, 0); atomic_set(&prtd->xrun, 1); pr_debug("%s: issue CMD_RESUME", __func__); q6asm_run_nowait(prtd->audio_client, 0, 0, 0); spin_unlock_irqrestore(&prtd->lock, flags); } pr_debug("%s: out of drain", __func__); prtd->cmd_interrupt = 0; break; case SND_COMPR_TRIGGER_NEXT_TRACK: pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__); spin_lock_irqsave(&prtd->lock, flags); rc = 0; stream_id = ac->stream_id^1; /*next stream in gapless*/ if (prtd->gapless_state.stream_opened[stream_id]) { pr_debug("next session is already in opened state\n"); spin_unlock_irqrestore(&prtd->lock, flags); break; } spin_unlock_irqrestore(&prtd->lock, flags); rc = q6asm_stream_open_write_v2(prtd->audio_client, prtd->codec, 16, stream_id, true /*gapless*/); if (rc < 0) { pr_err("%s: Session out open failed for gapless\n", __func__); break; } rc = msm_compr_send_media_format_block(cstream, stream_id); if (rc < 0) { pr_err("%s, failed to send media format block\n", __func__); break; } spin_lock_irqsave(&prtd->lock, flags); prtd->gapless_state.stream_opened[stream_id] = 1; prtd->gapless_state.set_next_stream_id = true; spin_unlock_irqrestore(&prtd->lock, flags); break; } Loading @@ -801,7 +1050,7 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream, struct msm_compr_audio *prtd = runtime->private_data; struct snd_compr_tstamp tstamp; uint64_t timestamp = 0; int rc = 0; int rc = 0, first_buffer; unsigned long flags; pr_debug("%s\n", __func__); Loading @@ -811,13 +1060,14 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream, tstamp.sampling_rate = prtd->sample_rate; tstamp.byte_offset = prtd->byte_offset; tstamp.copied_total = prtd->copied_total; first_buffer = prtd->first_buffer; spin_unlock_irqrestore(&prtd->lock, flags); /* Query timestamp from DSP if some data is with it. This prevents timeouts. */ if (prtd->copied_total) { if (!first_buffer) { rc = q6asm_get_session_time(prtd->audio_client, ×tamp); if (rc < 0) { pr_err("%s: Get Session Time return value =%lld\n", Loading Loading @@ -993,15 +1243,24 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, static int msm_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { struct msm_compr_audio *prtd; struct audio_client *ac; pr_debug("%s\n", __func__); if (!metadata || !cstream) return -EINVAL; prtd = cstream->runtime->private_data; if (!prtd && !prtd->audio_client) return -EINVAL; ac = prtd->audio_client; if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) { pr_debug("%s, got encoder padding %u", __func__, metadata->value[0]); prtd->gapless_state.trailing_samples_drop = metadata->value[0]; } else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY) { pr_debug("%s, got encoder delay %u", __func__, metadata->value[0]); prtd->gapless_state.initial_samples_drop = metadata->value[0]; } return 0; Loading