1 | // 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 | |
7 | #include <linux/clk.h> |
8 | #include <linux/gpio/consumer.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_graph.h> |
12 | #include <sound/jack.h> |
13 | #include <sound/pcm_params.h> |
14 | #include <sound/simple_card_utils.h> |
15 | |
16 | static void simple_fixup_sample_fmt(struct simple_util_data *data, |
17 | struct snd_pcm_hw_params *params) |
18 | { |
19 | int i; |
20 | struct snd_mask *mask = hw_param_mask(params, |
21 | SNDRV_PCM_HW_PARAM_FORMAT); |
22 | struct { |
23 | char *fmt; |
24 | u32 val; |
25 | } of_sample_fmt_table[] = { |
26 | { "s8" , SNDRV_PCM_FORMAT_S8}, |
27 | { "s16_le" , SNDRV_PCM_FORMAT_S16_LE}, |
28 | { "s24_le" , SNDRV_PCM_FORMAT_S24_LE}, |
29 | { "s24_3le" , SNDRV_PCM_FORMAT_S24_3LE}, |
30 | { "s32_le" , SNDRV_PCM_FORMAT_S32_LE}, |
31 | }; |
32 | |
33 | for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) { |
34 | if (!strcmp(data->convert_sample_format, |
35 | of_sample_fmt_table[i].fmt)) { |
36 | snd_mask_none(mask); |
37 | snd_mask_set(mask, val: of_sample_fmt_table[i].val); |
38 | break; |
39 | } |
40 | } |
41 | } |
42 | |
43 | void simple_util_parse_convert(struct device_node *np, |
44 | char *prefix, |
45 | struct simple_util_data *data) |
46 | { |
47 | char prop[128]; |
48 | |
49 | if (!prefix) |
50 | prefix = "" ; |
51 | |
52 | /* sampling rate convert */ |
53 | snprintf(buf: prop, size: sizeof(prop), fmt: "%s%s" , prefix, "convert-rate" ); |
54 | of_property_read_u32(np, propname: prop, out_value: &data->convert_rate); |
55 | |
56 | /* channels transfer */ |
57 | snprintf(buf: prop, size: sizeof(prop), fmt: "%s%s" , prefix, "convert-channels" ); |
58 | of_property_read_u32(np, propname: prop, out_value: &data->convert_channels); |
59 | |
60 | /* convert sample format */ |
61 | snprintf(buf: prop, size: sizeof(prop), fmt: "%s%s" , prefix, "convert-sample-format" ); |
62 | of_property_read_string(np, propname: prop, out_string: &data->convert_sample_format); |
63 | } |
64 | EXPORT_SYMBOL_GPL(simple_util_parse_convert); |
65 | |
66 | /** |
67 | * simple_util_is_convert_required() - Query if HW param conversion was requested |
68 | * @data: Link data. |
69 | * |
70 | * Returns true if any HW param conversion was requested for this DAI link with |
71 | * any "convert-xxx" properties. |
72 | */ |
73 | bool simple_util_is_convert_required(const struct simple_util_data *data) |
74 | { |
75 | return data->convert_rate || |
76 | data->convert_channels || |
77 | data->convert_sample_format; |
78 | } |
79 | EXPORT_SYMBOL_GPL(simple_util_is_convert_required); |
80 | |
81 | int simple_util_parse_daifmt(struct device *dev, |
82 | struct device_node *node, |
83 | struct device_node *codec, |
84 | char *prefix, |
85 | unsigned int *retfmt) |
86 | { |
87 | struct device_node *bitclkmaster = NULL; |
88 | struct device_node *framemaster = NULL; |
89 | unsigned int daifmt; |
90 | |
91 | daifmt = snd_soc_daifmt_parse_format(np: node, prefix); |
92 | |
93 | snd_soc_daifmt_parse_clock_provider_as_phandle(np: node, prefix, bitclkmaster: &bitclkmaster, framemaster: &framemaster); |
94 | if (!bitclkmaster && !framemaster) { |
95 | /* |
96 | * No dai-link level and master setting was not found from |
97 | * sound node level, revert back to legacy DT parsing and |
98 | * take the settings from codec node. |
99 | */ |
100 | dev_dbg(dev, "Revert to legacy daifmt parsing\n" ); |
101 | |
102 | daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL); |
103 | } else { |
104 | daifmt |= snd_soc_daifmt_clock_provider_from_bitmap( |
105 | bit_frame: ((codec == bitclkmaster) << 4) | (codec == framemaster)); |
106 | } |
107 | |
108 | of_node_put(node: bitclkmaster); |
109 | of_node_put(node: framemaster); |
110 | |
111 | *retfmt = daifmt; |
112 | |
113 | return 0; |
114 | } |
115 | EXPORT_SYMBOL_GPL(simple_util_parse_daifmt); |
116 | |
117 | int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, |
118 | struct simple_util_dai *dai) |
119 | { |
120 | u32 *array_values, *p; |
121 | int n, i, ret; |
122 | |
123 | if (!of_property_read_bool(np, propname: "dai-tdm-slot-width-map" )) |
124 | return 0; |
125 | |
126 | n = of_property_count_elems_of_size(np, propname: "dai-tdm-slot-width-map" , elem_size: sizeof(u32)); |
127 | if (n % 3) { |
128 | dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n" ); |
129 | return -EINVAL; |
130 | } |
131 | |
132 | dai->tdm_width_map = devm_kcalloc(dev, n, size: sizeof(*dai->tdm_width_map), GFP_KERNEL); |
133 | if (!dai->tdm_width_map) |
134 | return -ENOMEM; |
135 | |
136 | array_values = kcalloc(n, size: sizeof(*array_values), GFP_KERNEL); |
137 | if (!array_values) |
138 | return -ENOMEM; |
139 | |
140 | ret = of_property_read_u32_array(np, propname: "dai-tdm-slot-width-map" , out_values: array_values, sz: n); |
141 | if (ret < 0) { |
142 | dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n" , ret); |
143 | goto out; |
144 | } |
145 | |
146 | p = array_values; |
147 | for (i = 0; i < n / 3; ++i) { |
148 | dai->tdm_width_map[i].sample_bits = *p++; |
149 | dai->tdm_width_map[i].slot_width = *p++; |
150 | dai->tdm_width_map[i].slot_count = *p++; |
151 | } |
152 | |
153 | dai->n_tdm_widths = i; |
154 | ret = 0; |
155 | out: |
156 | kfree(objp: array_values); |
157 | |
158 | return ret; |
159 | } |
160 | EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map); |
161 | |
162 | int simple_util_set_dailink_name(struct device *dev, |
163 | struct snd_soc_dai_link *dai_link, |
164 | const char *fmt, ...) |
165 | { |
166 | va_list ap; |
167 | char *name = NULL; |
168 | int ret = -ENOMEM; |
169 | |
170 | va_start(ap, fmt); |
171 | name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap); |
172 | va_end(ap); |
173 | |
174 | if (name) { |
175 | ret = 0; |
176 | |
177 | dai_link->name = name; |
178 | dai_link->stream_name = name; |
179 | } |
180 | |
181 | return ret; |
182 | } |
183 | EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); |
184 | |
185 | int simple_util_parse_card_name(struct snd_soc_card *card, |
186 | char *prefix) |
187 | { |
188 | int ret; |
189 | |
190 | if (!prefix) |
191 | prefix = "" ; |
192 | |
193 | /* Parse the card name from DT */ |
194 | ret = snd_soc_of_parse_card_name(card, propname: "label" ); |
195 | if (ret < 0 || !card->name) { |
196 | char prop[128]; |
197 | |
198 | snprintf(buf: prop, size: sizeof(prop), fmt: "%sname" , prefix); |
199 | ret = snd_soc_of_parse_card_name(card, propname: prop); |
200 | if (ret < 0) |
201 | return ret; |
202 | } |
203 | |
204 | if (!card->name && card->dai_link) |
205 | card->name = card->dai_link->name; |
206 | |
207 | return 0; |
208 | } |
209 | EXPORT_SYMBOL_GPL(simple_util_parse_card_name); |
210 | |
211 | static int simple_clk_enable(struct simple_util_dai *dai) |
212 | { |
213 | if (dai) |
214 | return clk_prepare_enable(clk: dai->clk); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static void simple_clk_disable(struct simple_util_dai *dai) |
220 | { |
221 | if (dai) |
222 | clk_disable_unprepare(clk: dai->clk); |
223 | } |
224 | |
225 | int simple_util_parse_clk(struct device *dev, |
226 | struct device_node *node, |
227 | struct simple_util_dai *simple_dai, |
228 | struct snd_soc_dai_link_component *dlc) |
229 | { |
230 | struct clk *clk; |
231 | u32 val; |
232 | |
233 | /* |
234 | * Parse dai->sysclk come from "clocks = <&xxx>" |
235 | * (if system has common clock) |
236 | * or "system-clock-frequency = <xxx>" |
237 | * or device's module clock. |
238 | */ |
239 | clk = devm_get_clk_from_child(dev, np: node, NULL); |
240 | simple_dai->clk_fixed = of_property_read_bool( |
241 | np: node, propname: "system-clock-fixed" ); |
242 | if (!IS_ERR(ptr: clk)) { |
243 | simple_dai->sysclk = clk_get_rate(clk); |
244 | |
245 | simple_dai->clk = clk; |
246 | } else if (!of_property_read_u32(np: node, propname: "system-clock-frequency" , out_value: &val)) { |
247 | simple_dai->sysclk = val; |
248 | simple_dai->clk_fixed = true; |
249 | } else { |
250 | clk = devm_get_clk_from_child(dev, np: dlc->of_node, NULL); |
251 | if (!IS_ERR(ptr: clk)) |
252 | simple_dai->sysclk = clk_get_rate(clk); |
253 | } |
254 | |
255 | if (of_property_read_bool(np: node, propname: "system-clock-direction-out" )) |
256 | simple_dai->clk_direction = SND_SOC_CLOCK_OUT; |
257 | |
258 | return 0; |
259 | } |
260 | EXPORT_SYMBOL_GPL(simple_util_parse_clk); |
261 | |
262 | static int simple_check_fixed_sysclk(struct device *dev, |
263 | struct simple_util_dai *dai, |
264 | unsigned int *fixed_sysclk) |
265 | { |
266 | if (dai->clk_fixed) { |
267 | if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) { |
268 | dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n" , |
269 | *fixed_sysclk, dai->sysclk); |
270 | return -EINVAL; |
271 | } |
272 | *fixed_sysclk = dai->sysclk; |
273 | } |
274 | |
275 | return 0; |
276 | } |
277 | |
278 | int simple_util_startup(struct snd_pcm_substream *substream) |
279 | { |
280 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
281 | struct simple_util_priv *priv = snd_soc_card_get_drvdata(card: rtd->card); |
282 | struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); |
283 | struct simple_util_dai *dai; |
284 | unsigned int fixed_sysclk = 0; |
285 | int i1, i2, i; |
286 | int ret; |
287 | |
288 | for_each_prop_dai_cpu(props, i1, dai) { |
289 | ret = simple_clk_enable(dai); |
290 | if (ret) |
291 | goto cpu_err; |
292 | ret = simple_check_fixed_sysclk(dev: rtd->dev, dai, fixed_sysclk: &fixed_sysclk); |
293 | if (ret) |
294 | goto cpu_err; |
295 | } |
296 | |
297 | for_each_prop_dai_codec(props, i2, dai) { |
298 | ret = simple_clk_enable(dai); |
299 | if (ret) |
300 | goto codec_err; |
301 | ret = simple_check_fixed_sysclk(dev: rtd->dev, dai, fixed_sysclk: &fixed_sysclk); |
302 | if (ret) |
303 | goto codec_err; |
304 | } |
305 | |
306 | if (fixed_sysclk && props->mclk_fs) { |
307 | unsigned int fixed_rate = fixed_sysclk / props->mclk_fs; |
308 | |
309 | if (fixed_sysclk % props->mclk_fs) { |
310 | dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n" , |
311 | fixed_sysclk, props->mclk_fs); |
312 | ret = -EINVAL; |
313 | goto codec_err; |
314 | } |
315 | ret = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, SNDRV_PCM_HW_PARAM_RATE, |
316 | min: fixed_rate, max: fixed_rate); |
317 | if (ret < 0) |
318 | goto codec_err; |
319 | } |
320 | |
321 | return 0; |
322 | |
323 | codec_err: |
324 | for_each_prop_dai_codec(props, i, dai) { |
325 | if (i >= i2) |
326 | break; |
327 | simple_clk_disable(dai); |
328 | } |
329 | cpu_err: |
330 | for_each_prop_dai_cpu(props, i, dai) { |
331 | if (i >= i1) |
332 | break; |
333 | simple_clk_disable(dai); |
334 | } |
335 | return ret; |
336 | } |
337 | EXPORT_SYMBOL_GPL(simple_util_startup); |
338 | |
339 | void simple_util_shutdown(struct snd_pcm_substream *substream) |
340 | { |
341 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
342 | struct simple_util_priv *priv = snd_soc_card_get_drvdata(card: rtd->card); |
343 | struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); |
344 | struct simple_util_dai *dai; |
345 | int i; |
346 | |
347 | for_each_prop_dai_cpu(props, i, dai) { |
348 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, i); |
349 | |
350 | if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(dai: cpu_dai)) |
351 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
352 | clk_id: 0, freq: 0, SND_SOC_CLOCK_OUT); |
353 | |
354 | simple_clk_disable(dai); |
355 | } |
356 | for_each_prop_dai_codec(props, i, dai) { |
357 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, i); |
358 | |
359 | if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(dai: codec_dai)) |
360 | snd_soc_dai_set_sysclk(dai: codec_dai, |
361 | clk_id: 0, freq: 0, SND_SOC_CLOCK_IN); |
362 | |
363 | simple_clk_disable(dai); |
364 | } |
365 | } |
366 | EXPORT_SYMBOL_GPL(simple_util_shutdown); |
367 | |
368 | static int simple_set_clk_rate(struct device *dev, |
369 | struct simple_util_dai *simple_dai, |
370 | unsigned long rate) |
371 | { |
372 | if (!simple_dai) |
373 | return 0; |
374 | |
375 | if (simple_dai->clk_fixed && rate != simple_dai->sysclk) { |
376 | dev_err(dev, "dai %s invalid clock rate %lu\n" , simple_dai->name, rate); |
377 | return -EINVAL; |
378 | } |
379 | |
380 | if (!simple_dai->clk) |
381 | return 0; |
382 | |
383 | if (clk_get_rate(clk: simple_dai->clk) == rate) |
384 | return 0; |
385 | |
386 | return clk_set_rate(clk: simple_dai->clk, rate); |
387 | } |
388 | |
389 | static int simple_set_tdm(struct snd_soc_dai *dai, |
390 | struct simple_util_dai *simple_dai, |
391 | struct snd_pcm_hw_params *params) |
392 | { |
393 | int sample_bits = params_width(p: params); |
394 | int slot_width, slot_count; |
395 | int i, ret; |
396 | |
397 | if (!simple_dai || !simple_dai->tdm_width_map) |
398 | return 0; |
399 | |
400 | slot_width = simple_dai->slot_width; |
401 | slot_count = simple_dai->slots; |
402 | |
403 | if (slot_width == 0) |
404 | slot_width = sample_bits; |
405 | |
406 | for (i = 0; i < simple_dai->n_tdm_widths; ++i) { |
407 | if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) { |
408 | slot_width = simple_dai->tdm_width_map[i].slot_width; |
409 | slot_count = simple_dai->tdm_width_map[i].slot_count; |
410 | break; |
411 | } |
412 | } |
413 | |
414 | ret = snd_soc_dai_set_tdm_slot(dai, |
415 | tx_mask: simple_dai->tx_slot_mask, |
416 | rx_mask: simple_dai->rx_slot_mask, |
417 | slots: slot_count, |
418 | slot_width); |
419 | if (ret && ret != -ENOTSUPP) { |
420 | dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n" , ret); |
421 | return ret; |
422 | } |
423 | |
424 | return 0; |
425 | } |
426 | |
427 | int simple_util_hw_params(struct snd_pcm_substream *substream, |
428 | struct snd_pcm_hw_params *params) |
429 | { |
430 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
431 | struct simple_util_dai *pdai; |
432 | struct snd_soc_dai *sdai; |
433 | struct simple_util_priv *priv = snd_soc_card_get_drvdata(card: rtd->card); |
434 | struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); |
435 | unsigned int mclk, mclk_fs = 0; |
436 | int i, ret; |
437 | |
438 | if (props->mclk_fs) |
439 | mclk_fs = props->mclk_fs; |
440 | |
441 | if (mclk_fs) { |
442 | struct snd_soc_component *component; |
443 | mclk = params_rate(p: params) * mclk_fs; |
444 | |
445 | for_each_prop_dai_codec(props, i, pdai) { |
446 | ret = simple_set_clk_rate(dev: rtd->dev, simple_dai: pdai, rate: mclk); |
447 | if (ret < 0) |
448 | return ret; |
449 | } |
450 | |
451 | for_each_prop_dai_cpu(props, i, pdai) { |
452 | ret = simple_set_clk_rate(dev: rtd->dev, simple_dai: pdai, rate: mclk); |
453 | if (ret < 0) |
454 | return ret; |
455 | } |
456 | |
457 | /* Ensure sysclk is set on all components in case any |
458 | * (such as platform components) are missed by calls to |
459 | * snd_soc_dai_set_sysclk. |
460 | */ |
461 | for_each_rtd_components(rtd, i, component) { |
462 | ret = snd_soc_component_set_sysclk(component, clk_id: 0, source: 0, |
463 | freq: mclk, SND_SOC_CLOCK_IN); |
464 | if (ret && ret != -ENOTSUPP) |
465 | return ret; |
466 | } |
467 | |
468 | for_each_rtd_codec_dais(rtd, i, sdai) { |
469 | ret = snd_soc_dai_set_sysclk(dai: sdai, clk_id: 0, freq: mclk, SND_SOC_CLOCK_IN); |
470 | if (ret && ret != -ENOTSUPP) |
471 | return ret; |
472 | } |
473 | |
474 | for_each_rtd_cpu_dais(rtd, i, sdai) { |
475 | ret = snd_soc_dai_set_sysclk(dai: sdai, clk_id: 0, freq: mclk, SND_SOC_CLOCK_OUT); |
476 | if (ret && ret != -ENOTSUPP) |
477 | return ret; |
478 | } |
479 | } |
480 | |
481 | for_each_prop_dai_codec(props, i, pdai) { |
482 | sdai = snd_soc_rtd_to_codec(rtd, i); |
483 | ret = simple_set_tdm(dai: sdai, simple_dai: pdai, params); |
484 | if (ret < 0) |
485 | return ret; |
486 | } |
487 | |
488 | for_each_prop_dai_cpu(props, i, pdai) { |
489 | sdai = snd_soc_rtd_to_cpu(rtd, i); |
490 | ret = simple_set_tdm(dai: sdai, simple_dai: pdai, params); |
491 | if (ret < 0) |
492 | return ret; |
493 | } |
494 | |
495 | return 0; |
496 | } |
497 | EXPORT_SYMBOL_GPL(simple_util_hw_params); |
498 | |
499 | int simple_util_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, |
500 | struct snd_pcm_hw_params *params) |
501 | { |
502 | struct simple_util_priv *priv = snd_soc_card_get_drvdata(card: rtd->card); |
503 | struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); |
504 | struct simple_util_data *data = &dai_props->adata; |
505 | struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); |
506 | struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); |
507 | |
508 | if (data->convert_rate) |
509 | rate->min = |
510 | rate->max = data->convert_rate; |
511 | |
512 | if (data->convert_channels) |
513 | channels->min = |
514 | channels->max = data->convert_channels; |
515 | |
516 | if (data->convert_sample_format) |
517 | simple_fixup_sample_fmt(data, params); |
518 | |
519 | return 0; |
520 | } |
521 | EXPORT_SYMBOL_GPL(simple_util_be_hw_params_fixup); |
522 | |
523 | static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simple_dai) |
524 | { |
525 | int ret; |
526 | |
527 | if (!simple_dai) |
528 | return 0; |
529 | |
530 | if (simple_dai->sysclk) { |
531 | ret = snd_soc_dai_set_sysclk(dai, clk_id: 0, freq: simple_dai->sysclk, |
532 | dir: simple_dai->clk_direction); |
533 | if (ret && ret != -ENOTSUPP) { |
534 | dev_err(dai->dev, "simple-card: set_sysclk error\n" ); |
535 | return ret; |
536 | } |
537 | } |
538 | |
539 | if (simple_dai->slots) { |
540 | ret = snd_soc_dai_set_tdm_slot(dai, |
541 | tx_mask: simple_dai->tx_slot_mask, |
542 | rx_mask: simple_dai->rx_slot_mask, |
543 | slots: simple_dai->slots, |
544 | slot_width: simple_dai->slot_width); |
545 | if (ret && ret != -ENOTSUPP) { |
546 | dev_err(dai->dev, "simple-card: set_tdm_slot error\n" ); |
547 | return ret; |
548 | } |
549 | } |
550 | |
551 | return 0; |
552 | } |
553 | |
554 | static inline int simple_component_is_codec(struct snd_soc_component *component) |
555 | { |
556 | return component->driver->endianness; |
557 | } |
558 | |
559 | static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd, |
560 | struct simple_dai_props *dai_props) |
561 | { |
562 | struct snd_soc_dai_link *dai_link = rtd->dai_link; |
563 | struct snd_soc_component *component; |
564 | struct snd_soc_pcm_stream *c2c_params; |
565 | struct snd_pcm_hardware hw; |
566 | int i, ret, stream; |
567 | |
568 | /* Do nothing if it already has Codec2Codec settings */ |
569 | if (dai_link->c2c_params) |
570 | return 0; |
571 | |
572 | /* Do nothing if it was DPCM :: BE */ |
573 | if (dai_link->no_pcm) |
574 | return 0; |
575 | |
576 | /* Only Codecs */ |
577 | for_each_rtd_components(rtd, i, component) { |
578 | if (!simple_component_is_codec(component)) |
579 | return 0; |
580 | } |
581 | |
582 | /* Assumes the capabilities are the same for all supported streams */ |
583 | for_each_pcm_streams(stream) { |
584 | ret = snd_soc_runtime_calc_hw(rtd, hw: &hw, stream); |
585 | if (ret == 0) |
586 | break; |
587 | } |
588 | |
589 | if (ret < 0) { |
590 | dev_err(rtd->dev, "simple-card: no valid dai_link params\n" ); |
591 | return ret; |
592 | } |
593 | |
594 | c2c_params = devm_kzalloc(dev: rtd->dev, size: sizeof(*c2c_params), GFP_KERNEL); |
595 | if (!c2c_params) |
596 | return -ENOMEM; |
597 | |
598 | c2c_params->formats = hw.formats; |
599 | c2c_params->rates = hw.rates; |
600 | c2c_params->rate_min = hw.rate_min; |
601 | c2c_params->rate_max = hw.rate_max; |
602 | c2c_params->channels_min = hw.channels_min; |
603 | c2c_params->channels_max = hw.channels_max; |
604 | |
605 | dai_link->c2c_params = c2c_params; |
606 | dai_link->num_c2c_params = 1; |
607 | |
608 | return 0; |
609 | } |
610 | |
611 | int simple_util_dai_init(struct snd_soc_pcm_runtime *rtd) |
612 | { |
613 | struct simple_util_priv *priv = snd_soc_card_get_drvdata(card: rtd->card); |
614 | struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); |
615 | struct simple_util_dai *dai; |
616 | int i, ret; |
617 | |
618 | for_each_prop_dai_codec(props, i, dai) { |
619 | ret = simple_init_dai(snd_soc_rtd_to_codec(rtd, i), simple_dai: dai); |
620 | if (ret < 0) |
621 | return ret; |
622 | } |
623 | for_each_prop_dai_cpu(props, i, dai) { |
624 | ret = simple_init_dai(snd_soc_rtd_to_cpu(rtd, i), simple_dai: dai); |
625 | if (ret < 0) |
626 | return ret; |
627 | } |
628 | |
629 | ret = simple_init_for_codec2codec(rtd, dai_props: props); |
630 | if (ret < 0) |
631 | return ret; |
632 | |
633 | return 0; |
634 | } |
635 | EXPORT_SYMBOL_GPL(simple_util_dai_init); |
636 | |
637 | void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platforms, |
638 | struct snd_soc_dai_link_component *cpus) |
639 | { |
640 | /* |
641 | * Assumes Platform == CPU |
642 | * |
643 | * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform |
644 | * are different Component, but are sharing same component->dev. |
645 | * |
646 | * Let's assume Platform is same as CPU if it doesn't identify Platform on DT. |
647 | * see |
648 | * simple-card.c :: simple_count_noml() |
649 | */ |
650 | if (!platforms->of_node) |
651 | snd_soc_dlc_use_cpu_as_platform(platforms, cpus); |
652 | } |
653 | EXPORT_SYMBOL_GPL(simple_util_canonicalize_platform); |
654 | |
655 | void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, |
656 | int is_single_links) |
657 | { |
658 | /* |
659 | * In soc_bind_dai_link() will check cpu name after |
660 | * of_node matching if dai_link has cpu_dai_name. |
661 | * but, it will never match if name was created by |
662 | * fmt_single_name() remove cpu_dai_name if cpu_args |
663 | * was 0. See: |
664 | * fmt_single_name() |
665 | * fmt_multiple_name() |
666 | */ |
667 | if (is_single_links) |
668 | cpus->dai_name = NULL; |
669 | } |
670 | EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); |
671 | |
672 | void simple_util_clean_reference(struct snd_soc_card *card) |
673 | { |
674 | struct snd_soc_dai_link *dai_link; |
675 | struct snd_soc_dai_link_component *cpu; |
676 | struct snd_soc_dai_link_component *codec; |
677 | int i, j; |
678 | |
679 | for_each_card_prelinks(card, i, dai_link) { |
680 | for_each_link_cpus(dai_link, j, cpu) |
681 | of_node_put(node: cpu->of_node); |
682 | for_each_link_codecs(dai_link, j, codec) |
683 | of_node_put(node: codec->of_node); |
684 | } |
685 | } |
686 | EXPORT_SYMBOL_GPL(simple_util_clean_reference); |
687 | |
688 | int simple_util_parse_routing(struct snd_soc_card *card, |
689 | char *prefix) |
690 | { |
691 | struct device_node *node = card->dev->of_node; |
692 | char prop[128]; |
693 | |
694 | if (!prefix) |
695 | prefix = "" ; |
696 | |
697 | snprintf(buf: prop, size: sizeof(prop), fmt: "%s%s" , prefix, "routing" ); |
698 | |
699 | if (!of_property_read_bool(np: node, propname: prop)) |
700 | return 0; |
701 | |
702 | return snd_soc_of_parse_audio_routing(card, propname: prop); |
703 | } |
704 | EXPORT_SYMBOL_GPL(simple_util_parse_routing); |
705 | |
706 | int simple_util_parse_widgets(struct snd_soc_card *card, |
707 | char *prefix) |
708 | { |
709 | struct device_node *node = card->dev->of_node; |
710 | char prop[128]; |
711 | |
712 | if (!prefix) |
713 | prefix = "" ; |
714 | |
715 | snprintf(buf: prop, size: sizeof(prop), fmt: "%s%s" , prefix, "widgets" ); |
716 | |
717 | if (of_property_read_bool(np: node, propname: prop)) |
718 | return snd_soc_of_parse_audio_simple_widgets(card, propname: prop); |
719 | |
720 | /* no widgets is not error */ |
721 | return 0; |
722 | } |
723 | EXPORT_SYMBOL_GPL(simple_util_parse_widgets); |
724 | |
725 | int simple_util_parse_pin_switches(struct snd_soc_card *card, |
726 | char *prefix) |
727 | { |
728 | char prop[128]; |
729 | |
730 | if (!prefix) |
731 | prefix = "" ; |
732 | |
733 | snprintf(buf: prop, size: sizeof(prop), fmt: "%s%s" , prefix, "pin-switches" ); |
734 | |
735 | return snd_soc_of_parse_pin_switches(card, prop); |
736 | } |
737 | EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); |
738 | |
739 | int simple_util_init_jack(struct snd_soc_card *card, |
740 | struct simple_util_jack *sjack, |
741 | int is_hp, char *prefix, |
742 | char *pin) |
743 | { |
744 | struct device *dev = card->dev; |
745 | struct gpio_desc *desc; |
746 | char prop[128]; |
747 | char *pin_name; |
748 | char *gpio_name; |
749 | int mask; |
750 | int error; |
751 | |
752 | if (!prefix) |
753 | prefix = "" ; |
754 | |
755 | sjack->gpio.gpio = -ENOENT; |
756 | |
757 | if (is_hp) { |
758 | snprintf(buf: prop, size: sizeof(prop), fmt: "%shp-det" , prefix); |
759 | pin_name = pin ? pin : "Headphones" ; |
760 | gpio_name = "Headphone detection" ; |
761 | mask = SND_JACK_HEADPHONE; |
762 | } else { |
763 | snprintf(buf: prop, size: sizeof(prop), fmt: "%smic-det" , prefix); |
764 | pin_name = pin ? pin : "Mic Jack" ; |
765 | gpio_name = "Mic detection" ; |
766 | mask = SND_JACK_MICROPHONE; |
767 | } |
768 | |
769 | desc = gpiod_get_optional(dev, con_id: prop, flags: GPIOD_IN); |
770 | error = PTR_ERR_OR_ZERO(ptr: desc); |
771 | if (error) |
772 | return error; |
773 | |
774 | if (desc) { |
775 | error = gpiod_set_consumer_name(desc, name: gpio_name); |
776 | if (error) |
777 | return error; |
778 | |
779 | sjack->pin.pin = pin_name; |
780 | sjack->pin.mask = mask; |
781 | |
782 | sjack->gpio.name = gpio_name; |
783 | sjack->gpio.report = mask; |
784 | sjack->gpio.desc = desc; |
785 | sjack->gpio.debounce_time = 150; |
786 | |
787 | snd_soc_card_jack_new_pins(card, id: pin_name, type: mask, jack: &sjack->jack, |
788 | pins: &sjack->pin, num_pins: 1); |
789 | |
790 | snd_soc_jack_add_gpios(jack: &sjack->jack, count: 1, gpios: &sjack->gpio); |
791 | } |
792 | |
793 | return 0; |
794 | } |
795 | EXPORT_SYMBOL_GPL(simple_util_init_jack); |
796 | |
797 | int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) |
798 | { |
799 | struct snd_soc_card *card = simple_priv_to_card(priv); |
800 | struct snd_soc_component *component; |
801 | int found_jack_index = 0; |
802 | int type = 0; |
803 | int num = 0; |
804 | int ret; |
805 | |
806 | if (priv->aux_jacks) |
807 | return 0; |
808 | |
809 | for_each_card_auxs(card, component) { |
810 | type = snd_soc_component_get_jack_type(component); |
811 | if (type > 0) |
812 | num++; |
813 | } |
814 | if (num < 1) |
815 | return 0; |
816 | |
817 | priv->aux_jacks = devm_kcalloc(dev: card->dev, n: num, |
818 | size: sizeof(struct snd_soc_jack), GFP_KERNEL); |
819 | if (!priv->aux_jacks) |
820 | return -ENOMEM; |
821 | |
822 | for_each_card_auxs(card, component) { |
823 | char id[128]; |
824 | struct snd_soc_jack *jack; |
825 | |
826 | if (found_jack_index >= num) |
827 | break; |
828 | |
829 | type = snd_soc_component_get_jack_type(component); |
830 | if (type <= 0) |
831 | continue; |
832 | |
833 | /* create jack */ |
834 | jack = &(priv->aux_jacks[found_jack_index++]); |
835 | snprintf(buf: id, size: sizeof(id), fmt: "%s-jack" , component->name); |
836 | ret = snd_soc_card_jack_new(card, id, type, jack); |
837 | if (ret) |
838 | continue; |
839 | |
840 | (void)snd_soc_component_set_jack(component, jack, NULL); |
841 | } |
842 | return 0; |
843 | } |
844 | EXPORT_SYMBOL_GPL(simple_util_init_aux_jacks); |
845 | |
846 | int simple_util_init_priv(struct simple_util_priv *priv, |
847 | struct link_info *li) |
848 | { |
849 | struct snd_soc_card *card = simple_priv_to_card(priv); |
850 | struct device *dev = simple_priv_to_dev(priv); |
851 | struct snd_soc_dai_link *dai_link; |
852 | struct simple_dai_props *dai_props; |
853 | struct simple_util_dai *dais; |
854 | struct snd_soc_dai_link_component *dlcs; |
855 | struct snd_soc_codec_conf *cconf = NULL; |
856 | int i, dai_num = 0, dlc_num = 0, cnf_num = 0; |
857 | |
858 | dai_props = devm_kcalloc(dev, n: li->link, size: sizeof(*dai_props), GFP_KERNEL); |
859 | dai_link = devm_kcalloc(dev, n: li->link, size: sizeof(*dai_link), GFP_KERNEL); |
860 | if (!dai_props || !dai_link) |
861 | return -ENOMEM; |
862 | |
863 | /* |
864 | * dais (= CPU+Codec) |
865 | * dlcs (= CPU+Codec+Platform) |
866 | */ |
867 | for (i = 0; i < li->link; i++) { |
868 | int cc = li->num[i].cpus + li->num[i].codecs; |
869 | |
870 | dai_num += cc; |
871 | dlc_num += cc + li->num[i].platforms; |
872 | |
873 | if (!li->num[i].cpus) |
874 | cnf_num += li->num[i].codecs; |
875 | } |
876 | |
877 | dais = devm_kcalloc(dev, n: dai_num, size: sizeof(*dais), GFP_KERNEL); |
878 | dlcs = devm_kcalloc(dev, n: dlc_num, size: sizeof(*dlcs), GFP_KERNEL); |
879 | if (!dais || !dlcs) |
880 | return -ENOMEM; |
881 | |
882 | if (cnf_num) { |
883 | cconf = devm_kcalloc(dev, n: cnf_num, size: sizeof(*cconf), GFP_KERNEL); |
884 | if (!cconf) |
885 | return -ENOMEM; |
886 | } |
887 | |
888 | dev_dbg(dev, "link %d, dais %d, ccnf %d\n" , |
889 | li->link, dai_num, cnf_num); |
890 | |
891 | priv->dai_props = dai_props; |
892 | priv->dai_link = dai_link; |
893 | priv->dais = dais; |
894 | priv->dlcs = dlcs; |
895 | priv->codec_conf = cconf; |
896 | |
897 | card->dai_link = priv->dai_link; |
898 | card->num_links = li->link; |
899 | card->codec_conf = cconf; |
900 | card->num_configs = cnf_num; |
901 | |
902 | for (i = 0; i < li->link; i++) { |
903 | if (li->num[i].cpus) { |
904 | /* Normal CPU */ |
905 | dai_link[i].cpus = dlcs; |
906 | dai_props[i].num.cpus = |
907 | dai_link[i].num_cpus = li->num[i].cpus; |
908 | dai_props[i].cpu_dai = dais; |
909 | |
910 | dlcs += li->num[i].cpus; |
911 | dais += li->num[i].cpus; |
912 | } else { |
913 | /* DPCM Be's CPU = dummy */ |
914 | dai_link[i].cpus = &snd_soc_dummy_dlc; |
915 | dai_props[i].num.cpus = |
916 | dai_link[i].num_cpus = 1; |
917 | } |
918 | |
919 | if (li->num[i].codecs) { |
920 | /* Normal Codec */ |
921 | dai_link[i].codecs = dlcs; |
922 | dai_props[i].num.codecs = |
923 | dai_link[i].num_codecs = li->num[i].codecs; |
924 | dai_props[i].codec_dai = dais; |
925 | |
926 | dlcs += li->num[i].codecs; |
927 | dais += li->num[i].codecs; |
928 | |
929 | if (!li->num[i].cpus) { |
930 | /* DPCM Be's Codec */ |
931 | dai_props[i].codec_conf = cconf; |
932 | cconf += li->num[i].codecs; |
933 | } |
934 | } else { |
935 | /* DPCM Fe's Codec = dummy */ |
936 | dai_link[i].codecs = &snd_soc_dummy_dlc; |
937 | dai_props[i].num.codecs = |
938 | dai_link[i].num_codecs = 1; |
939 | } |
940 | |
941 | if (li->num[i].platforms) { |
942 | /* Have Platform */ |
943 | dai_link[i].platforms = dlcs; |
944 | dai_props[i].num.platforms = |
945 | dai_link[i].num_platforms = li->num[i].platforms; |
946 | |
947 | dlcs += li->num[i].platforms; |
948 | } else { |
949 | /* Doesn't have Platform */ |
950 | dai_link[i].platforms = NULL; |
951 | dai_props[i].num.platforms = |
952 | dai_link[i].num_platforms = 0; |
953 | } |
954 | } |
955 | |
956 | return 0; |
957 | } |
958 | EXPORT_SYMBOL_GPL(simple_util_init_priv); |
959 | |
960 | void simple_util_remove(struct platform_device *pdev) |
961 | { |
962 | struct snd_soc_card *card = platform_get_drvdata(pdev); |
963 | |
964 | simple_util_clean_reference(card); |
965 | } |
966 | EXPORT_SYMBOL_GPL(simple_util_remove); |
967 | |
968 | int graph_util_card_probe(struct snd_soc_card *card) |
969 | { |
970 | struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); |
971 | int ret; |
972 | |
973 | ret = simple_util_init_hp(card, &priv->hp_jack, NULL); |
974 | if (ret < 0) |
975 | return ret; |
976 | |
977 | ret = simple_util_init_mic(card, &priv->mic_jack, NULL); |
978 | if (ret < 0) |
979 | return ret; |
980 | |
981 | return 0; |
982 | } |
983 | EXPORT_SYMBOL_GPL(graph_util_card_probe); |
984 | |
985 | int graph_util_is_ports0(struct device_node *np) |
986 | { |
987 | struct device_node *port, *ports, *ports0, *top; |
988 | int ret; |
989 | |
990 | /* np is "endpoint" or "port" */ |
991 | if (of_node_name_eq(np, name: "endpoint" )) { |
992 | port = of_get_parent(node: np); |
993 | } else { |
994 | port = np; |
995 | of_node_get(node: port); |
996 | } |
997 | |
998 | ports = of_get_parent(node: port); |
999 | top = of_get_parent(node: ports); |
1000 | ports0 = of_get_child_by_name(node: top, name: "ports" ); |
1001 | |
1002 | ret = ports0 == ports; |
1003 | |
1004 | of_node_put(node: port); |
1005 | of_node_put(node: ports); |
1006 | of_node_put(node: ports0); |
1007 | of_node_put(node: top); |
1008 | |
1009 | return ret; |
1010 | } |
1011 | EXPORT_SYMBOL_GPL(graph_util_is_ports0); |
1012 | |
1013 | static int graph_get_dai_id(struct device_node *ep) |
1014 | { |
1015 | struct device_node *node; |
1016 | struct device_node *endpoint; |
1017 | struct of_endpoint info; |
1018 | int i, id; |
1019 | int ret; |
1020 | |
1021 | /* use driver specified DAI ID if exist */ |
1022 | ret = snd_soc_get_dai_id(ep); |
1023 | if (ret != -ENOTSUPP) |
1024 | return ret; |
1025 | |
1026 | /* use endpoint/port reg if exist */ |
1027 | ret = of_graph_parse_endpoint(node: ep, endpoint: &info); |
1028 | if (ret == 0) { |
1029 | /* |
1030 | * Because it will count port/endpoint if it doesn't have "reg". |
1031 | * But, we can't judge whether it has "no reg", or "reg = <0>" |
1032 | * only of_graph_parse_endpoint(). |
1033 | * We need to check "reg" property |
1034 | */ |
1035 | if (of_property_present(np: ep, propname: "reg" )) |
1036 | return info.id; |
1037 | |
1038 | node = of_get_parent(node: ep); |
1039 | ret = of_property_present(np: node, propname: "reg" ); |
1040 | of_node_put(node); |
1041 | if (ret) |
1042 | return info.port; |
1043 | } |
1044 | node = of_graph_get_port_parent(node: ep); |
1045 | |
1046 | /* |
1047 | * Non HDMI sound case, counting port/endpoint on its DT |
1048 | * is enough. Let's count it. |
1049 | */ |
1050 | i = 0; |
1051 | id = -1; |
1052 | for_each_endpoint_of_node(node, endpoint) { |
1053 | if (endpoint == ep) |
1054 | id = i; |
1055 | i++; |
1056 | } |
1057 | |
1058 | of_node_put(node); |
1059 | |
1060 | if (id < 0) |
1061 | return -ENODEV; |
1062 | |
1063 | return id; |
1064 | } |
1065 | |
1066 | int graph_util_parse_dai(struct device *dev, struct device_node *ep, |
1067 | struct snd_soc_dai_link_component *dlc, int *is_single_link) |
1068 | { |
1069 | struct device_node *node; |
1070 | struct of_phandle_args args = {}; |
1071 | struct snd_soc_dai *dai; |
1072 | int ret; |
1073 | |
1074 | if (!ep) |
1075 | return 0; |
1076 | |
1077 | node = of_graph_get_port_parent(node: ep); |
1078 | |
1079 | /* |
1080 | * Try to find from DAI node |
1081 | */ |
1082 | args.np = ep; |
1083 | dai = snd_soc_get_dai_via_args(dai_args: &args); |
1084 | if (dai) { |
1085 | dlc->dai_name = snd_soc_dai_name_get(dai); |
1086 | dlc->dai_args = snd_soc_copy_dai_args(dev, args: &args); |
1087 | if (!dlc->dai_args) |
1088 | return -ENOMEM; |
1089 | |
1090 | goto parse_dai_end; |
1091 | } |
1092 | |
1093 | /* Get dai->name */ |
1094 | args.np = node; |
1095 | args.args[0] = graph_get_dai_id(ep); |
1096 | args.args_count = (of_graph_get_endpoint_count(np: node) > 1); |
1097 | |
1098 | /* |
1099 | * FIXME |
1100 | * |
1101 | * Here, dlc->dai_name is pointer to CPU/Codec DAI name. |
1102 | * If user unbinded CPU or Codec driver, but not for Sound Card, |
1103 | * dlc->dai_name is keeping unbinded CPU or Codec |
1104 | * driver's pointer. |
1105 | * |
1106 | * If user re-bind CPU or Codec driver again, ALSA SoC will try |
1107 | * to rebind Card via snd_soc_try_rebind_card(), but because of |
1108 | * above reason, it might can't bind Sound Card. |
1109 | * Because Sound Card is pointing to released dai_name pointer. |
1110 | * |
1111 | * To avoid this rebind Card issue, |
1112 | * 1) It needs to alloc memory to keep dai_name eventhough |
1113 | * CPU or Codec driver was unbinded, or |
1114 | * 2) user need to rebind Sound Card everytime |
1115 | * if he unbinded CPU or Codec. |
1116 | */ |
1117 | ret = snd_soc_get_dlc(args: &args, dlc); |
1118 | if (ret < 0) { |
1119 | of_node_put(node); |
1120 | return ret; |
1121 | } |
1122 | |
1123 | parse_dai_end: |
1124 | if (is_single_link) |
1125 | *is_single_link = of_graph_get_endpoint_count(np: node) == 1; |
1126 | |
1127 | return 0; |
1128 | } |
1129 | EXPORT_SYMBOL_GPL(graph_util_parse_dai); |
1130 | |
1131 | int graph_util_parse_link_direction(struct device_node *np, |
1132 | bool *playback_only, bool *capture_only) |
1133 | { |
1134 | bool is_playback_only = false; |
1135 | bool is_capture_only = false; |
1136 | |
1137 | is_playback_only = of_property_read_bool(np, propname: "playback-only" ); |
1138 | is_capture_only = of_property_read_bool(np, propname: "capture-only" ); |
1139 | |
1140 | if (is_playback_only && is_capture_only) |
1141 | return -EINVAL; |
1142 | |
1143 | *playback_only = is_playback_only; |
1144 | *capture_only = is_capture_only; |
1145 | |
1146 | return 0; |
1147 | } |
1148 | EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); |
1149 | |
1150 | /* Module information */ |
1151 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>" ); |
1152 | MODULE_DESCRIPTION("ALSA SoC Simple Card Utils" ); |
1153 | MODULE_LICENSE("GPL v2" ); |
1154 | |