162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// simple-card-utils.c 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/gpio.h> 962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_graph.h> 1362306a36Sopenharmony_ci#include <sound/jack.h> 1462306a36Sopenharmony_ci#include <sound/pcm_params.h> 1562306a36Sopenharmony_ci#include <sound/simple_card_utils.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic void asoc_simple_fixup_sample_fmt(struct asoc_simple_data *data, 1862306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci int i; 2162306a36Sopenharmony_ci struct snd_mask *mask = hw_param_mask(params, 2262306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT); 2362306a36Sopenharmony_ci struct { 2462306a36Sopenharmony_ci char *fmt; 2562306a36Sopenharmony_ci u32 val; 2662306a36Sopenharmony_ci } of_sample_fmt_table[] = { 2762306a36Sopenharmony_ci { "s8", SNDRV_PCM_FORMAT_S8}, 2862306a36Sopenharmony_ci { "s16_le", SNDRV_PCM_FORMAT_S16_LE}, 2962306a36Sopenharmony_ci { "s24_le", SNDRV_PCM_FORMAT_S24_LE}, 3062306a36Sopenharmony_ci { "s24_3le", SNDRV_PCM_FORMAT_S24_3LE}, 3162306a36Sopenharmony_ci { "s32_le", SNDRV_PCM_FORMAT_S32_LE}, 3262306a36Sopenharmony_ci }; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) { 3562306a36Sopenharmony_ci if (!strcmp(data->convert_sample_format, 3662306a36Sopenharmony_ci of_sample_fmt_table[i].fmt)) { 3762306a36Sopenharmony_ci snd_mask_none(mask); 3862306a36Sopenharmony_ci snd_mask_set(mask, of_sample_fmt_table[i].val); 3962306a36Sopenharmony_ci break; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_civoid asoc_simple_parse_convert(struct device_node *np, 4562306a36Sopenharmony_ci char *prefix, 4662306a36Sopenharmony_ci struct asoc_simple_data *data) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci char prop[128]; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (!prefix) 5162306a36Sopenharmony_ci prefix = ""; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* sampling rate convert */ 5462306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate"); 5562306a36Sopenharmony_ci of_property_read_u32(np, prop, &data->convert_rate); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* channels transfer */ 5862306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels"); 5962306a36Sopenharmony_ci of_property_read_u32(np, prop, &data->convert_channels); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* convert sample format */ 6262306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format"); 6362306a36Sopenharmony_ci of_property_read_string(np, prop, &data->convert_sample_format); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_convert); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * asoc_simple_is_convert_required() - Query if HW param conversion was requested 6962306a36Sopenharmony_ci * @data: Link data. 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * Returns true if any HW param conversion was requested for this DAI link with 7262306a36Sopenharmony_ci * any "convert-xxx" properties. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cibool asoc_simple_is_convert_required(const struct asoc_simple_data *data) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci return data->convert_rate || 7762306a36Sopenharmony_ci data->convert_channels || 7862306a36Sopenharmony_ci data->convert_sample_format; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_is_convert_required); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ciint asoc_simple_parse_daifmt(struct device *dev, 8362306a36Sopenharmony_ci struct device_node *node, 8462306a36Sopenharmony_ci struct device_node *codec, 8562306a36Sopenharmony_ci char *prefix, 8662306a36Sopenharmony_ci unsigned int *retfmt) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct device_node *bitclkmaster = NULL; 8962306a36Sopenharmony_ci struct device_node *framemaster = NULL; 9062306a36Sopenharmony_ci unsigned int daifmt; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci daifmt = snd_soc_daifmt_parse_format(node, prefix); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster); 9562306a36Sopenharmony_ci if (!bitclkmaster && !framemaster) { 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * No dai-link level and master setting was not found from 9862306a36Sopenharmony_ci * sound node level, revert back to legacy DT parsing and 9962306a36Sopenharmony_ci * take the settings from codec node. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci dev_dbg(dev, "Revert to legacy daifmt parsing\n"); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL); 10462306a36Sopenharmony_ci } else { 10562306a36Sopenharmony_ci daifmt |= snd_soc_daifmt_clock_provider_from_bitmap( 10662306a36Sopenharmony_ci ((codec == bitclkmaster) << 4) | (codec == framemaster)); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci of_node_put(bitclkmaster); 11062306a36Sopenharmony_ci of_node_put(framemaster); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci *retfmt = daifmt; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np, 11962306a36Sopenharmony_ci struct asoc_simple_dai *dai) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u32 *array_values, *p; 12262306a36Sopenharmony_ci int n, i, ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!of_property_read_bool(np, "dai-tdm-slot-width-map")) 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32)); 12862306a36Sopenharmony_ci if (n % 3) { 12962306a36Sopenharmony_ci dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n"); 13062306a36Sopenharmony_ci return -EINVAL; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL); 13462306a36Sopenharmony_ci if (!dai->tdm_width_map) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL); 13862306a36Sopenharmony_ci if (!array_values) 13962306a36Sopenharmony_ci return -ENOMEM; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n); 14262306a36Sopenharmony_ci if (ret < 0) { 14362306a36Sopenharmony_ci dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret); 14462306a36Sopenharmony_ci goto out; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci p = array_values; 14862306a36Sopenharmony_ci for (i = 0; i < n / 3; ++i) { 14962306a36Sopenharmony_ci dai->tdm_width_map[i].sample_bits = *p++; 15062306a36Sopenharmony_ci dai->tdm_width_map[i].slot_width = *p++; 15162306a36Sopenharmony_ci dai->tdm_width_map[i].slot_count = *p++; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dai->n_tdm_widths = i; 15562306a36Sopenharmony_ci ret = 0; 15662306a36Sopenharmony_ciout: 15762306a36Sopenharmony_ci kfree(array_values); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return ret; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciint asoc_simple_set_dailink_name(struct device *dev, 16462306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link, 16562306a36Sopenharmony_ci const char *fmt, ...) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci va_list ap; 16862306a36Sopenharmony_ci char *name = NULL; 16962306a36Sopenharmony_ci int ret = -ENOMEM; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci va_start(ap, fmt); 17262306a36Sopenharmony_ci name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap); 17362306a36Sopenharmony_ci va_end(ap); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (name) { 17662306a36Sopenharmony_ci ret = 0; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci dai_link->name = name; 17962306a36Sopenharmony_ci dai_link->stream_name = name; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciint asoc_simple_parse_card_name(struct snd_soc_card *card, 18762306a36Sopenharmony_ci char *prefix) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci int ret; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (!prefix) 19262306a36Sopenharmony_ci prefix = ""; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Parse the card name from DT */ 19562306a36Sopenharmony_ci ret = snd_soc_of_parse_card_name(card, "label"); 19662306a36Sopenharmony_ci if (ret < 0 || !card->name) { 19762306a36Sopenharmony_ci char prop[128]; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%sname", prefix); 20062306a36Sopenharmony_ci ret = snd_soc_of_parse_card_name(card, prop); 20162306a36Sopenharmony_ci if (ret < 0) 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!card->name && card->dai_link) 20662306a36Sopenharmony_ci card->name = card->dai_link->name; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_card_name); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int asoc_simple_clk_enable(struct asoc_simple_dai *dai) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci if (dai) 21562306a36Sopenharmony_ci return clk_prepare_enable(dai->clk); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void asoc_simple_clk_disable(struct asoc_simple_dai *dai) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci if (dai) 22362306a36Sopenharmony_ci clk_disable_unprepare(dai->clk); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciint asoc_simple_parse_clk(struct device *dev, 22762306a36Sopenharmony_ci struct device_node *node, 22862306a36Sopenharmony_ci struct asoc_simple_dai *simple_dai, 22962306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct clk *clk; 23262306a36Sopenharmony_ci u32 val; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * Parse dai->sysclk come from "clocks = <&xxx>" 23662306a36Sopenharmony_ci * (if system has common clock) 23762306a36Sopenharmony_ci * or "system-clock-frequency = <xxx>" 23862306a36Sopenharmony_ci * or device's module clock. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci clk = devm_get_clk_from_child(dev, node, NULL); 24162306a36Sopenharmony_ci simple_dai->clk_fixed = of_property_read_bool( 24262306a36Sopenharmony_ci node, "system-clock-fixed"); 24362306a36Sopenharmony_ci if (!IS_ERR(clk)) { 24462306a36Sopenharmony_ci simple_dai->sysclk = clk_get_rate(clk); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci simple_dai->clk = clk; 24762306a36Sopenharmony_ci } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { 24862306a36Sopenharmony_ci simple_dai->sysclk = val; 24962306a36Sopenharmony_ci simple_dai->clk_fixed = true; 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci clk = devm_get_clk_from_child(dev, dlc->of_node, NULL); 25262306a36Sopenharmony_ci if (!IS_ERR(clk)) 25362306a36Sopenharmony_ci simple_dai->sysclk = clk_get_rate(clk); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (of_property_read_bool(node, "system-clock-direction-out")) 25762306a36Sopenharmony_ci simple_dai->clk_direction = SND_SOC_CLOCK_OUT; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_clk); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int asoc_simple_check_fixed_sysclk(struct device *dev, 26462306a36Sopenharmony_ci struct asoc_simple_dai *dai, 26562306a36Sopenharmony_ci unsigned int *fixed_sysclk) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci if (dai->clk_fixed) { 26862306a36Sopenharmony_ci if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) { 26962306a36Sopenharmony_ci dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n", 27062306a36Sopenharmony_ci *fixed_sysclk, dai->sysclk); 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci *fixed_sysclk = dai->sysclk; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ciint asoc_simple_startup(struct snd_pcm_substream *substream) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 28262306a36Sopenharmony_ci struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); 28362306a36Sopenharmony_ci struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); 28462306a36Sopenharmony_ci struct asoc_simple_dai *dai; 28562306a36Sopenharmony_ci unsigned int fixed_sysclk = 0; 28662306a36Sopenharmony_ci int i1, i2, i; 28762306a36Sopenharmony_ci int ret; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci for_each_prop_dai_cpu(props, i1, dai) { 29062306a36Sopenharmony_ci ret = asoc_simple_clk_enable(dai); 29162306a36Sopenharmony_ci if (ret) 29262306a36Sopenharmony_ci goto cpu_err; 29362306a36Sopenharmony_ci ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk); 29462306a36Sopenharmony_ci if (ret) 29562306a36Sopenharmony_ci goto cpu_err; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci for_each_prop_dai_codec(props, i2, dai) { 29962306a36Sopenharmony_ci ret = asoc_simple_clk_enable(dai); 30062306a36Sopenharmony_ci if (ret) 30162306a36Sopenharmony_ci goto codec_err; 30262306a36Sopenharmony_ci ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk); 30362306a36Sopenharmony_ci if (ret) 30462306a36Sopenharmony_ci goto codec_err; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (fixed_sysclk && props->mclk_fs) { 30862306a36Sopenharmony_ci unsigned int fixed_rate = fixed_sysclk / props->mclk_fs; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (fixed_sysclk % props->mclk_fs) { 31162306a36Sopenharmony_ci dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n", 31262306a36Sopenharmony_ci fixed_sysclk, props->mclk_fs); 31362306a36Sopenharmony_ci ret = -EINVAL; 31462306a36Sopenharmony_ci goto codec_err; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, 31762306a36Sopenharmony_ci fixed_rate, fixed_rate); 31862306a36Sopenharmony_ci if (ret < 0) 31962306a36Sopenharmony_ci goto codec_err; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cicodec_err: 32562306a36Sopenharmony_ci for_each_prop_dai_codec(props, i, dai) { 32662306a36Sopenharmony_ci if (i >= i2) 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci asoc_simple_clk_disable(dai); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_cicpu_err: 33162306a36Sopenharmony_ci for_each_prop_dai_cpu(props, i, dai) { 33262306a36Sopenharmony_ci if (i >= i1) 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci asoc_simple_clk_disable(dai); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_startup); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_civoid asoc_simple_shutdown(struct snd_pcm_substream *substream) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 34362306a36Sopenharmony_ci struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); 34462306a36Sopenharmony_ci struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); 34562306a36Sopenharmony_ci struct asoc_simple_dai *dai; 34662306a36Sopenharmony_ci int i; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci for_each_prop_dai_cpu(props, i, dai) { 34962306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai)) 35262306a36Sopenharmony_ci snd_soc_dai_set_sysclk(cpu_dai, 35362306a36Sopenharmony_ci 0, 0, SND_SOC_CLOCK_OUT); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci asoc_simple_clk_disable(dai); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci for_each_prop_dai_codec(props, i, dai) { 35862306a36Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai)) 36162306a36Sopenharmony_ci snd_soc_dai_set_sysclk(codec_dai, 36262306a36Sopenharmony_ci 0, 0, SND_SOC_CLOCK_IN); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci asoc_simple_clk_disable(dai); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_shutdown); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int asoc_simple_set_clk_rate(struct device *dev, 37062306a36Sopenharmony_ci struct asoc_simple_dai *simple_dai, 37162306a36Sopenharmony_ci unsigned long rate) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci if (!simple_dai) 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (simple_dai->clk_fixed && rate != simple_dai->sysclk) { 37762306a36Sopenharmony_ci dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate); 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!simple_dai->clk) 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (clk_get_rate(simple_dai->clk) == rate) 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return clk_set_rate(simple_dai->clk, rate); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int asoc_simple_set_tdm(struct snd_soc_dai *dai, 39162306a36Sopenharmony_ci struct asoc_simple_dai *simple_dai, 39262306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci int sample_bits = params_width(params); 39562306a36Sopenharmony_ci int slot_width, slot_count; 39662306a36Sopenharmony_ci int i, ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (!simple_dai || !simple_dai->tdm_width_map) 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci slot_width = simple_dai->slot_width; 40262306a36Sopenharmony_ci slot_count = simple_dai->slots; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (slot_width == 0) 40562306a36Sopenharmony_ci slot_width = sample_bits; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (i = 0; i < simple_dai->n_tdm_widths; ++i) { 40862306a36Sopenharmony_ci if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) { 40962306a36Sopenharmony_ci slot_width = simple_dai->tdm_width_map[i].slot_width; 41062306a36Sopenharmony_ci slot_count = simple_dai->tdm_width_map[i].slot_count; 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(dai, 41662306a36Sopenharmony_ci simple_dai->tx_slot_mask, 41762306a36Sopenharmony_ci simple_dai->rx_slot_mask, 41862306a36Sopenharmony_ci slot_count, 41962306a36Sopenharmony_ci slot_width); 42062306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) { 42162306a36Sopenharmony_ci dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret); 42262306a36Sopenharmony_ci return ret; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciint asoc_simple_hw_params(struct snd_pcm_substream *substream, 42962306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 43262306a36Sopenharmony_ci struct asoc_simple_dai *pdai; 43362306a36Sopenharmony_ci struct snd_soc_dai *sdai; 43462306a36Sopenharmony_ci struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); 43562306a36Sopenharmony_ci struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); 43662306a36Sopenharmony_ci unsigned int mclk, mclk_fs = 0; 43762306a36Sopenharmony_ci int i, ret; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (props->mclk_fs) 44062306a36Sopenharmony_ci mclk_fs = props->mclk_fs; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (mclk_fs) { 44362306a36Sopenharmony_ci struct snd_soc_component *component; 44462306a36Sopenharmony_ci mclk = params_rate(params) * mclk_fs; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci for_each_prop_dai_codec(props, i, pdai) { 44762306a36Sopenharmony_ci ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk); 44862306a36Sopenharmony_ci if (ret < 0) 44962306a36Sopenharmony_ci return ret; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci for_each_prop_dai_cpu(props, i, pdai) { 45362306a36Sopenharmony_ci ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk); 45462306a36Sopenharmony_ci if (ret < 0) 45562306a36Sopenharmony_ci return ret; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Ensure sysclk is set on all components in case any 45962306a36Sopenharmony_ci * (such as platform components) are missed by calls to 46062306a36Sopenharmony_ci * snd_soc_dai_set_sysclk. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 46362306a36Sopenharmony_ci ret = snd_soc_component_set_sysclk(component, 0, 0, 46462306a36Sopenharmony_ci mclk, SND_SOC_CLOCK_IN); 46562306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, sdai) { 47062306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN); 47162306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 47262306a36Sopenharmony_ci return ret; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, sdai) { 47662306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT); 47762306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci for_each_prop_dai_codec(props, i, pdai) { 48362306a36Sopenharmony_ci sdai = asoc_rtd_to_codec(rtd, i); 48462306a36Sopenharmony_ci ret = asoc_simple_set_tdm(sdai, pdai, params); 48562306a36Sopenharmony_ci if (ret < 0) 48662306a36Sopenharmony_ci return ret; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci for_each_prop_dai_cpu(props, i, pdai) { 49062306a36Sopenharmony_ci sdai = asoc_rtd_to_cpu(rtd, i); 49162306a36Sopenharmony_ci ret = asoc_simple_set_tdm(sdai, pdai, params); 49262306a36Sopenharmony_ci if (ret < 0) 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_hw_params); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ciint asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, 50162306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); 50462306a36Sopenharmony_ci struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); 50562306a36Sopenharmony_ci struct asoc_simple_data *data = &dai_props->adata; 50662306a36Sopenharmony_ci struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 50762306a36Sopenharmony_ci struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (data->convert_rate) 51062306a36Sopenharmony_ci rate->min = 51162306a36Sopenharmony_ci rate->max = data->convert_rate; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (data->convert_channels) 51462306a36Sopenharmony_ci channels->min = 51562306a36Sopenharmony_ci channels->max = data->convert_channels; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (data->convert_sample_format) 51862306a36Sopenharmony_ci asoc_simple_fixup_sample_fmt(data, params); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int asoc_simple_init_dai(struct snd_soc_dai *dai, 52562306a36Sopenharmony_ci struct asoc_simple_dai *simple_dai) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci int ret; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!simple_dai) 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (simple_dai->sysclk) { 53362306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 53462306a36Sopenharmony_ci simple_dai->clk_direction); 53562306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) { 53662306a36Sopenharmony_ci dev_err(dai->dev, "simple-card: set_sysclk error\n"); 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (simple_dai->slots) { 54262306a36Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(dai, 54362306a36Sopenharmony_ci simple_dai->tx_slot_mask, 54462306a36Sopenharmony_ci simple_dai->rx_slot_mask, 54562306a36Sopenharmony_ci simple_dai->slots, 54662306a36Sopenharmony_ci simple_dai->slot_width); 54762306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) { 54862306a36Sopenharmony_ci dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic inline int asoc_simple_component_is_codec(struct snd_soc_component *component) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci return component->driver->endianness; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd, 56262306a36Sopenharmony_ci struct simple_dai_props *dai_props) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = rtd->dai_link; 56562306a36Sopenharmony_ci struct snd_soc_component *component; 56662306a36Sopenharmony_ci struct snd_soc_pcm_stream *c2c_params; 56762306a36Sopenharmony_ci struct snd_pcm_hardware hw; 56862306a36Sopenharmony_ci int i, ret, stream; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Do nothing if it already has Codec2Codec settings */ 57162306a36Sopenharmony_ci if (dai_link->c2c_params) 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Do nothing if it was DPCM :: BE */ 57562306a36Sopenharmony_ci if (dai_link->no_pcm) 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* Only Codecs */ 57962306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 58062306a36Sopenharmony_ci if (!asoc_simple_component_is_codec(component)) 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Assumes the capabilities are the same for all supported streams */ 58562306a36Sopenharmony_ci for_each_pcm_streams(stream) { 58662306a36Sopenharmony_ci ret = snd_soc_runtime_calc_hw(rtd, &hw, stream); 58762306a36Sopenharmony_ci if (ret == 0) 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (ret < 0) { 59262306a36Sopenharmony_ci dev_err(rtd->dev, "simple-card: no valid dai_link params\n"); 59362306a36Sopenharmony_ci return ret; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL); 59762306a36Sopenharmony_ci if (!c2c_params) 59862306a36Sopenharmony_ci return -ENOMEM; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci c2c_params->formats = hw.formats; 60162306a36Sopenharmony_ci c2c_params->rates = hw.rates; 60262306a36Sopenharmony_ci c2c_params->rate_min = hw.rate_min; 60362306a36Sopenharmony_ci c2c_params->rate_max = hw.rate_max; 60462306a36Sopenharmony_ci c2c_params->channels_min = hw.channels_min; 60562306a36Sopenharmony_ci c2c_params->channels_max = hw.channels_max; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci dai_link->c2c_params = c2c_params; 60862306a36Sopenharmony_ci dai_link->num_c2c_params = 1; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciint asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); 61662306a36Sopenharmony_ci struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); 61762306a36Sopenharmony_ci struct asoc_simple_dai *dai; 61862306a36Sopenharmony_ci int i, ret; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci for_each_prop_dai_codec(props, i, dai) { 62162306a36Sopenharmony_ci ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai); 62262306a36Sopenharmony_ci if (ret < 0) 62362306a36Sopenharmony_ci return ret; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci for_each_prop_dai_cpu(props, i, dai) { 62662306a36Sopenharmony_ci ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai); 62762306a36Sopenharmony_ci if (ret < 0) 62862306a36Sopenharmony_ci return ret; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci ret = asoc_simple_init_for_codec2codec(rtd, props); 63262306a36Sopenharmony_ci if (ret < 0) 63362306a36Sopenharmony_ci return ret; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_dai_init); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_civoid asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms, 64062306a36Sopenharmony_ci struct snd_soc_dai_link_component *cpus) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci /* 64362306a36Sopenharmony_ci * Assumes Platform == CPU 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform 64662306a36Sopenharmony_ci * are different Component, but are sharing same component->dev. 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * Let's assume Platform is same as CPU if it doesn't identify Platform on DT. 64962306a36Sopenharmony_ci * see 65062306a36Sopenharmony_ci * simple-card.c :: simple_count_noml() 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci if (!platforms->of_node) 65362306a36Sopenharmony_ci snd_soc_dlc_use_cpu_as_platform(platforms, cpus); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_civoid asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, 65862306a36Sopenharmony_ci int is_single_links) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci /* 66162306a36Sopenharmony_ci * In soc_bind_dai_link() will check cpu name after 66262306a36Sopenharmony_ci * of_node matching if dai_link has cpu_dai_name. 66362306a36Sopenharmony_ci * but, it will never match if name was created by 66462306a36Sopenharmony_ci * fmt_single_name() remove cpu_dai_name if cpu_args 66562306a36Sopenharmony_ci * was 0. See: 66662306a36Sopenharmony_ci * fmt_single_name() 66762306a36Sopenharmony_ci * fmt_multiple_name() 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci if (is_single_links) 67062306a36Sopenharmony_ci cpus->dai_name = NULL; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_civoid asoc_simple_clean_reference(struct snd_soc_card *card) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link; 67762306a36Sopenharmony_ci struct snd_soc_dai_link_component *cpu; 67862306a36Sopenharmony_ci struct snd_soc_dai_link_component *codec; 67962306a36Sopenharmony_ci int i, j; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 68262306a36Sopenharmony_ci for_each_link_cpus(dai_link, j, cpu) 68362306a36Sopenharmony_ci of_node_put(cpu->of_node); 68462306a36Sopenharmony_ci for_each_link_codecs(dai_link, j, codec) 68562306a36Sopenharmony_ci of_node_put(codec->of_node); 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_clean_reference); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ciint asoc_simple_parse_routing(struct snd_soc_card *card, 69162306a36Sopenharmony_ci char *prefix) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct device_node *node = card->dev->of_node; 69462306a36Sopenharmony_ci char prop[128]; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (!prefix) 69762306a36Sopenharmony_ci prefix = ""; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (!of_property_read_bool(node, prop)) 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return snd_soc_of_parse_audio_routing(card, prop); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_routing); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ciint asoc_simple_parse_widgets(struct snd_soc_card *card, 70962306a36Sopenharmony_ci char *prefix) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct device_node *node = card->dev->of_node; 71262306a36Sopenharmony_ci char prop[128]; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (!prefix) 71562306a36Sopenharmony_ci prefix = ""; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (of_property_read_bool(node, prop)) 72062306a36Sopenharmony_ci return snd_soc_of_parse_audio_simple_widgets(card, prop); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* no widgets is not error */ 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_widgets); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ciint asoc_simple_parse_pin_switches(struct snd_soc_card *card, 72862306a36Sopenharmony_ci char *prefix) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci char prop[128]; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (!prefix) 73362306a36Sopenharmony_ci prefix = ""; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return snd_soc_of_parse_pin_switches(card, prop); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ciint asoc_simple_init_jack(struct snd_soc_card *card, 74262306a36Sopenharmony_ci struct asoc_simple_jack *sjack, 74362306a36Sopenharmony_ci int is_hp, char *prefix, 74462306a36Sopenharmony_ci char *pin) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct device *dev = card->dev; 74762306a36Sopenharmony_ci struct gpio_desc *desc; 74862306a36Sopenharmony_ci char prop[128]; 74962306a36Sopenharmony_ci char *pin_name; 75062306a36Sopenharmony_ci char *gpio_name; 75162306a36Sopenharmony_ci int mask; 75262306a36Sopenharmony_ci int error; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (!prefix) 75562306a36Sopenharmony_ci prefix = ""; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci sjack->gpio.gpio = -ENOENT; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (is_hp) { 76062306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%shp-det", prefix); 76162306a36Sopenharmony_ci pin_name = pin ? pin : "Headphones"; 76262306a36Sopenharmony_ci gpio_name = "Headphone detection"; 76362306a36Sopenharmony_ci mask = SND_JACK_HEADPHONE; 76462306a36Sopenharmony_ci } else { 76562306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%smic-det", prefix); 76662306a36Sopenharmony_ci pin_name = pin ? pin : "Mic Jack"; 76762306a36Sopenharmony_ci gpio_name = "Mic detection"; 76862306a36Sopenharmony_ci mask = SND_JACK_MICROPHONE; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci desc = gpiod_get_optional(dev, prop, GPIOD_IN); 77262306a36Sopenharmony_ci error = PTR_ERR_OR_ZERO(desc); 77362306a36Sopenharmony_ci if (error) 77462306a36Sopenharmony_ci return error; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (desc) { 77762306a36Sopenharmony_ci error = gpiod_set_consumer_name(desc, gpio_name); 77862306a36Sopenharmony_ci if (error) 77962306a36Sopenharmony_ci return error; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci sjack->pin.pin = pin_name; 78262306a36Sopenharmony_ci sjack->pin.mask = mask; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci sjack->gpio.name = gpio_name; 78562306a36Sopenharmony_ci sjack->gpio.report = mask; 78662306a36Sopenharmony_ci sjack->gpio.desc = desc; 78762306a36Sopenharmony_ci sjack->gpio.debounce_time = 150; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack, 79062306a36Sopenharmony_ci &sjack->pin, 1); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return 0; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_init_jack); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ciint asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv, char *prefix) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct snd_soc_card *card = simple_priv_to_card(priv); 80262306a36Sopenharmony_ci struct snd_soc_component *component; 80362306a36Sopenharmony_ci int found_jack_index = 0; 80462306a36Sopenharmony_ci int type = 0; 80562306a36Sopenharmony_ci int num = 0; 80662306a36Sopenharmony_ci int ret; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (priv->aux_jacks) 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci for_each_card_auxs(card, component) { 81262306a36Sopenharmony_ci type = snd_soc_component_get_jack_type(component); 81362306a36Sopenharmony_ci if (type > 0) 81462306a36Sopenharmony_ci num++; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci if (num < 1) 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci priv->aux_jacks = devm_kcalloc(card->dev, num, 82062306a36Sopenharmony_ci sizeof(struct snd_soc_jack), GFP_KERNEL); 82162306a36Sopenharmony_ci if (!priv->aux_jacks) 82262306a36Sopenharmony_ci return -ENOMEM; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci for_each_card_auxs(card, component) { 82562306a36Sopenharmony_ci char id[128]; 82662306a36Sopenharmony_ci struct snd_soc_jack *jack; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (found_jack_index >= num) 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci type = snd_soc_component_get_jack_type(component); 83262306a36Sopenharmony_ci if (type <= 0) 83362306a36Sopenharmony_ci continue; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* create jack */ 83662306a36Sopenharmony_ci jack = &(priv->aux_jacks[found_jack_index++]); 83762306a36Sopenharmony_ci snprintf(id, sizeof(id), "%s-jack", component->name); 83862306a36Sopenharmony_ci ret = snd_soc_card_jack_new(card, id, type, jack); 83962306a36Sopenharmony_ci if (ret) 84062306a36Sopenharmony_ci continue; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci (void)snd_soc_component_set_jack(component, jack, NULL); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci return 0; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_init_aux_jacks); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ciint asoc_simple_init_priv(struct asoc_simple_priv *priv, 84962306a36Sopenharmony_ci struct link_info *li) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct snd_soc_card *card = simple_priv_to_card(priv); 85262306a36Sopenharmony_ci struct device *dev = simple_priv_to_dev(priv); 85362306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link; 85462306a36Sopenharmony_ci struct simple_dai_props *dai_props; 85562306a36Sopenharmony_ci struct asoc_simple_dai *dais; 85662306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlcs; 85762306a36Sopenharmony_ci struct snd_soc_codec_conf *cconf = NULL; 85862306a36Sopenharmony_ci int i, dai_num = 0, dlc_num = 0, cnf_num = 0; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL); 86162306a36Sopenharmony_ci dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL); 86262306a36Sopenharmony_ci if (!dai_props || !dai_link) 86362306a36Sopenharmony_ci return -ENOMEM; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* 86662306a36Sopenharmony_ci * dais (= CPU+Codec) 86762306a36Sopenharmony_ci * dlcs (= CPU+Codec+Platform) 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_ci for (i = 0; i < li->link; i++) { 87062306a36Sopenharmony_ci int cc = li->num[i].cpus + li->num[i].codecs; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci dai_num += cc; 87362306a36Sopenharmony_ci dlc_num += cc + li->num[i].platforms; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (!li->num[i].cpus) 87662306a36Sopenharmony_ci cnf_num += li->num[i].codecs; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL); 88062306a36Sopenharmony_ci dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL); 88162306a36Sopenharmony_ci if (!dais || !dlcs) 88262306a36Sopenharmony_ci return -ENOMEM; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (cnf_num) { 88562306a36Sopenharmony_ci cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL); 88662306a36Sopenharmony_ci if (!cconf) 88762306a36Sopenharmony_ci return -ENOMEM; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci dev_dbg(dev, "link %d, dais %d, ccnf %d\n", 89162306a36Sopenharmony_ci li->link, dai_num, cnf_num); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci priv->dai_props = dai_props; 89462306a36Sopenharmony_ci priv->dai_link = dai_link; 89562306a36Sopenharmony_ci priv->dais = dais; 89662306a36Sopenharmony_ci priv->dlcs = dlcs; 89762306a36Sopenharmony_ci priv->codec_conf = cconf; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci card->dai_link = priv->dai_link; 90062306a36Sopenharmony_ci card->num_links = li->link; 90162306a36Sopenharmony_ci card->codec_conf = cconf; 90262306a36Sopenharmony_ci card->num_configs = cnf_num; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci for (i = 0; i < li->link; i++) { 90562306a36Sopenharmony_ci if (li->num[i].cpus) { 90662306a36Sopenharmony_ci /* Normal CPU */ 90762306a36Sopenharmony_ci dai_link[i].cpus = dlcs; 90862306a36Sopenharmony_ci dai_props[i].num.cpus = 90962306a36Sopenharmony_ci dai_link[i].num_cpus = li->num[i].cpus; 91062306a36Sopenharmony_ci dai_props[i].cpu_dai = dais; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci dlcs += li->num[i].cpus; 91362306a36Sopenharmony_ci dais += li->num[i].cpus; 91462306a36Sopenharmony_ci } else { 91562306a36Sopenharmony_ci /* DPCM Be's CPU = dummy */ 91662306a36Sopenharmony_ci dai_link[i].cpus = &asoc_dummy_dlc; 91762306a36Sopenharmony_ci dai_props[i].num.cpus = 91862306a36Sopenharmony_ci dai_link[i].num_cpus = 1; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (li->num[i].codecs) { 92262306a36Sopenharmony_ci /* Normal Codec */ 92362306a36Sopenharmony_ci dai_link[i].codecs = dlcs; 92462306a36Sopenharmony_ci dai_props[i].num.codecs = 92562306a36Sopenharmony_ci dai_link[i].num_codecs = li->num[i].codecs; 92662306a36Sopenharmony_ci dai_props[i].codec_dai = dais; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci dlcs += li->num[i].codecs; 92962306a36Sopenharmony_ci dais += li->num[i].codecs; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (!li->num[i].cpus) { 93262306a36Sopenharmony_ci /* DPCM Be's Codec */ 93362306a36Sopenharmony_ci dai_props[i].codec_conf = cconf; 93462306a36Sopenharmony_ci cconf += li->num[i].codecs; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci } else { 93762306a36Sopenharmony_ci /* DPCM Fe's Codec = dummy */ 93862306a36Sopenharmony_ci dai_link[i].codecs = &asoc_dummy_dlc; 93962306a36Sopenharmony_ci dai_props[i].num.codecs = 94062306a36Sopenharmony_ci dai_link[i].num_codecs = 1; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (li->num[i].platforms) { 94462306a36Sopenharmony_ci /* Have Platform */ 94562306a36Sopenharmony_ci dai_link[i].platforms = dlcs; 94662306a36Sopenharmony_ci dai_props[i].num.platforms = 94762306a36Sopenharmony_ci dai_link[i].num_platforms = li->num[i].platforms; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci dlcs += li->num[i].platforms; 95062306a36Sopenharmony_ci } else { 95162306a36Sopenharmony_ci /* Doesn't have Platform */ 95262306a36Sopenharmony_ci dai_link[i].platforms = NULL; 95362306a36Sopenharmony_ci dai_props[i].num.platforms = 95462306a36Sopenharmony_ci dai_link[i].num_platforms = 0; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return 0; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_init_priv); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ciint asoc_simple_remove(struct platform_device *pdev) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct snd_soc_card *card = platform_get_drvdata(pdev); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci asoc_simple_clean_reference(card); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_simple_remove); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ciint asoc_graph_card_probe(struct snd_soc_card *card) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); 97562306a36Sopenharmony_ci int ret; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL); 97862306a36Sopenharmony_ci if (ret < 0) 97962306a36Sopenharmony_ci return ret; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL); 98262306a36Sopenharmony_ci if (ret < 0) 98362306a36Sopenharmony_ci return ret; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return 0; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_graph_card_probe); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ciint asoc_graph_is_ports0(struct device_node *np) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci struct device_node *port, *ports, *ports0, *top; 99262306a36Sopenharmony_ci int ret; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* np is "endpoint" or "port" */ 99562306a36Sopenharmony_ci if (of_node_name_eq(np, "endpoint")) { 99662306a36Sopenharmony_ci port = of_get_parent(np); 99762306a36Sopenharmony_ci } else { 99862306a36Sopenharmony_ci port = np; 99962306a36Sopenharmony_ci of_node_get(port); 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci ports = of_get_parent(port); 100362306a36Sopenharmony_ci top = of_get_parent(ports); 100462306a36Sopenharmony_ci ports0 = of_get_child_by_name(top, "ports"); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci ret = ports0 == ports; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci of_node_put(port); 100962306a36Sopenharmony_ci of_node_put(ports); 101062306a36Sopenharmony_ci of_node_put(ports0); 101162306a36Sopenharmony_ci of_node_put(top); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci return ret; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_graph_is_ports0); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic int graph_get_dai_id(struct device_node *ep) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci struct device_node *node; 102062306a36Sopenharmony_ci struct device_node *endpoint; 102162306a36Sopenharmony_ci struct of_endpoint info; 102262306a36Sopenharmony_ci int i, id; 102362306a36Sopenharmony_ci int ret; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* use driver specified DAI ID if exist */ 102662306a36Sopenharmony_ci ret = snd_soc_get_dai_id(ep); 102762306a36Sopenharmony_ci if (ret != -ENOTSUPP) 102862306a36Sopenharmony_ci return ret; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* use endpoint/port reg if exist */ 103162306a36Sopenharmony_ci ret = of_graph_parse_endpoint(ep, &info); 103262306a36Sopenharmony_ci if (ret == 0) { 103362306a36Sopenharmony_ci /* 103462306a36Sopenharmony_ci * Because it will count port/endpoint if it doesn't have "reg". 103562306a36Sopenharmony_ci * But, we can't judge whether it has "no reg", or "reg = <0>" 103662306a36Sopenharmony_ci * only of_graph_parse_endpoint(). 103762306a36Sopenharmony_ci * We need to check "reg" property 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci if (of_property_present(ep, "reg")) 104062306a36Sopenharmony_ci return info.id; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci node = of_get_parent(ep); 104362306a36Sopenharmony_ci ret = of_property_present(node, "reg"); 104462306a36Sopenharmony_ci of_node_put(node); 104562306a36Sopenharmony_ci if (ret) 104662306a36Sopenharmony_ci return info.port; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci node = of_graph_get_port_parent(ep); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci /* 105162306a36Sopenharmony_ci * Non HDMI sound case, counting port/endpoint on its DT 105262306a36Sopenharmony_ci * is enough. Let's count it. 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ci i = 0; 105562306a36Sopenharmony_ci id = -1; 105662306a36Sopenharmony_ci for_each_endpoint_of_node(node, endpoint) { 105762306a36Sopenharmony_ci if (endpoint == ep) 105862306a36Sopenharmony_ci id = i; 105962306a36Sopenharmony_ci i++; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci of_node_put(node); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (id < 0) 106562306a36Sopenharmony_ci return -ENODEV; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci return id; 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ciint asoc_graph_parse_dai(struct device *dev, struct device_node *ep, 107162306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc, int *is_single_link) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct device_node *node; 107462306a36Sopenharmony_ci struct of_phandle_args args = {}; 107562306a36Sopenharmony_ci struct snd_soc_dai *dai; 107662306a36Sopenharmony_ci int ret; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (!ep) 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci node = of_graph_get_port_parent(ep); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* 108462306a36Sopenharmony_ci * Try to find from DAI node 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci args.np = ep; 108762306a36Sopenharmony_ci dai = snd_soc_get_dai_via_args(&args); 108862306a36Sopenharmony_ci if (dai) { 108962306a36Sopenharmony_ci dlc->dai_name = snd_soc_dai_name_get(dai); 109062306a36Sopenharmony_ci dlc->dai_args = snd_soc_copy_dai_args(dev, &args); 109162306a36Sopenharmony_ci if (!dlc->dai_args) 109262306a36Sopenharmony_ci return -ENOMEM; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci goto parse_dai_end; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* Get dai->name */ 109862306a36Sopenharmony_ci args.np = node; 109962306a36Sopenharmony_ci args.args[0] = graph_get_dai_id(ep); 110062306a36Sopenharmony_ci args.args_count = (of_graph_get_endpoint_count(node) > 1); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci /* 110362306a36Sopenharmony_ci * FIXME 110462306a36Sopenharmony_ci * 110562306a36Sopenharmony_ci * Here, dlc->dai_name is pointer to CPU/Codec DAI name. 110662306a36Sopenharmony_ci * If user unbinded CPU or Codec driver, but not for Sound Card, 110762306a36Sopenharmony_ci * dlc->dai_name is keeping unbinded CPU or Codec 110862306a36Sopenharmony_ci * driver's pointer. 110962306a36Sopenharmony_ci * 111062306a36Sopenharmony_ci * If user re-bind CPU or Codec driver again, ALSA SoC will try 111162306a36Sopenharmony_ci * to rebind Card via snd_soc_try_rebind_card(), but because of 111262306a36Sopenharmony_ci * above reason, it might can't bind Sound Card. 111362306a36Sopenharmony_ci * Because Sound Card is pointing to released dai_name pointer. 111462306a36Sopenharmony_ci * 111562306a36Sopenharmony_ci * To avoid this rebind Card issue, 111662306a36Sopenharmony_ci * 1) It needs to alloc memory to keep dai_name eventhough 111762306a36Sopenharmony_ci * CPU or Codec driver was unbinded, or 111862306a36Sopenharmony_ci * 2) user need to rebind Sound Card everytime 111962306a36Sopenharmony_ci * if he unbinded CPU or Codec. 112062306a36Sopenharmony_ci */ 112162306a36Sopenharmony_ci ret = snd_soc_get_dlc(&args, dlc); 112262306a36Sopenharmony_ci if (ret < 0) { 112362306a36Sopenharmony_ci of_node_put(node); 112462306a36Sopenharmony_ci return ret; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ciparse_dai_end: 112862306a36Sopenharmony_ci if (is_single_link) 112962306a36Sopenharmony_ci *is_single_link = of_graph_get_endpoint_count(node) == 1; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci return 0; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_graph_parse_dai); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci/* Module information */ 113662306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 113762306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); 113862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1139