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