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;
}