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  */
qcom_snd_sdw_startup(struct snd_pcm_substream * substream)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 
qcom_snd_sdw_prepare(struct snd_pcm_substream * substream,struct sdw_stream_runtime * sruntime,bool * stream_prepared)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 
qcom_snd_sdw_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct sdw_stream_runtime ** psruntime)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 
qcom_snd_sdw_hw_free(struct snd_pcm_substream * substream,struct sdw_stream_runtime * sruntime,bool * stream_prepared)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