Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/qcom/sdw.c
50720 views
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
218