1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // soc-card.c |
4 | // |
5 | // Copyright (C) 2019 Renesas Electronics Corp. |
6 | // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
7 | // |
8 | |
9 | #include <linux/lockdep.h> |
10 | #include <linux/rwsem.h> |
11 | #include <sound/soc.h> |
12 | #include <sound/jack.h> |
13 | |
14 | #define soc_card_ret(dai, ret) _soc_card_ret(dai, __func__, ret) |
15 | static inline int _soc_card_ret(struct snd_soc_card *card, |
16 | const char *func, int ret) |
17 | { |
18 | switch (ret) { |
19 | case -EPROBE_DEFER: |
20 | case -ENOTSUPP: |
21 | case 0: |
22 | break; |
23 | default: |
24 | dev_err(card->dev, |
25 | "ASoC: error at %s on %s: %d\n" , |
26 | func, card->name, ret); |
27 | } |
28 | |
29 | return ret; |
30 | } |
31 | |
32 | struct snd_kcontrol *snd_soc_card_get_kcontrol_locked(struct snd_soc_card *soc_card, |
33 | const char *name) |
34 | { |
35 | struct snd_card *card = soc_card->snd_card; |
36 | struct snd_kcontrol *kctl; |
37 | |
38 | /* must be held read or write */ |
39 | lockdep_assert_held(&card->controls_rwsem); |
40 | |
41 | if (unlikely(!name)) |
42 | return NULL; |
43 | |
44 | list_for_each_entry(kctl, &card->controls, list) |
45 | if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) |
46 | return kctl; |
47 | return NULL; |
48 | } |
49 | EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol_locked); |
50 | |
51 | struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, |
52 | const char *name) |
53 | { |
54 | struct snd_card *card = soc_card->snd_card; |
55 | struct snd_kcontrol *kctl; |
56 | |
57 | down_read(sem: &card->controls_rwsem); |
58 | kctl = snd_soc_card_get_kcontrol_locked(soc_card, name); |
59 | up_read(sem: &card->controls_rwsem); |
60 | |
61 | return kctl; |
62 | } |
63 | EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); |
64 | |
65 | static int jack_new(struct snd_soc_card *card, const char *id, int type, |
66 | struct snd_soc_jack *jack, bool initial_kctl) |
67 | { |
68 | mutex_init(&jack->mutex); |
69 | jack->card = card; |
70 | INIT_LIST_HEAD(list: &jack->pins); |
71 | INIT_LIST_HEAD(list: &jack->jack_zones); |
72 | BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); |
73 | |
74 | return snd_jack_new(card: card->snd_card, id, type, jack: &jack->jack, initial_kctl, phantom_jack: false); |
75 | } |
76 | |
77 | /** |
78 | * snd_soc_card_jack_new - Create a new jack without pins |
79 | * @card: ASoC card |
80 | * @id: an identifying string for this jack |
81 | * @type: a bitmask of enum snd_jack_type values that can be detected by |
82 | * this jack |
83 | * @jack: structure to use for the jack |
84 | * |
85 | * Creates a new jack object without pins. If adding pins later, |
86 | * snd_soc_card_jack_new_pins() should be used instead with 0 as num_pins |
87 | * argument. |
88 | * |
89 | * Returns zero if successful, or a negative error code on failure. |
90 | * On success jack will be initialised. |
91 | */ |
92 | int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, |
93 | struct snd_soc_jack *jack) |
94 | { |
95 | return soc_card_ret(card, jack_new(card, id, type, jack, true)); |
96 | } |
97 | EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); |
98 | |
99 | /** |
100 | * snd_soc_card_jack_new_pins - Create a new jack with pins |
101 | * @card: ASoC card |
102 | * @id: an identifying string for this jack |
103 | * @type: a bitmask of enum snd_jack_type values that can be detected by |
104 | * this jack |
105 | * @jack: structure to use for the jack |
106 | * @pins: Array of jack pins to be added to the jack or NULL |
107 | * @num_pins: Number of elements in the @pins array |
108 | * |
109 | * Creates a new jack object with pins. If not adding pins, |
110 | * snd_soc_card_jack_new() should be used instead. |
111 | * |
112 | * Returns zero if successful, or a negative error code on failure. |
113 | * On success jack will be initialised. |
114 | */ |
115 | int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id, |
116 | int type, struct snd_soc_jack *jack, |
117 | struct snd_soc_jack_pin *pins, |
118 | unsigned int num_pins) |
119 | { |
120 | int ret; |
121 | |
122 | ret = jack_new(card, id, type, jack, initial_kctl: false); |
123 | if (ret) |
124 | goto end; |
125 | |
126 | if (num_pins) |
127 | ret = snd_soc_jack_add_pins(jack, count: num_pins, pins); |
128 | end: |
129 | return soc_card_ret(card, ret); |
130 | } |
131 | EXPORT_SYMBOL_GPL(snd_soc_card_jack_new_pins); |
132 | |
133 | int snd_soc_card_suspend_pre(struct snd_soc_card *card) |
134 | { |
135 | int ret = 0; |
136 | |
137 | if (card->suspend_pre) |
138 | ret = card->suspend_pre(card); |
139 | |
140 | return soc_card_ret(card, ret); |
141 | } |
142 | |
143 | int snd_soc_card_suspend_post(struct snd_soc_card *card) |
144 | { |
145 | int ret = 0; |
146 | |
147 | if (card->suspend_post) |
148 | ret = card->suspend_post(card); |
149 | |
150 | return soc_card_ret(card, ret); |
151 | } |
152 | |
153 | int snd_soc_card_resume_pre(struct snd_soc_card *card) |
154 | { |
155 | int ret = 0; |
156 | |
157 | if (card->resume_pre) |
158 | ret = card->resume_pre(card); |
159 | |
160 | return soc_card_ret(card, ret); |
161 | } |
162 | |
163 | int snd_soc_card_resume_post(struct snd_soc_card *card) |
164 | { |
165 | int ret = 0; |
166 | |
167 | if (card->resume_post) |
168 | ret = card->resume_post(card); |
169 | |
170 | return soc_card_ret(card, ret); |
171 | } |
172 | |
173 | int snd_soc_card_probe(struct snd_soc_card *card) |
174 | { |
175 | if (card->probe) { |
176 | int ret = card->probe(card); |
177 | |
178 | if (ret < 0) |
179 | return soc_card_ret(card, ret); |
180 | |
181 | /* |
182 | * It has "card->probe" and "card->late_probe" callbacks. |
183 | * So, set "probed" flag here, because it needs to care |
184 | * about "late_probe". |
185 | * |
186 | * see |
187 | * snd_soc_bind_card() |
188 | * snd_soc_card_late_probe() |
189 | */ |
190 | card->probed = 1; |
191 | } |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | int snd_soc_card_late_probe(struct snd_soc_card *card) |
197 | { |
198 | if (card->late_probe) { |
199 | int ret = card->late_probe(card); |
200 | |
201 | if (ret < 0) |
202 | return soc_card_ret(card, ret); |
203 | } |
204 | |
205 | /* |
206 | * It has "card->probe" and "card->late_probe" callbacks, |
207 | * and "late_probe" callback is called after "probe". |
208 | * This means, we can set "card->probed" flag afer "late_probe" |
209 | * for all cases. |
210 | * |
211 | * see |
212 | * snd_soc_bind_card() |
213 | * snd_soc_card_probe() |
214 | */ |
215 | card->probed = 1; |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | void snd_soc_card_fixup_controls(struct snd_soc_card *card) |
221 | { |
222 | if (card->fixup_controls) |
223 | card->fixup_controls(card); |
224 | } |
225 | |
226 | int snd_soc_card_remove(struct snd_soc_card *card) |
227 | { |
228 | int ret = 0; |
229 | |
230 | if (card->probed && |
231 | card->remove) |
232 | ret = card->remove(card); |
233 | |
234 | card->probed = 0; |
235 | |
236 | return soc_card_ret(card, ret); |
237 | } |
238 | |
239 | int snd_soc_card_set_bias_level(struct snd_soc_card *card, |
240 | struct snd_soc_dapm_context *dapm, |
241 | enum snd_soc_bias_level level) |
242 | { |
243 | int ret = 0; |
244 | |
245 | if (card && card->set_bias_level) |
246 | ret = card->set_bias_level(card, dapm, level); |
247 | |
248 | return soc_card_ret(card, ret); |
249 | } |
250 | |
251 | int snd_soc_card_set_bias_level_post(struct snd_soc_card *card, |
252 | struct snd_soc_dapm_context *dapm, |
253 | enum snd_soc_bias_level level) |
254 | { |
255 | int ret = 0; |
256 | |
257 | if (card && card->set_bias_level_post) |
258 | ret = card->set_bias_level_post(card, dapm, level); |
259 | |
260 | return soc_card_ret(card, ret); |
261 | } |
262 | |
263 | int snd_soc_card_add_dai_link(struct snd_soc_card *card, |
264 | struct snd_soc_dai_link *dai_link) |
265 | { |
266 | int ret = 0; |
267 | |
268 | if (card->add_dai_link) |
269 | ret = card->add_dai_link(card, dai_link); |
270 | |
271 | return soc_card_ret(card, ret); |
272 | } |
273 | EXPORT_SYMBOL_GPL(snd_soc_card_add_dai_link); |
274 | |
275 | void snd_soc_card_remove_dai_link(struct snd_soc_card *card, |
276 | struct snd_soc_dai_link *dai_link) |
277 | { |
278 | if (card->remove_dai_link) |
279 | card->remove_dai_link(card, dai_link); |
280 | } |
281 | EXPORT_SYMBOL_GPL(snd_soc_card_remove_dai_link); |
282 | |