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 */ 22 int 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 74 err_set_stream: 75 sdw_release_stream(sruntime); 76 77 return ret; 78 } 79 EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup); 80 81 int 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 } 130 EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare); 131 132 int 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 } 161 EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_params); 162 163 int 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 } 190 EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free); 191 MODULE_DESCRIPTION("Qualcomm ASoC SoundWire helper functions"); 192 MODULE_LICENSE("GPL"); 193