xref: /linux/sound/soc/soc-card.c (revision 40ccd6aa3e2e05be93394e3cd560c718dedfcc77)
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 	if (unlikely(!name))
36 		return NULL;
37 
38 	return snd_ctl_find_id_mixer_locked(soc_card->snd_card, name);
39 }
40 EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol_locked);
41 
42 struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
43 					       const char *name)
44 {
45 	if (unlikely(!name))
46 		return NULL;
47 
48 	return snd_ctl_find_id_mixer(soc_card->snd_card, name);
49 }
50 EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
51 
52 static int jack_new(struct snd_soc_card *card, const char *id, int type,
53 		    struct snd_soc_jack *jack, bool initial_kctl)
54 {
55 	mutex_init(&jack->mutex);
56 	jack->card = card;
57 	INIT_LIST_HEAD(&jack->pins);
58 	INIT_LIST_HEAD(&jack->jack_zones);
59 	BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
60 
61 	return snd_jack_new(card->snd_card, id, type, &jack->jack, initial_kctl, false);
62 }
63 
64 /**
65  * snd_soc_card_jack_new - Create a new jack without pins
66  * @card:  ASoC card
67  * @id:    an identifying string for this jack
68  * @type:  a bitmask of enum snd_jack_type values that can be detected by
69  *         this jack
70  * @jack:  structure to use for the jack
71  *
72  * Creates a new jack object without pins. If adding pins later,
73  * snd_soc_card_jack_new_pins() should be used instead with 0 as num_pins
74  * argument.
75  *
76  * Returns zero if successful, or a negative error code on failure.
77  * On success jack will be initialised.
78  */
79 int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
80 			  struct snd_soc_jack *jack)
81 {
82 	return soc_card_ret(card, jack_new(card, id, type, jack, true));
83 }
84 EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
85 
86 /**
87  * snd_soc_card_jack_new_pins - Create a new jack with pins
88  * @card:  ASoC card
89  * @id:    an identifying string for this jack
90  * @type:  a bitmask of enum snd_jack_type values that can be detected by
91  *         this jack
92  * @jack:  structure to use for the jack
93  * @pins:  Array of jack pins to be added to the jack or NULL
94  * @num_pins: Number of elements in the @pins array
95  *
96  * Creates a new jack object with pins. If not adding pins,
97  * snd_soc_card_jack_new() should be used instead.
98  *
99  * Returns zero if successful, or a negative error code on failure.
100  * On success jack will be initialised.
101  */
102 int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id,
103 			       int type, struct snd_soc_jack *jack,
104 			       struct snd_soc_jack_pin *pins,
105 			       unsigned int num_pins)
106 {
107 	int ret;
108 
109 	ret = jack_new(card, id, type, jack, false);
110 	if (ret)
111 		goto end;
112 
113 	if (num_pins)
114 		ret = snd_soc_jack_add_pins(jack, num_pins, pins);
115 end:
116 	return soc_card_ret(card, ret);
117 }
118 EXPORT_SYMBOL_GPL(snd_soc_card_jack_new_pins);
119 
120 int snd_soc_card_suspend_pre(struct snd_soc_card *card)
121 {
122 	int ret = 0;
123 
124 	if (card->suspend_pre)
125 		ret = card->suspend_pre(card);
126 
127 	return soc_card_ret(card, ret);
128 }
129 
130 int snd_soc_card_suspend_post(struct snd_soc_card *card)
131 {
132 	int ret = 0;
133 
134 	if (card->suspend_post)
135 		ret = card->suspend_post(card);
136 
137 	return soc_card_ret(card, ret);
138 }
139 
140 int snd_soc_card_resume_pre(struct snd_soc_card *card)
141 {
142 	int ret = 0;
143 
144 	if (card->resume_pre)
145 		ret = card->resume_pre(card);
146 
147 	return soc_card_ret(card, ret);
148 }
149 
150 int snd_soc_card_resume_post(struct snd_soc_card *card)
151 {
152 	int ret = 0;
153 
154 	if (card->resume_post)
155 		ret = card->resume_post(card);
156 
157 	return soc_card_ret(card, ret);
158 }
159 
160 int snd_soc_card_probe(struct snd_soc_card *card)
161 {
162 	if (card->probe) {
163 		int ret = card->probe(card);
164 
165 		if (ret < 0)
166 			return soc_card_ret(card, ret);
167 
168 		/*
169 		 * It has "card->probe" and "card->late_probe" callbacks.
170 		 * So, set "probed" flag here, because it needs to care
171 		 * about "late_probe".
172 		 *
173 		 * see
174 		 *	snd_soc_bind_card()
175 		 *	snd_soc_card_late_probe()
176 		 */
177 		card->probed = 1;
178 	}
179 
180 	return 0;
181 }
182 
183 int snd_soc_card_late_probe(struct snd_soc_card *card)
184 {
185 	if (card->late_probe) {
186 		int ret = card->late_probe(card);
187 
188 		if (ret < 0)
189 			return soc_card_ret(card, ret);
190 	}
191 
192 	/*
193 	 * It has "card->probe" and "card->late_probe" callbacks,
194 	 * and "late_probe" callback is called after "probe".
195 	 * This means, we can set "card->probed" flag afer "late_probe"
196 	 * for all cases.
197 	 *
198 	 * see
199 	 *	snd_soc_bind_card()
200 	 *	snd_soc_card_probe()
201 	 */
202 	card->probed = 1;
203 
204 	return 0;
205 }
206 
207 void snd_soc_card_fixup_controls(struct snd_soc_card *card)
208 {
209 	if (card->fixup_controls)
210 		card->fixup_controls(card);
211 }
212 
213 int snd_soc_card_remove(struct snd_soc_card *card)
214 {
215 	int ret = 0;
216 
217 	if (card->probed &&
218 	    card->remove)
219 		ret = card->remove(card);
220 
221 	card->probed = 0;
222 
223 	return soc_card_ret(card, ret);
224 }
225 
226 int snd_soc_card_set_bias_level(struct snd_soc_card *card,
227 				struct snd_soc_dapm_context *dapm,
228 				enum snd_soc_bias_level level)
229 {
230 	int ret = 0;
231 
232 	if (card && card->set_bias_level)
233 		ret = card->set_bias_level(card, dapm, level);
234 
235 	return soc_card_ret(card, ret);
236 }
237 
238 int snd_soc_card_set_bias_level_post(struct snd_soc_card *card,
239 				     struct snd_soc_dapm_context *dapm,
240 				     enum snd_soc_bias_level level)
241 {
242 	int ret = 0;
243 
244 	if (card && card->set_bias_level_post)
245 		ret = card->set_bias_level_post(card, dapm, level);
246 
247 	return soc_card_ret(card, ret);
248 }
249 
250 int snd_soc_card_add_dai_link(struct snd_soc_card *card,
251 			      struct snd_soc_dai_link *dai_link)
252 {
253 	int ret = 0;
254 
255 	if (card->add_dai_link)
256 		ret = card->add_dai_link(card, dai_link);
257 
258 	return soc_card_ret(card, ret);
259 }
260 EXPORT_SYMBOL_GPL(snd_soc_card_add_dai_link);
261 
262 void snd_soc_card_remove_dai_link(struct snd_soc_card *card,
263 				  struct snd_soc_dai_link *dai_link)
264 {
265 	if (card->remove_dai_link)
266 		card->remove_dai_link(card, dai_link);
267 }
268 EXPORT_SYMBOL_GPL(snd_soc_card_remove_dai_link);
269