root/sound/soc/soc-link.c
// SPDX-License-Identifier: GPL-2.0
//
// soc-link.c
//
// Copyright (C) 2019 Renesas Electronics Corp.
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
#include <sound/soc.h>
#include <sound/soc-link.h>

#define soc_link_ret(rtd, ret) _soc_link_ret(rtd, __func__, ret)
static inline int _soc_link_ret(struct snd_soc_pcm_runtime *rtd,
                                const char *func, int ret)
{
        return snd_soc_ret(rtd->dev, ret,
                           "at %s() on %s\n", func, rtd->dai_link->name);
}

/*
 * We might want to check substream by using list.
 * In such case, we can update these macros.
 */
#define soc_link_mark_push(rtd, substream, tgt)         ((rtd)->mark_##tgt = substream)
#define soc_link_mark_pop(rtd, tgt)             ((rtd)->mark_##tgt = NULL)
#define soc_link_mark_match(rtd, substream, tgt)        ((rtd)->mark_##tgt == substream)

int snd_soc_link_init(struct snd_soc_pcm_runtime *rtd)
{
        int ret = 0;

        if (rtd->dai_link->init)
                ret = rtd->dai_link->init(rtd);

        return soc_link_ret(rtd, ret);
}

void snd_soc_link_exit(struct snd_soc_pcm_runtime *rtd)
{
        if (rtd->dai_link->exit)
                rtd->dai_link->exit(rtd);
}

int snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
                                    struct snd_pcm_hw_params *params)
{
        int ret = 0;

        if (rtd->dai_link->be_hw_params_fixup)
                ret = rtd->dai_link->be_hw_params_fixup(rtd, params);

        return soc_link_ret(rtd, ret);
}

int snd_soc_link_startup(struct snd_pcm_substream *substream)
{
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        int ret = 0;

        if (rtd->dai_link->ops &&
            rtd->dai_link->ops->startup)
                ret = rtd->dai_link->ops->startup(substream);

        /* mark substream if succeeded */
        if (ret == 0)
                soc_link_mark_push(rtd, substream, startup);

        return soc_link_ret(rtd, ret);
}

void snd_soc_link_shutdown(struct snd_pcm_substream *substream,
                           int rollback)
{
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);

        if (rollback && !soc_link_mark_match(rtd, substream, startup))
                return;

        if (rtd->dai_link->ops &&
            rtd->dai_link->ops->shutdown)
                rtd->dai_link->ops->shutdown(substream);

        /* remove marked substream */
        soc_link_mark_pop(rtd, startup);
}

int snd_soc_link_prepare(struct snd_pcm_substream *substream)
{
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        int ret = 0;

        if (rtd->dai_link->ops &&
            rtd->dai_link->ops->prepare)
                ret = rtd->dai_link->ops->prepare(substream);

        return soc_link_ret(rtd, ret);
}

int snd_soc_link_hw_params(struct snd_pcm_substream *substream,
                           struct snd_pcm_hw_params *params)
{
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        int ret = 0;

        if (rtd->dai_link->ops &&
            rtd->dai_link->ops->hw_params)
                ret = rtd->dai_link->ops->hw_params(substream, params);

        /* mark substream if succeeded */
        if (ret == 0)
                soc_link_mark_push(rtd, substream, hw_params);

        return soc_link_ret(rtd, ret);
}

void snd_soc_link_hw_free(struct snd_pcm_substream *substream, int rollback)
{
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);

        if (rollback && !soc_link_mark_match(rtd, substream, hw_params))
                return;

        if (rtd->dai_link->ops &&
            rtd->dai_link->ops->hw_free)
                rtd->dai_link->ops->hw_free(substream);

        /* remove marked substream */
        soc_link_mark_pop(rtd, hw_params);
}

static int soc_link_trigger(struct snd_pcm_substream *substream, int cmd)
{
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        int ret = 0;

        if (rtd->dai_link->ops &&
            rtd->dai_link->ops->trigger)
                ret = rtd->dai_link->ops->trigger(substream, cmd);

        return soc_link_ret(rtd, ret);
}

int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd,
                         int rollback)
{
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        int ret = 0;

        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                ret = soc_link_trigger(substream, cmd);
                if (ret < 0)
                        break;
                soc_link_mark_push(rtd, substream, trigger);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                if (rollback && !soc_link_mark_match(rtd, substream, trigger))
                        break;

                ret = soc_link_trigger(substream, cmd);
                soc_link_mark_pop(rtd, startup);
        }

        return ret;
}

int snd_soc_link_compr_startup(struct snd_compr_stream *cstream)
{
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
        int ret = 0;

        if (rtd->dai_link->compr_ops &&
            rtd->dai_link->compr_ops->startup)
                ret = rtd->dai_link->compr_ops->startup(cstream);

        if (ret == 0)
                soc_link_mark_push(rtd, cstream, compr_startup);

        return soc_link_ret(rtd, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_link_compr_startup);

void snd_soc_link_compr_shutdown(struct snd_compr_stream *cstream,
                                 int rollback)
{
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;

        if (rollback && !soc_link_mark_match(rtd, cstream, compr_startup))
                return;

        if (rtd->dai_link->compr_ops &&
            rtd->dai_link->compr_ops->shutdown)
                rtd->dai_link->compr_ops->shutdown(cstream);

        soc_link_mark_pop(rtd, compr_startup);
}
EXPORT_SYMBOL_GPL(snd_soc_link_compr_shutdown);

int snd_soc_link_compr_set_params(struct snd_compr_stream *cstream)
{
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
        int ret = 0;

        if (rtd->dai_link->compr_ops &&
            rtd->dai_link->compr_ops->set_params)
                ret = rtd->dai_link->compr_ops->set_params(cstream);

        return soc_link_ret(rtd, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_link_compr_set_params);