xref: /linux/sound/soc/qcom/sdw.c (revision d30c1683aaecb93d2ab95685dc4300a33d3cea7a)
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