blob: 556b1a789629d221a972d161b3c7efa6d8813717 [file] [log] [blame]
Kuninori Morimotod613a7f2018-07-02 06:30:44 +00001// SPDX-License-Identifier: GPL-2.0
2//
3// simple-card-utils.c
4//
5// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6
Kuninori Morimotobb6fc622016-08-08 05:59:56 +00007#include <linux/clk.h>
Katsuhiro Suzuki62c2c9f2018-06-11 17:32:12 +09008#include <linux/gpio.h>
9#include <linux/gpio/consumer.h>
Kuninori Morimoto1f85e112016-08-03 01:24:05 +000010#include <linux/module.h>
Kuninori Morimotoabd31472016-05-31 09:00:14 +000011#include <linux/of.h>
Katsuhiro Suzuki62c2c9f2018-06-11 17:32:12 +090012#include <linux/of_gpio.h>
Kuninori Morimoto16893332017-04-20 01:35:18 +000013#include <linux/of_graph.h>
Katsuhiro Suzuki62c2c9f2018-06-11 17:32:12 +090014#include <sound/jack.h>
Kuninori Morimotoabd31472016-05-31 09:00:14 +000015#include <sound/simple_card_utils.h>
16
Kuninori Morimotoad11e592019-03-20 13:56:50 +090017void asoc_simple_convert_fixup(struct asoc_simple_data *data,
18 struct snd_pcm_hw_params *params)
Kuninori Morimoto13bb1cc2017-06-15 00:24:09 +000019{
20 struct snd_interval *rate = hw_param_interval(params,
21 SNDRV_PCM_HW_PARAM_RATE);
22 struct snd_interval *channels = hw_param_interval(params,
23 SNDRV_PCM_HW_PARAM_CHANNELS);
24
25 if (data->convert_rate)
26 rate->min =
27 rate->max = data->convert_rate;
28
29 if (data->convert_channels)
30 channels->min =
31 channels->max = data->convert_channels;
32}
Kuninori Morimotoad11e592019-03-20 13:56:50 +090033EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
Kuninori Morimoto13bb1cc2017-06-15 00:24:09 +000034
Kuninori Morimotoad11e592019-03-20 13:56:50 +090035void asoc_simple_parse_convert(struct device *dev,
36 struct device_node *np,
37 char *prefix,
38 struct asoc_simple_data *data)
Kuninori Morimoto13bb1cc2017-06-15 00:24:09 +000039{
Kuninori Morimoto13bb1cc2017-06-15 00:24:09 +000040 char prop[128];
41
42 if (!prefix)
43 prefix = "";
44
45 /* sampling rate convert */
46 snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
47 of_property_read_u32(np, prop, &data->convert_rate);
48
49 /* channels transfer */
50 snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
51 of_property_read_u32(np, prop, &data->convert_channels);
Kuninori Morimoto13bb1cc2017-06-15 00:24:09 +000052}
Kuninori Morimotoad11e592019-03-20 13:56:50 +090053EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
Kuninori Morimoto13bb1cc2017-06-15 00:24:09 +000054
Kuninori Morimotoad11e592019-03-20 13:56:50 +090055int asoc_simple_parse_daifmt(struct device *dev,
56 struct device_node *node,
57 struct device_node *codec,
58 char *prefix,
59 unsigned int *retfmt)
Kuninori Morimotoabd31472016-05-31 09:00:14 +000060{
61 struct device_node *bitclkmaster = NULL;
62 struct device_node *framemaster = NULL;
Kuninori Morimotoabd31472016-05-31 09:00:14 +000063 unsigned int daifmt;
64
65 daifmt = snd_soc_of_parse_daifmt(node, prefix,
66 &bitclkmaster, &framemaster);
67 daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
68
Kuninori Morimoto155b8f32017-05-25 01:51:31 +000069 if (!bitclkmaster && !framemaster) {
Kuninori Morimotoabd31472016-05-31 09:00:14 +000070 /*
71 * No dai-link level and master setting was not found from
72 * sound node level, revert back to legacy DT parsing and
73 * take the settings from codec node.
74 */
75 dev_dbg(dev, "Revert to legacy daifmt parsing\n");
76
77 daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
78 (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
79 } else {
80 if (codec == bitclkmaster)
81 daifmt |= (codec == framemaster) ?
82 SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
83 else
84 daifmt |= (codec == framemaster) ?
85 SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
86 }
87
88 of_node_put(bitclkmaster);
89 of_node_put(framemaster);
90
91 *retfmt = daifmt;
92
93 return 0;
94}
Kuninori Morimotoad11e592019-03-20 13:56:50 +090095EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
Kuninori Morimoto1db33122016-07-11 23:57:14 +000096
Kuninori Morimotoad11e592019-03-20 13:56:50 +090097int asoc_simple_set_dailink_name(struct device *dev,
98 struct snd_soc_dai_link *dai_link,
99 const char *fmt, ...)
Kuninori Morimoto1db33122016-07-11 23:57:14 +0000100{
101 va_list ap;
102 char *name = NULL;
103 int ret = -ENOMEM;
104
105 va_start(ap, fmt);
106 name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
107 va_end(ap);
108
109 if (name) {
110 ret = 0;
111
112 dai_link->name = name;
113 dai_link->stream_name = name;
114 }
115
116 return ret;
117}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900118EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
Kuninori Morimotofc55c9b2016-07-11 23:59:16 +0000119
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900120int asoc_simple_parse_card_name(struct snd_soc_card *card,
121 char *prefix)
Kuninori Morimotofc55c9b2016-07-11 23:59:16 +0000122{
Kuninori Morimotofc55c9b2016-07-11 23:59:16 +0000123 int ret;
124
Kuninori Morimotodedfaa12017-04-20 01:34:49 +0000125 if (!prefix)
126 prefix = "";
Kuninori Morimotofc55c9b2016-07-11 23:59:16 +0000127
128 /* Parse the card name from DT */
Kuninori Morimotodedfaa12017-04-20 01:34:49 +0000129 ret = snd_soc_of_parse_card_name(card, "label");
Lucas Stach1b4a56c2017-08-29 17:51:22 +0200130 if (ret < 0 || !card->name) {
Kuninori Morimotodedfaa12017-04-20 01:34:49 +0000131 char prop[128];
132
133 snprintf(prop, sizeof(prop), "%sname", prefix);
134 ret = snd_soc_of_parse_card_name(card, prop);
135 if (ret < 0)
136 return ret;
137 }
Kuninori Morimotofc55c9b2016-07-11 23:59:16 +0000138
139 if (!card->name && card->dai_link)
140 card->name = card->dai_link->name;
141
142 return 0;
143}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900144EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
Kuninori Morimoto1f85e112016-08-03 01:24:05 +0000145
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900146static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
Kuninori Morimoto891caea2017-06-09 00:43:18 +0000147{
Kuninori Morimotof31a1712018-11-21 02:09:16 +0000148 if (dai)
149 return clk_prepare_enable(dai->clk);
150
151 return 0;
Kuninori Morimoto891caea2017-06-09 00:43:18 +0000152}
153
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900154static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
Kuninori Morimoto891caea2017-06-09 00:43:18 +0000155{
Kuninori Morimotof31a1712018-11-21 02:09:16 +0000156 if (dai)
157 clk_disable_unprepare(dai->clk);
Kuninori Morimoto891caea2017-06-09 00:43:18 +0000158}
159
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900160int asoc_simple_parse_clk(struct device *dev,
161 struct device_node *node,
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900162 struct asoc_simple_dai *simple_dai,
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900163 struct snd_soc_dai_link_component *dlc)
Kuninori Morimotobb6fc622016-08-08 05:59:56 +0000164{
165 struct clk *clk;
166 u32 val;
167
168 /*
169 * Parse dai->sysclk come from "clocks = <&xxx>"
170 * (if system has common clock)
171 * or "system-clock-frequency = <xxx>"
172 * or device's module clock.
173 */
Kuninori Morimotoe984fd62017-01-23 07:29:42 +0000174 clk = devm_get_clk_from_child(dev, node, NULL);
Kuninori Morimotobb6fc622016-08-08 05:59:56 +0000175 if (!IS_ERR(clk)) {
176 simple_dai->sysclk = clk_get_rate(clk);
Kuninori Morimoto891caea2017-06-09 00:43:18 +0000177
Kuninori Morimotoc0f46972018-11-21 02:08:59 +0000178 simple_dai->clk = clk;
Kuninori Morimotobb6fc622016-08-08 05:59:56 +0000179 } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
180 simple_dai->sysclk = val;
181 } else {
Kuninori Morimotof1072942019-06-06 13:07:35 +0900182 clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
Kuninori Morimotobb6fc622016-08-08 05:59:56 +0000183 if (!IS_ERR(clk))
184 simple_dai->sysclk = clk_get_rate(clk);
185 }
186
Vitaly Woola728f562017-08-17 13:42:36 +0200187 if (of_property_read_bool(node, "system-clock-direction-out"))
188 simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
189
Kuninori Morimotobb6fc622016-08-08 05:59:56 +0000190 return 0;
191}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900192EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
Kuninori Morimotobb6fc622016-08-08 05:59:56 +0000193
Kuninori Morimotof38df5b2019-03-20 13:55:14 +0900194int asoc_simple_startup(struct snd_pcm_substream *substream)
195{
196 struct snd_soc_pcm_runtime *rtd = substream->private_data;
197 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
198 struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
199 int ret;
200
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900201 ret = asoc_simple_clk_enable(dai_props->cpu_dai);
Kuninori Morimotof38df5b2019-03-20 13:55:14 +0900202 if (ret)
203 return ret;
204
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900205 ret = asoc_simple_clk_enable(dai_props->codec_dai);
Kuninori Morimotof38df5b2019-03-20 13:55:14 +0900206 if (ret)
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900207 asoc_simple_clk_disable(dai_props->cpu_dai);
Kuninori Morimotof38df5b2019-03-20 13:55:14 +0900208
209 return ret;
210}
211EXPORT_SYMBOL_GPL(asoc_simple_startup);
212
Kuninori Morimoto686911b2019-03-20 13:55:27 +0900213void asoc_simple_shutdown(struct snd_pcm_substream *substream)
214{
215 struct snd_soc_pcm_runtime *rtd = substream->private_data;
216 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
217 struct simple_dai_props *dai_props =
218 simple_priv_to_props(priv, rtd->num);
219
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900220 asoc_simple_clk_disable(dai_props->cpu_dai);
Kuninori Morimoto686911b2019-03-20 13:55:27 +0900221
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900222 asoc_simple_clk_disable(dai_props->codec_dai);
Kuninori Morimoto686911b2019-03-20 13:55:27 +0900223}
224EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
225
Kuninori Morimotof48dcbb2019-03-20 13:55:39 +0900226static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
227 unsigned long rate)
228{
229 if (!simple_dai)
230 return 0;
231
232 if (!simple_dai->clk)
233 return 0;
234
235 if (clk_get_rate(simple_dai->clk) == rate)
236 return 0;
237
238 return clk_set_rate(simple_dai->clk, rate);
239}
240
241int asoc_simple_hw_params(struct snd_pcm_substream *substream,
242 struct snd_pcm_hw_params *params)
243{
244 struct snd_soc_pcm_runtime *rtd = substream->private_data;
245 struct snd_soc_dai *codec_dai = rtd->codec_dai;
246 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
247 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
248 struct simple_dai_props *dai_props =
249 simple_priv_to_props(priv, rtd->num);
250 unsigned int mclk, mclk_fs = 0;
251 int ret = 0;
252
253 if (dai_props->mclk_fs)
254 mclk_fs = dai_props->mclk_fs;
255
256 if (mclk_fs) {
257 mclk = params_rate(params) * mclk_fs;
258
259 ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk);
260 if (ret < 0)
261 return ret;
262
263 ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk);
264 if (ret < 0)
265 return ret;
266
267 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
268 SND_SOC_CLOCK_IN);
269 if (ret && ret != -ENOTSUPP)
270 goto err;
271
272 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
273 SND_SOC_CLOCK_OUT);
274 if (ret && ret != -ENOTSUPP)
275 goto err;
276 }
277 return 0;
278err:
279 return ret;
280}
281EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
282
Kuninori Morimoto629f7542019-03-20 13:56:06 +0900283int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
284 struct snd_pcm_hw_params *params)
285{
286 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
287 struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
288
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900289 asoc_simple_convert_fixup(&dai_props->adata, params);
Kuninori Morimoto629f7542019-03-20 13:56:06 +0900290
291 return 0;
292}
293EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
294
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900295static int asoc_simple_init_dai(struct snd_soc_dai *dai,
Kuninori Morimotoad934ca2019-03-20 13:55:52 +0900296 struct asoc_simple_dai *simple_dai)
Kuninori Morimoto21ba62f2016-08-09 05:48:30 +0000297{
298 int ret;
299
Kuninori Morimotof31a1712018-11-21 02:09:16 +0000300 if (!simple_dai)
301 return 0;
302
Kuninori Morimoto21ba62f2016-08-09 05:48:30 +0000303 if (simple_dai->sysclk) {
Vitaly Woola728f562017-08-17 13:42:36 +0200304 ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
305 simple_dai->clk_direction);
Kuninori Morimoto21ba62f2016-08-09 05:48:30 +0000306 if (ret && ret != -ENOTSUPP) {
307 dev_err(dai->dev, "simple-card: set_sysclk error\n");
308 return ret;
309 }
310 }
311
312 if (simple_dai->slots) {
313 ret = snd_soc_dai_set_tdm_slot(dai,
314 simple_dai->tx_slot_mask,
315 simple_dai->rx_slot_mask,
316 simple_dai->slots,
317 simple_dai->slot_width);
318 if (ret && ret != -ENOTSUPP) {
319 dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
320 return ret;
321 }
322 }
323
324 return 0;
325}
Kuninori Morimotoad934ca2019-03-20 13:55:52 +0900326
327int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
328{
329 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
330 struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
331 int ret;
332
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900333 ret = asoc_simple_init_dai(rtd->codec_dai,
334 dai_props->codec_dai);
Kuninori Morimotoad934ca2019-03-20 13:55:52 +0900335 if (ret < 0)
336 return ret;
337
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900338 ret = asoc_simple_init_dai(rtd->cpu_dai,
339 dai_props->cpu_dai);
Kuninori Morimotoad934ca2019-03-20 13:55:52 +0900340 if (ret < 0)
341 return ret;
342
343 return 0;
344}
345EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
Kuninori Morimoto21ba62f2016-08-09 05:48:30 +0000346
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900347void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link)
Kuninori Morimotoc262c9a2016-08-09 05:49:41 +0000348{
Kuninori Morimoto9f3eb912019-06-28 10:49:44 +0900349 /* Assumes platform == cpu */
350 if (!dai_link->platforms->of_node)
351 dai_link->platforms->of_node = dai_link->cpus->of_node;
Kuninori Morimoto794fcee2019-07-10 17:01:12 +0900352
353 /*
354 * DPCM BE can be no platform.
355 * Alloced memory will be waste, but not leak.
356 */
357 if (!dai_link->platforms->of_node)
358 dai_link->num_platforms = 0;
Kuninori Morimotoc262c9a2016-08-09 05:49:41 +0000359}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900360EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
Kuninori Morimotoc262c9a2016-08-09 05:49:41 +0000361
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900362void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
363 int is_single_links)
Kuninori Morimoto983cebd2016-08-10 02:20:19 +0000364{
365 /*
366 * In soc_bind_dai_link() will check cpu name after
367 * of_node matching if dai_link has cpu_dai_name.
368 * but, it will never match if name was created by
369 * fmt_single_name() remove cpu_dai_name if cpu_args
370 * was 0. See:
371 * fmt_single_name()
372 * fmt_multiple_name()
373 */
374 if (is_single_links)
Kuninori Morimotof1072942019-06-06 13:07:35 +0900375 dai_link->cpus->dai_name = NULL;
Kuninori Morimoto983cebd2016-08-10 02:20:19 +0000376}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900377EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
Kuninori Morimoto983cebd2016-08-10 02:20:19 +0000378
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900379int asoc_simple_clean_reference(struct snd_soc_card *card)
Kuninori Morimoto0f4e0712016-08-10 02:21:25 +0000380{
381 struct snd_soc_dai_link *dai_link;
Kuninori Morimoto7fe072b2018-09-18 01:28:49 +0000382 int i;
Kuninori Morimoto0f4e0712016-08-10 02:21:25 +0000383
Kuninori Morimoto7fe072b2018-09-18 01:28:49 +0000384 for_each_card_prelinks(card, i, dai_link) {
Kuninori Morimotof1072942019-06-06 13:07:35 +0900385 of_node_put(dai_link->cpus->of_node);
Kuninori Morimoto2967e5e2018-08-31 03:09:47 +0000386 of_node_put(dai_link->codecs->of_node);
Kuninori Morimoto0f4e0712016-08-10 02:21:25 +0000387 }
388 return 0;
389}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900390EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
Kuninori Morimoto0f4e0712016-08-10 02:21:25 +0000391
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900392int asoc_simple_parse_routing(struct snd_soc_card *card,
393 char *prefix)
Kuninori Morimoto3296d072017-06-15 00:25:02 +0000394{
395 struct device_node *node = card->dev->of_node;
396 char prop[128];
397
398 if (!prefix)
399 prefix = "";
400
401 snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
402
Kuninori Morimoto33404f32018-11-21 02:11:13 +0000403 if (!of_property_read_bool(node, prop))
404 return 0;
Kuninori Morimoto3296d072017-06-15 00:25:02 +0000405
406 return snd_soc_of_parse_audio_routing(card, prop);
407}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900408EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
Kuninori Morimoto3296d072017-06-15 00:25:02 +0000409
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900410int asoc_simple_parse_widgets(struct snd_soc_card *card,
411 char *prefix)
Kuninori Morimotob31f11d2017-06-16 01:38:50 +0000412{
413 struct device_node *node = card->dev->of_node;
414 char prop[128];
415
416 if (!prefix)
417 prefix = "";
418
419 snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
420
421 if (of_property_read_bool(node, prop))
422 return snd_soc_of_parse_audio_simple_widgets(card, prop);
423
424 /* no widgets is not error */
425 return 0;
426}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900427EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
Kuninori Morimotob31f11d2017-06-16 01:38:50 +0000428
Paul Cercueil90194282019-04-26 04:25:49 +0200429int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
430 char *prefix)
431{
432 const unsigned int nb_controls_max = 16;
433 const char **strings, *control_name;
434 struct snd_kcontrol_new *controls;
435 struct device *dev = card->dev;
436 unsigned int i, nb_controls;
437 char prop[128];
438 int ret;
439
440 if (!prefix)
441 prefix = "";
442
443 snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
444
445 if (!of_property_read_bool(dev->of_node, prop))
446 return 0;
447
448 strings = devm_kcalloc(dev, nb_controls_max,
449 sizeof(*strings), GFP_KERNEL);
450 if (!strings)
451 return -ENOMEM;
452
453 ret = of_property_read_string_array(dev->of_node, prop,
454 strings, nb_controls_max);
455 if (ret < 0)
456 return ret;
457
458 nb_controls = (unsigned int)ret;
459
460 controls = devm_kcalloc(dev, nb_controls,
461 sizeof(*controls), GFP_KERNEL);
462 if (!controls)
463 return -ENOMEM;
464
465 for (i = 0; i < nb_controls; i++) {
466 control_name = devm_kasprintf(dev, GFP_KERNEL,
467 "%s Switch", strings[i]);
468 if (!control_name)
469 return -ENOMEM;
470
471 controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
472 controls[i].name = control_name;
473 controls[i].info = snd_soc_dapm_info_pin_switch;
474 controls[i].get = snd_soc_dapm_get_pin_switch;
475 controls[i].put = snd_soc_dapm_put_pin_switch;
476 controls[i].private_value = (unsigned long)strings[i];
477 }
478
479 card->controls = controls;
480 card->num_controls = nb_controls;
481
482 return 0;
483}
484EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
485
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900486int asoc_simple_init_jack(struct snd_soc_card *card,
487 struct asoc_simple_jack *sjack,
488 int is_hp, char *prefix)
Katsuhiro Suzuki62c2c9f2018-06-11 17:32:12 +0900489{
490 struct device *dev = card->dev;
491 enum of_gpio_flags flags;
492 char prop[128];
493 char *pin_name;
494 char *gpio_name;
495 int mask;
496 int det;
497
498 if (!prefix)
499 prefix = "";
500
501 sjack->gpio.gpio = -ENOENT;
502
503 if (is_hp) {
504 snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
505 pin_name = "Headphones";
506 gpio_name = "Headphone detection";
507 mask = SND_JACK_HEADPHONE;
508 } else {
509 snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
510 pin_name = "Mic Jack";
511 gpio_name = "Mic detection";
512 mask = SND_JACK_MICROPHONE;
513 }
514
515 det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
516 if (det == -EPROBE_DEFER)
517 return -EPROBE_DEFER;
518
519 if (gpio_is_valid(det)) {
520 sjack->pin.pin = pin_name;
521 sjack->pin.mask = mask;
522
523 sjack->gpio.name = gpio_name;
524 sjack->gpio.report = mask;
525 sjack->gpio.gpio = det;
526 sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW);
527 sjack->gpio.debounce_time = 150;
528
529 snd_soc_card_jack_new(card, pin_name, mask,
530 &sjack->jack,
531 &sjack->pin, 1);
532
533 snd_soc_jack_add_gpios(&sjack->jack, 1,
534 &sjack->gpio);
535 }
536
537 return 0;
538}
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900539EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
Katsuhiro Suzuki62c2c9f2018-06-11 17:32:12 +0900540
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900541int asoc_simple_init_priv(struct asoc_simple_priv *priv,
542 struct link_info *li)
Kuninori Morimoto65a50562019-03-20 13:56:26 +0900543{
544 struct snd_soc_card *card = simple_priv_to_card(priv);
545 struct device *dev = simple_priv_to_dev(priv);
546 struct snd_soc_dai_link *dai_link;
547 struct simple_dai_props *dai_props;
548 struct asoc_simple_dai *dais;
Kuninori Morimoto008fe4e2019-03-20 13:57:02 +0900549 struct snd_soc_codec_conf *cconf = NULL;
Kuninori Morimoto65a50562019-03-20 13:56:26 +0900550 int i;
551
552 dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
553 dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
554 dais = devm_kcalloc(dev, li->dais, sizeof(*dais), GFP_KERNEL);
Kuninori Morimoto65a50562019-03-20 13:56:26 +0900555 if (!dai_props || !dai_link || !dais)
556 return -ENOMEM;
557
Kuninori Morimoto008fe4e2019-03-20 13:57:02 +0900558 if (li->conf) {
559 cconf = devm_kcalloc(dev, li->conf, sizeof(*cconf), GFP_KERNEL);
560 if (!cconf)
561 return -ENOMEM;
562 }
563
Kuninori Morimoto65a50562019-03-20 13:56:26 +0900564 /*
565 * Use snd_soc_dai_link_component instead of legacy style
566 * It is codec only. but cpu/platform will be supported in the future.
567 * see
568 * soc-core.c :: snd_soc_init_multicodec()
569 *
570 * "platform" might be removed
571 * see
Kuninori Morimotoad11e592019-03-20 13:56:50 +0900572 * simple-card-utils.c :: asoc_simple_canonicalize_platform()
Kuninori Morimoto65a50562019-03-20 13:56:26 +0900573 */
574 for (i = 0; i < li->link; i++) {
Kuninori Morimotof1072942019-06-06 13:07:35 +0900575 dai_link[i].cpus = &dai_props[i].cpus;
576 dai_link[i].num_cpus = 1;
Kuninori Morimoto65a50562019-03-20 13:56:26 +0900577 dai_link[i].codecs = &dai_props[i].codecs;
578 dai_link[i].num_codecs = 1;
579 dai_link[i].platforms = &dai_props[i].platforms;
580 dai_link[i].num_platforms = 1;
581 }
582
583 priv->dai_props = dai_props;
584 priv->dai_link = dai_link;
585 priv->dais = dais;
586 priv->codec_conf = cconf;
587
588 card->dai_link = priv->dai_link;
589 card->num_links = li->link;
590 card->codec_conf = cconf;
591 card->num_configs = li->conf;
592
593 return 0;
594}
595EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
596
Kuninori Morimoto1f85e112016-08-03 01:24:05 +0000597/* Module information */
598MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
599MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
600MODULE_LICENSE("GPL v2");