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