static int snd_compr_stop(struct snd_compr_stream *stream) { int retval; if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); if (!retval) { snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; } return retval; }
static int compr_trigger(struct snd_compr_stream *cstream, int cmd) { struct snd_compr_runtime *runtime = cstream->runtime; struct runtime_data *prtd = runtime->private_data; unsigned long flags; int ret; pr_debug("%s: trigger cmd(%d)\n", __func__, cmd); /* platform -> codec -> cpu */ if (cstream->direction != SND_COMPRESS_PLAYBACK) { pr_err("%s: Unsupported stream type\n", __func__); return -EINVAL; } switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: pr_info("%s: SNDRV_PCM_TRIGGER_PAUSE_PUSH\n", __func__); spin_lock_irqsave(&prtd->lock, flags); ret = esa_compr_send_cmd(CMD_COMPR_PAUSE, prtd->ap); if (ret) { pr_err("%s: pause cmd failed(%d)\n", __func__, ret); spin_unlock_irqrestore(&prtd->lock, flags); return ret; } spin_unlock_irqrestore(&prtd->lock, flags); atomic_set(&prtd->start, 0); break; case SNDRV_PCM_TRIGGER_STOP: pr_info("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); spin_lock_irqsave(&prtd->lock, flags); if (atomic_read(&prtd->eos)) { /* ALSA Framework callback to notify drain complete */ snd_compr_drain_notify(cstream); atomic_set(&prtd->eos, 0); pr_debug("%s: interrupt drain and eos wait queues", __func__); } pr_debug("CMD_STOP\n"); prtd->stop_ack = 0; ret = esa_compr_send_cmd(CMD_COMPR_STOP, prtd->ap); if (ret) { pr_err("%s: stop cmd failed (%d)\n", __func__, ret); spin_unlock_irqrestore(&prtd->lock, flags); return ret; } spin_unlock_irqrestore(&prtd->lock, flags); ret = wait_event_interruptible_timeout(prtd->flush_wait, prtd->stop_ack, 1 * HZ); if (!ret) { pr_err("CMD_STOP cmd timeout(%d)\n", ret); ret = -ETIMEDOUT; } else ret = 0; ret = compr_dai_cmd(prtd, cmd); if (ret) { pr_err("%s: compr_dai_cmd fail(%d)\n", __func__, ret); return ret; } atomic_set(&prtd->start, 0); /* reset */ prtd->stop_ack = 0; prtd->byte_offset = 0; prtd->app_pointer = 0; prtd->copied_total = 0; prtd->received_total = 0; break; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (SNDRV_PCM_TRIGGER_START == cmd) pr_info("%s: SNDRV_PCM_TRIGGER_START\n", __func__); else if (SNDRV_PCM_TRIGGER_PAUSE_RELEASE == cmd) pr_info("%s: SNDRV_PCM_TRIGGER_PAUSE_RELEASE\n", __func__); ret = compr_dai_cmd(prtd, cmd); if (ret) { pr_err("%s: compr_dai_cmd fail(%d)\n", __func__, ret); return ret; } atomic_set(&prtd->start, 1); spin_lock_irqsave(&prtd->lock, flags); ret = esa_compr_send_cmd(CMD_COMPR_START, prtd->ap); if (ret) { pr_err("%s: start cmd failed\n", __func__); spin_unlock_irqrestore(&prtd->lock, flags); return ret; } spin_unlock_irqrestore(&prtd->lock, flags); break; case SND_COMPR_TRIGGER_NEXT_TRACK: pr_info("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__); break; case SND_COMPR_TRIGGER_PARTIAL_DRAIN: pr_info("%s: SND_COMPR_TRIGGER_PARTIAL_DRAIN\n", __func__); case SND_COMPR_TRIGGER_DRAIN: if (SND_COMPR_TRIGGER_DRAIN == cmd) pr_info("%s: SND_COMPR_TRIGGER_DRAIN\n", __func__); /* Make sure all the data is sent to F/W before sending EOS */ spin_lock_irqsave(&prtd->lock, flags); #ifdef AUDIO_PERF prtd->start_time[DRAIN_T] = sched_clock(); #endif if (!atomic_read(&prtd->start)) { pr_err("%s: stream is not in started state\n", __func__); ret = -EPERM; spin_unlock_irqrestore(&prtd->lock, flags); break; } atomic_set(&prtd->eos, 1); pr_debug("%s: CMD_EOS\n", __func__); ret = esa_compr_send_cmd(CMD_COMPR_EOS, prtd->ap); if (ret) { pr_err("%s: can't send eos (%d)\n", __func__, ret); spin_unlock_irqrestore(&prtd->lock, flags); return ret; } spin_unlock_irqrestore(&prtd->lock, flags); #ifdef AUDIO_PERF prtd->end_time[DRAIN_T] = sched_clock(); prtd->total_time[DRAIN_T] += prtd->end_time[DRAIN_T] - prtd->start_time[DRAIN_T]; #endif pr_info("%s: Out of %s Drain", __func__, (cmd == SND_COMPR_TRIGGER_DRAIN ? "Full" : "Partial")); break; default: break; } return 0; }
static int compr_free(struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime = cstream->runtime; struct runtime_data *prtd = runtime->private_data; struct snd_pcm_substream *substream; struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; const struct snd_soc_dai_ops *cpu_dai_ops; const struct snd_soc_dai_ops *codec_dai_ops; unsigned long flags; int ret; #ifdef AUDIO_PERF u64 playback_time, total_time = 0; int idx; #endif pr_debug("%s\n", __func__); if (!prtd) { pr_info("compress dai has already freed.\n"); return 0; } substream = &prtd->substream; cpu_dai = prtd->cpu_dai; codec_dai = prtd->codec_dai; cpu_dai_ops = cpu_dai->driver->ops; codec_dai_ops = codec_dai->driver->ops; if (atomic_read(&prtd->eos)) { /* ALSA Framework callback to notify drain complete */ snd_compr_drain_notify(cstream); atomic_set(&prtd->eos, 0); pr_debug("%s Call Drain notify to wakeup\n", __func__); } if (atomic_read(&prtd->created)) { spin_lock_irqsave(&prtd->lock, flags); atomic_set(&prtd->created, 0); prtd->exit_ack = 0; ret = esa_compr_send_cmd(CMD_COMPR_DESTROY, prtd->ap); if (ret) { esa_err("%s: can't send CMD_COMPR_DESTROY (%d)\n", __func__, ret); spin_unlock_irqrestore(&prtd->lock, flags); } else { spin_unlock_irqrestore(&prtd->lock, flags); ret = wait_event_interruptible_timeout(prtd->exit_wait, prtd->exit_ack, 1 * HZ); if (!ret) pr_err("%s: CMD_DESTROY timed out!!!\n", __func__); } } #ifdef CONFIG_SND_ESA_SA_EFFECT aud_vol.ap[COMPR_DAI_MULTIMEDIA_1] = NULL; #endif esa_compr_set_state(false); /* codec hw_free -> cpu hw_free -> cpu shutdown -> codec shutdown */ if (codec_dai_ops->hw_free) (*codec_dai_ops->hw_free)(substream, codec_dai); if (cpu_dai_ops->hw_free) (*cpu_dai_ops->hw_free)(substream, cpu_dai); if (cpu_dai_ops->shutdown) (*cpu_dai_ops->shutdown)(substream, cpu_dai); if (codec_dai_ops->shutdown) (*codec_dai_ops->shutdown)(substream, codec_dai); if (substream->runtime) kfree(substream->runtime->hw_constraints.rules); kfree(substream->runtime); esa_compr_close(); #ifdef AUDIO_PERF prtd->end_time[OPEN_T] = sched_clock(); playback_time = prtd->end_time[OPEN_T] - prtd->start_time[OPEN_T]; for (idx = 0; idx < TOTAL_TIMES; idx++) { total_time += prtd->total_time[idx]; } pr_debug("%s: measure the audio waken time : %llu\n", __func__, total_time); pr_debug("%s: may be the ap sleep time : (%llu/%llu)\n", __func__, playback_time - total_time, playback_time); #endif kfree(prtd->ap); kfree(prtd); return 0; }
static int compr_event_handler(uint32_t cmd, uint32_t size, void* priv) { struct runtime_data *prtd = priv; struct snd_compr_runtime *runtime = prtd->cstream->runtime; u64 bytes_available; int ret; pr_debug("%s: event handler cmd(%x)\n", __func__, cmd); #ifdef AUDIO_PERF prtd->start_time[ISR_T] = sched_clock(); #endif switch(cmd) { case INTR_CREATED: pr_debug("%s: offload instance is created\n", __func__); break; case INTR_DECODED: spin_lock(&prtd->lock); /* update copied total bytes */ prtd->copied_total += size; prtd->byte_offset += size; if (prtd->byte_offset >= runtime->buffer_size) prtd->byte_offset -= runtime->buffer_size; snd_compr_fragment_elapsed(prtd->cstream); if (!atomic_read(&prtd->start) && runtime->state != SNDRV_PCM_STATE_PAUSED) { /* Writes must be restarted from _copy() */ pr_err("%s: write_done received while not started(%d)", __func__, runtime->state); spin_unlock(&prtd->lock); return -EIO; } bytes_available = prtd->received_total - prtd->copied_total; pr_debug("%s: current free bufsize(%llu)\n", __func__, runtime->buffer_size - bytes_available); if (bytes_available < runtime->fragment_size) { pr_debug("%s: WRITE_DONE Insufficient data to send.(avail:%llu)\n", __func__, bytes_available); } spin_unlock(&prtd->lock); break; case INTR_FLUSH: prtd->stop_ack = 1; wake_up(&prtd->flush_wait); break; case INTR_PAUSED: ret = compr_dai_cmd(prtd, cmd); if (ret) pr_err("%s: compr_dai_cmd fail(%d)\n", __func__, ret); break; case INTR_EOS: if (atomic_read(&prtd->eos)) { if (prtd->copied_total != prtd->received_total) pr_err("%s: EOS is not sync!(%llu/%llu)\n", __func__, prtd->copied_total, prtd->received_total); /* ALSA Framework callback to notify drain complete */ snd_compr_drain_notify(prtd->cstream); atomic_set(&prtd->eos, 0); pr_info("%s: DATA_CMD_EOS wake up\n", __func__); } break; case INTR_DESTROY: prtd->exit_ack = 1; wake_up(&prtd->exit_wait); break; default: pr_err("%s: unknown command(%x)\n", __func__, cmd); break; } #ifdef AUDIO_PERF prtd->end_time[ISR_T] = sched_clock(); prtd->total_time[ISR_T] += prtd->end_time[ISR_T] - prtd->start_time[ISR_T]; #endif return 0; }