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