linux/sound/soc/qcom/sdw.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2018-2023, Linaro Limited.
   3// Copyright (c) 2018, The Linux Foundation. All rights reserved.
   4
   5#include <dt-bindings/sound/qcom,q6afe.h>
   6#include <linux/module.h>
   7#include <sound/soc.h>
   8#include "sdw.h"
   9
  10/**
  11 * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
  12 * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
  13 *
  14 * Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set
  15 * Soundwire stream runtime to each codec DAI.
  16 *
  17 * The shutdown() callback should call sdw_release_stream() on the same
  18 * sdw_stream_runtime.
  19 *
  20 * Return: 0 or errno
  21 */
  22int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
  23{
  24        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
  25        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
  26        u32 rx_ch[SDW_MAX_PORTS], tx_ch[SDW_MAX_PORTS];
  27        struct sdw_stream_runtime *sruntime;
  28        struct snd_soc_dai *codec_dai;
  29        u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
  30        int ret, i, j;
  31
  32        sruntime = sdw_alloc_stream(cpu_dai->name, SDW_STREAM_PCM);
  33        if (!sruntime)
  34                return -ENOMEM;
  35
  36        for_each_rtd_codec_dais(rtd, i, codec_dai) {
  37                ret = snd_soc_dai_set_stream(codec_dai, sruntime,
  38                                             substream->stream);
  39                if (ret < 0 && ret != -ENOTSUPP) {
  40                        dev_err(rtd->dev, "Failed to set sdw stream on %s\n", codec_dai->name);
  41                        goto err_set_stream;
  42                } else if (ret == -ENOTSUPP) {
  43                        /* Ignore unsupported */
  44                        continue;
  45                }
  46
  47                ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch,
  48                                                  &rx_ch_cnt, rx_ch);
  49                if (ret != 0 && ret != -ENOTSUPP) {
  50                        dev_err(rtd->dev, "Failed to get codec chan map %s\n", codec_dai->name);
  51                        goto err_set_stream;
  52                } else if (ret == -ENOTSUPP) {
  53                        /* Ignore unsupported */
  54                        continue;
  55                }
  56        }
  57
  58        switch (cpu_dai->id) {
  59        case RX_CODEC_DMA_RX_0:
  60        case TX_CODEC_DMA_TX_3:
  61                if (tx_ch_cnt || rx_ch_cnt) {
  62                        for_each_rtd_codec_dais(rtd, j, codec_dai) {
  63                                ret = snd_soc_dai_set_channel_map(codec_dai,
  64                                                                  tx_ch_cnt, tx_ch,
  65                                                                  rx_ch_cnt, rx_ch);
  66                                if (ret != 0 && ret != -ENOTSUPP)
  67                                        goto err_set_stream;
  68                        }
  69                }
  70        }
  71
  72        return 0;
  73
  74err_set_stream:
  75        sdw_release_stream(sruntime);
  76
  77        return ret;
  78}
  79EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup);
  80
  81int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
  82                         struct sdw_stream_runtime *sruntime,
  83                         bool *stream_prepared)
  84{
  85        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
  86        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
  87        int ret;
  88
  89        if (!sruntime)
  90                return 0;
  91
  92        switch (cpu_dai->id) {
  93        case WSA_CODEC_DMA_RX_0:
  94        case WSA_CODEC_DMA_RX_1:
  95        case RX_CODEC_DMA_RX_0:
  96        case RX_CODEC_DMA_RX_1:
  97        case TX_CODEC_DMA_TX_0:
  98        case TX_CODEC_DMA_TX_1:
  99        case TX_CODEC_DMA_TX_2:
 100        case TX_CODEC_DMA_TX_3:
 101                break;
 102        default:
 103                return 0;
 104        }
 105
 106        if (*stream_prepared)
 107                return 0;
 108
 109        ret = sdw_prepare_stream(sruntime);
 110        if (ret)
 111                return ret;
 112
 113        /**
 114         * NOTE: there is a strict hw requirement about the ordering of port
 115         * enables and actual WSA881x PA enable. PA enable should only happen
 116         * after soundwire ports are enabled if not DC on the line is
 117         * accumulated resulting in Click/Pop Noise
 118         * PA enable/mute are handled as part of codec DAPM and digital mute.
 119         */
 120
 121        ret = sdw_enable_stream(sruntime);
 122        if (ret) {
 123                sdw_deprepare_stream(sruntime);
 124                return ret;
 125        }
 126        *stream_prepared  = true;
 127
 128        return ret;
 129}
 130EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
 131
 132int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
 133                           struct snd_pcm_hw_params *params,
 134                           struct sdw_stream_runtime **psruntime)
 135{
 136        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 137        struct snd_soc_dai *codec_dai;
 138        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 139        struct sdw_stream_runtime *sruntime;
 140        int i;
 141
 142        switch (cpu_dai->id) {
 143        case WSA_CODEC_DMA_RX_0:
 144        case RX_CODEC_DMA_RX_0:
 145        case RX_CODEC_DMA_RX_1:
 146        case TX_CODEC_DMA_TX_0:
 147        case TX_CODEC_DMA_TX_1:
 148        case TX_CODEC_DMA_TX_2:
 149        case TX_CODEC_DMA_TX_3:
 150                for_each_rtd_codec_dais(rtd, i, codec_dai) {
 151                        sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
 152                        if (sruntime != ERR_PTR(-ENOTSUPP))
 153                                *psruntime = sruntime;
 154                }
 155                break;
 156        }
 157
 158        return 0;
 159
 160}
 161EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_params);
 162
 163int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
 164                         struct sdw_stream_runtime *sruntime, bool *stream_prepared)
 165{
 166        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 167        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 168
 169        switch (cpu_dai->id) {
 170        case WSA_CODEC_DMA_RX_0:
 171        case WSA_CODEC_DMA_RX_1:
 172        case RX_CODEC_DMA_RX_0:
 173        case RX_CODEC_DMA_RX_1:
 174        case TX_CODEC_DMA_TX_0:
 175        case TX_CODEC_DMA_TX_1:
 176        case TX_CODEC_DMA_TX_2:
 177        case TX_CODEC_DMA_TX_3:
 178                if (sruntime && *stream_prepared) {
 179                        sdw_disable_stream(sruntime);
 180                        sdw_deprepare_stream(sruntime);
 181                        *stream_prepared = false;
 182                }
 183                break;
 184        default:
 185                break;
 186        }
 187
 188        return 0;
 189}
 190EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
 191MODULE_DESCRIPTION("Qualcomm ASoC SoundWire helper functions");
 192MODULE_LICENSE("GPL");
 193