1d613a7f4SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
2d613a7f4SKuninori Morimoto //
3d613a7f4SKuninori Morimoto // simple-card-utils.c
4d613a7f4SKuninori Morimoto //
5d613a7f4SKuninori Morimoto // Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6d613a7f4SKuninori Morimoto
7bb6fc620SKuninori Morimoto #include <linux/clk.h>
862c2c9fcSKatsuhiro Suzuki #include <linux/gpio.h>
962c2c9fcSKatsuhiro Suzuki #include <linux/gpio/consumer.h>
101f85e118SKuninori Morimoto #include <linux/module.h>
11abd3147eSKuninori Morimoto #include <linux/of.h>
121689333fSKuninori Morimoto #include <linux/of_graph.h>
1362c2c9fcSKatsuhiro Suzuki #include <sound/jack.h>
141e974e5bSRichard Fitzgerald #include <sound/pcm_params.h>
15abd3147eSKuninori Morimoto #include <sound/simple_card_utils.h>
16abd3147eSKuninori Morimoto
asoc_simple_fixup_sample_fmt(struct asoc_simple_data * data,struct snd_pcm_hw_params * params)17047a0536SSameer Pujar static void asoc_simple_fixup_sample_fmt(struct asoc_simple_data *data,
18047a0536SSameer Pujar struct snd_pcm_hw_params *params)
19047a0536SSameer Pujar {
20047a0536SSameer Pujar int i;
21047a0536SSameer Pujar struct snd_mask *mask = hw_param_mask(params,
22047a0536SSameer Pujar SNDRV_PCM_HW_PARAM_FORMAT);
23047a0536SSameer Pujar struct {
24047a0536SSameer Pujar char *fmt;
25047a0536SSameer Pujar u32 val;
26047a0536SSameer Pujar } of_sample_fmt_table[] = {
27047a0536SSameer Pujar { "s8", SNDRV_PCM_FORMAT_S8},
28047a0536SSameer Pujar { "s16_le", SNDRV_PCM_FORMAT_S16_LE},
29047a0536SSameer Pujar { "s24_le", SNDRV_PCM_FORMAT_S24_LE},
30047a0536SSameer Pujar { "s24_3le", SNDRV_PCM_FORMAT_S24_3LE},
31047a0536SSameer Pujar { "s32_le", SNDRV_PCM_FORMAT_S32_LE},
32047a0536SSameer Pujar };
33047a0536SSameer Pujar
34047a0536SSameer Pujar for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) {
35047a0536SSameer Pujar if (!strcmp(data->convert_sample_format,
36047a0536SSameer Pujar of_sample_fmt_table[i].fmt)) {
37047a0536SSameer Pujar snd_mask_none(mask);
38047a0536SSameer Pujar snd_mask_set(mask, of_sample_fmt_table[i].val);
39047a0536SSameer Pujar break;
40047a0536SSameer Pujar }
41047a0536SSameer Pujar }
42047a0536SSameer Pujar }
43047a0536SSameer Pujar
asoc_simple_parse_convert(struct device_node * np,char * prefix,struct asoc_simple_data * data)44fcfd763bSKuninori Morimoto void asoc_simple_parse_convert(struct device_node *np,
45a48bf02bSKuninori Morimoto char *prefix,
46ad11e59fSKuninori Morimoto struct asoc_simple_data *data)
4713bb1cc0SKuninori Morimoto {
4813bb1cc0SKuninori Morimoto char prop[128];
4913bb1cc0SKuninori Morimoto
5013bb1cc0SKuninori Morimoto if (!prefix)
5113bb1cc0SKuninori Morimoto prefix = "";
5213bb1cc0SKuninori Morimoto
5313bb1cc0SKuninori Morimoto /* sampling rate convert */
5413bb1cc0SKuninori Morimoto snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
5513bb1cc0SKuninori Morimoto of_property_read_u32(np, prop, &data->convert_rate);
5613bb1cc0SKuninori Morimoto
5713bb1cc0SKuninori Morimoto /* channels transfer */
5813bb1cc0SKuninori Morimoto snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
5913bb1cc0SKuninori Morimoto of_property_read_u32(np, prop, &data->convert_channels);
60047a0536SSameer Pujar
61047a0536SSameer Pujar /* convert sample format */
62047a0536SSameer Pujar snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format");
63047a0536SSameer Pujar of_property_read_string(np, prop, &data->convert_sample_format);
6413bb1cc0SKuninori Morimoto }
65ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
6613bb1cc0SKuninori Morimoto
6732def55dSAidan MacDonald /**
6832def55dSAidan MacDonald * asoc_simple_is_convert_required() - Query if HW param conversion was requested
6932def55dSAidan MacDonald * @data: Link data.
7032def55dSAidan MacDonald *
7132def55dSAidan MacDonald * Returns true if any HW param conversion was requested for this DAI link with
7232def55dSAidan MacDonald * any "convert-xxx" properties.
7332def55dSAidan MacDonald */
asoc_simple_is_convert_required(const struct asoc_simple_data * data)7432def55dSAidan MacDonald bool asoc_simple_is_convert_required(const struct asoc_simple_data *data)
7532def55dSAidan MacDonald {
7632def55dSAidan MacDonald return data->convert_rate ||
7732def55dSAidan MacDonald data->convert_channels ||
7832def55dSAidan MacDonald data->convert_sample_format;
7932def55dSAidan MacDonald }
8032def55dSAidan MacDonald EXPORT_SYMBOL_GPL(asoc_simple_is_convert_required);
8132def55dSAidan MacDonald
asoc_simple_parse_daifmt(struct device * dev,struct device_node * node,struct device_node * codec,char * prefix,unsigned int * retfmt)82ad11e59fSKuninori Morimoto int asoc_simple_parse_daifmt(struct device *dev,
83abd3147eSKuninori Morimoto struct device_node *node,
84abd3147eSKuninori Morimoto struct device_node *codec,
85abd3147eSKuninori Morimoto char *prefix,
86abd3147eSKuninori Morimoto unsigned int *retfmt)
87abd3147eSKuninori Morimoto {
88abd3147eSKuninori Morimoto struct device_node *bitclkmaster = NULL;
89abd3147eSKuninori Morimoto struct device_node *framemaster = NULL;
90abd3147eSKuninori Morimoto unsigned int daifmt;
91abd3147eSKuninori Morimoto
922c7fd9deSKuninori Morimoto daifmt = snd_soc_daifmt_parse_format(node, prefix);
93abd3147eSKuninori Morimoto
942c7fd9deSKuninori Morimoto snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);
95155b8f3aSKuninori Morimoto if (!bitclkmaster && !framemaster) {
96abd3147eSKuninori Morimoto /*
97abd3147eSKuninori Morimoto * No dai-link level and master setting was not found from
98abd3147eSKuninori Morimoto * sound node level, revert back to legacy DT parsing and
99abd3147eSKuninori Morimoto * take the settings from codec node.
100abd3147eSKuninori Morimoto */
101abd3147eSKuninori Morimoto dev_dbg(dev, "Revert to legacy daifmt parsing\n");
102abd3147eSKuninori Morimoto
1032c7fd9deSKuninori Morimoto daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);
104abd3147eSKuninori Morimoto } else {
1052c7fd9deSKuninori Morimoto daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(
1062c7fd9deSKuninori Morimoto ((codec == bitclkmaster) << 4) | (codec == framemaster));
107abd3147eSKuninori Morimoto }
108abd3147eSKuninori Morimoto
109abd3147eSKuninori Morimoto of_node_put(bitclkmaster);
110abd3147eSKuninori Morimoto of_node_put(framemaster);
111abd3147eSKuninori Morimoto
112abd3147eSKuninori Morimoto *retfmt = daifmt;
113abd3147eSKuninori Morimoto
114abd3147eSKuninori Morimoto return 0;
115abd3147eSKuninori Morimoto }
116ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
1171db3312eSKuninori Morimoto
asoc_simple_parse_tdm_width_map(struct device * dev,struct device_node * np,struct asoc_simple_dai * dai)1181e974e5bSRichard Fitzgerald int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
1191e974e5bSRichard Fitzgerald struct asoc_simple_dai *dai)
1201e974e5bSRichard Fitzgerald {
1211e974e5bSRichard Fitzgerald u32 *array_values, *p;
1221e974e5bSRichard Fitzgerald int n, i, ret;
1231e974e5bSRichard Fitzgerald
1241e974e5bSRichard Fitzgerald if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))
1251e974e5bSRichard Fitzgerald return 0;
1261e974e5bSRichard Fitzgerald
1271e974e5bSRichard Fitzgerald n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
1281e974e5bSRichard Fitzgerald if (n % 3) {
1291e974e5bSRichard Fitzgerald dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
1301e974e5bSRichard Fitzgerald return -EINVAL;
1311e974e5bSRichard Fitzgerald }
1321e974e5bSRichard Fitzgerald
1331e974e5bSRichard Fitzgerald dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
1341e974e5bSRichard Fitzgerald if (!dai->tdm_width_map)
1351e974e5bSRichard Fitzgerald return -ENOMEM;
1361e974e5bSRichard Fitzgerald
1371e974e5bSRichard Fitzgerald array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
1381e974e5bSRichard Fitzgerald if (!array_values)
1391e974e5bSRichard Fitzgerald return -ENOMEM;
1401e974e5bSRichard Fitzgerald
1411e974e5bSRichard Fitzgerald ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
1421e974e5bSRichard Fitzgerald if (ret < 0) {
1431e974e5bSRichard Fitzgerald dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
1441e974e5bSRichard Fitzgerald goto out;
1451e974e5bSRichard Fitzgerald }
1461e974e5bSRichard Fitzgerald
1471e974e5bSRichard Fitzgerald p = array_values;
1481e974e5bSRichard Fitzgerald for (i = 0; i < n / 3; ++i) {
1491e974e5bSRichard Fitzgerald dai->tdm_width_map[i].sample_bits = *p++;
1501e974e5bSRichard Fitzgerald dai->tdm_width_map[i].slot_width = *p++;
1511e974e5bSRichard Fitzgerald dai->tdm_width_map[i].slot_count = *p++;
1521e974e5bSRichard Fitzgerald }
1531e974e5bSRichard Fitzgerald
1541e974e5bSRichard Fitzgerald dai->n_tdm_widths = i;
1551e974e5bSRichard Fitzgerald ret = 0;
1561e974e5bSRichard Fitzgerald out:
1571e974e5bSRichard Fitzgerald kfree(array_values);
1581e974e5bSRichard Fitzgerald
1591e974e5bSRichard Fitzgerald return ret;
1601e974e5bSRichard Fitzgerald }
1611e974e5bSRichard Fitzgerald EXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map);
1621e974e5bSRichard Fitzgerald
asoc_simple_set_dailink_name(struct device * dev,struct snd_soc_dai_link * dai_link,const char * fmt,...)163ad11e59fSKuninori Morimoto int asoc_simple_set_dailink_name(struct device *dev,
1641db3312eSKuninori Morimoto struct snd_soc_dai_link *dai_link,
1651db3312eSKuninori Morimoto const char *fmt, ...)
1661db3312eSKuninori Morimoto {
1671db3312eSKuninori Morimoto va_list ap;
1681db3312eSKuninori Morimoto char *name = NULL;
1691db3312eSKuninori Morimoto int ret = -ENOMEM;
1701db3312eSKuninori Morimoto
1711db3312eSKuninori Morimoto va_start(ap, fmt);
1721db3312eSKuninori Morimoto name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
1731db3312eSKuninori Morimoto va_end(ap);
1741db3312eSKuninori Morimoto
1751db3312eSKuninori Morimoto if (name) {
1761db3312eSKuninori Morimoto ret = 0;
1771db3312eSKuninori Morimoto
1781db3312eSKuninori Morimoto dai_link->name = name;
1791db3312eSKuninori Morimoto dai_link->stream_name = name;
1801db3312eSKuninori Morimoto }
1811db3312eSKuninori Morimoto
1821db3312eSKuninori Morimoto return ret;
1831db3312eSKuninori Morimoto }
184ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
185fc55c9b5SKuninori Morimoto
asoc_simple_parse_card_name(struct snd_soc_card * card,char * prefix)186ad11e59fSKuninori Morimoto int asoc_simple_parse_card_name(struct snd_soc_card *card,
187fc55c9b5SKuninori Morimoto char *prefix)
188fc55c9b5SKuninori Morimoto {
189fc55c9b5SKuninori Morimoto int ret;
190fc55c9b5SKuninori Morimoto
191dedfaa1eSKuninori Morimoto if (!prefix)
192dedfaa1eSKuninori Morimoto prefix = "";
193fc55c9b5SKuninori Morimoto
194fc55c9b5SKuninori Morimoto /* Parse the card name from DT */
195dedfaa1eSKuninori Morimoto ret = snd_soc_of_parse_card_name(card, "label");
1961b4a56cdSLucas Stach if (ret < 0 || !card->name) {
197dedfaa1eSKuninori Morimoto char prop[128];
198dedfaa1eSKuninori Morimoto
199dedfaa1eSKuninori Morimoto snprintf(prop, sizeof(prop), "%sname", prefix);
200fc55c9b5SKuninori Morimoto ret = snd_soc_of_parse_card_name(card, prop);
201fc55c9b5SKuninori Morimoto if (ret < 0)
202fc55c9b5SKuninori Morimoto return ret;
203dedfaa1eSKuninori Morimoto }
204fc55c9b5SKuninori Morimoto
205fc55c9b5SKuninori Morimoto if (!card->name && card->dai_link)
206fc55c9b5SKuninori Morimoto card->name = card->dai_link->name;
207fc55c9b5SKuninori Morimoto
208fc55c9b5SKuninori Morimoto return 0;
209fc55c9b5SKuninori Morimoto }
210ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
2111f85e118SKuninori Morimoto
asoc_simple_clk_enable(struct asoc_simple_dai * dai)212ad11e59fSKuninori Morimoto static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
213891caea4SKuninori Morimoto {
214f31a1710SKuninori Morimoto if (dai)
215891caea4SKuninori Morimoto return clk_prepare_enable(dai->clk);
216f31a1710SKuninori Morimoto
217f31a1710SKuninori Morimoto return 0;
218891caea4SKuninori Morimoto }
219891caea4SKuninori Morimoto
asoc_simple_clk_disable(struct asoc_simple_dai * dai)220ad11e59fSKuninori Morimoto static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
221891caea4SKuninori Morimoto {
222f31a1710SKuninori Morimoto if (dai)
223891caea4SKuninori Morimoto clk_disable_unprepare(dai->clk);
224891caea4SKuninori Morimoto }
225891caea4SKuninori Morimoto
asoc_simple_parse_clk(struct device * dev,struct device_node * node,struct asoc_simple_dai * simple_dai,struct snd_soc_dai_link_component * dlc)226ad11e59fSKuninori Morimoto int asoc_simple_parse_clk(struct device *dev,
227e984fd61SKuninori Morimoto struct device_node *node,
2288e166382SKuninori Morimoto struct asoc_simple_dai *simple_dai,
229e664de68SKuninori Morimoto struct snd_soc_dai_link_component *dlc)
230bb6fc620SKuninori Morimoto {
231bb6fc620SKuninori Morimoto struct clk *clk;
232bb6fc620SKuninori Morimoto u32 val;
233bb6fc620SKuninori Morimoto
234bb6fc620SKuninori Morimoto /*
235bb6fc620SKuninori Morimoto * Parse dai->sysclk come from "clocks = <&xxx>"
236bb6fc620SKuninori Morimoto * (if system has common clock)
237bb6fc620SKuninori Morimoto * or "system-clock-frequency = <xxx>"
238bb6fc620SKuninori Morimoto * or device's module clock.
239bb6fc620SKuninori Morimoto */
240e984fd61SKuninori Morimoto clk = devm_get_clk_from_child(dev, node, NULL);
2415ca2ab45SRobert Hancock simple_dai->clk_fixed = of_property_read_bool(
2425ca2ab45SRobert Hancock node, "system-clock-fixed");
2431e30f642SSameer Pujar if (!IS_ERR(clk)) {
244bb6fc620SKuninori Morimoto simple_dai->sysclk = clk_get_rate(clk);
2458ca88d53SSameer Pujar
2468ca88d53SSameer Pujar simple_dai->clk = clk;
2478ca88d53SSameer Pujar } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
2481e30f642SSameer Pujar simple_dai->sysclk = val;
2495ca2ab45SRobert Hancock simple_dai->clk_fixed = true;
2508ca88d53SSameer Pujar } else {
2518ca88d53SSameer Pujar clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
2528ca88d53SSameer Pujar if (!IS_ERR(clk))
2538ca88d53SSameer Pujar simple_dai->sysclk = clk_get_rate(clk);
254bb6fc620SKuninori Morimoto }
255bb6fc620SKuninori Morimoto
256a728f560SVitaly Wool if (of_property_read_bool(node, "system-clock-direction-out"))
257a728f560SVitaly Wool simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
258a728f560SVitaly Wool
259bb6fc620SKuninori Morimoto return 0;
260bb6fc620SKuninori Morimoto }
261ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
262bb6fc620SKuninori Morimoto
asoc_simple_check_fixed_sysclk(struct device * dev,struct asoc_simple_dai * dai,unsigned int * fixed_sysclk)2635ca2ab45SRobert Hancock static int asoc_simple_check_fixed_sysclk(struct device *dev,
2645ca2ab45SRobert Hancock struct asoc_simple_dai *dai,
2655ca2ab45SRobert Hancock unsigned int *fixed_sysclk)
2665ca2ab45SRobert Hancock {
2675ca2ab45SRobert Hancock if (dai->clk_fixed) {
2685ca2ab45SRobert Hancock if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {
2695ca2ab45SRobert Hancock dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",
2705ca2ab45SRobert Hancock *fixed_sysclk, dai->sysclk);
2715ca2ab45SRobert Hancock return -EINVAL;
2725ca2ab45SRobert Hancock }
2735ca2ab45SRobert Hancock *fixed_sysclk = dai->sysclk;
2745ca2ab45SRobert Hancock }
2755ca2ab45SRobert Hancock
2765ca2ab45SRobert Hancock return 0;
2775ca2ab45SRobert Hancock }
2785ca2ab45SRobert Hancock
asoc_simple_startup(struct snd_pcm_substream * substream)279f38df5bfSKuninori Morimoto int asoc_simple_startup(struct snd_pcm_substream *substream)
280f38df5bfSKuninori Morimoto {
2819ae035e2SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
282f38df5bfSKuninori Morimoto struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
283fafc05aaSKuninori Morimoto struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
284fafc05aaSKuninori Morimoto struct asoc_simple_dai *dai;
2855ca2ab45SRobert Hancock unsigned int fixed_sysclk = 0;
286fafc05aaSKuninori Morimoto int i1, i2, i;
287f38df5bfSKuninori Morimoto int ret;
288f38df5bfSKuninori Morimoto
289fafc05aaSKuninori Morimoto for_each_prop_dai_cpu(props, i1, dai) {
290fafc05aaSKuninori Morimoto ret = asoc_simple_clk_enable(dai);
291f38df5bfSKuninori Morimoto if (ret)
292fafc05aaSKuninori Morimoto goto cpu_err;
2935ca2ab45SRobert Hancock ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
2945ca2ab45SRobert Hancock if (ret)
2955ca2ab45SRobert Hancock goto cpu_err;
296fafc05aaSKuninori Morimoto }
297f38df5bfSKuninori Morimoto
298fafc05aaSKuninori Morimoto for_each_prop_dai_codec(props, i2, dai) {
299fafc05aaSKuninori Morimoto ret = asoc_simple_clk_enable(dai);
300f38df5bfSKuninori Morimoto if (ret)
301fafc05aaSKuninori Morimoto goto codec_err;
3025ca2ab45SRobert Hancock ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
3035ca2ab45SRobert Hancock if (ret)
3045ca2ab45SRobert Hancock goto codec_err;
3055ca2ab45SRobert Hancock }
3065ca2ab45SRobert Hancock
3075ca2ab45SRobert Hancock if (fixed_sysclk && props->mclk_fs) {
3085ca2ab45SRobert Hancock unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;
3095ca2ab45SRobert Hancock
3105ca2ab45SRobert Hancock if (fixed_sysclk % props->mclk_fs) {
3115ca2ab45SRobert Hancock dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",
3125ca2ab45SRobert Hancock fixed_sysclk, props->mclk_fs);
313*69cf63b6SKuninori Morimoto ret = -EINVAL;
314*69cf63b6SKuninori Morimoto goto codec_err;
3155ca2ab45SRobert Hancock }
3165ca2ab45SRobert Hancock ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,
3175ca2ab45SRobert Hancock fixed_rate, fixed_rate);
318635071f5SRobert Hancock if (ret < 0)
3195ca2ab45SRobert Hancock goto codec_err;
320fafc05aaSKuninori Morimoto }
321f38df5bfSKuninori Morimoto
322fafc05aaSKuninori Morimoto return 0;
323fafc05aaSKuninori Morimoto
324fafc05aaSKuninori Morimoto codec_err:
325fafc05aaSKuninori Morimoto for_each_prop_dai_codec(props, i, dai) {
326fafc05aaSKuninori Morimoto if (i >= i2)
327fafc05aaSKuninori Morimoto break;
328fafc05aaSKuninori Morimoto asoc_simple_clk_disable(dai);
329fafc05aaSKuninori Morimoto }
330fafc05aaSKuninori Morimoto cpu_err:
331fafc05aaSKuninori Morimoto for_each_prop_dai_cpu(props, i, dai) {
332fafc05aaSKuninori Morimoto if (i >= i1)
333fafc05aaSKuninori Morimoto break;
334fafc05aaSKuninori Morimoto asoc_simple_clk_disable(dai);
335fafc05aaSKuninori Morimoto }
336f38df5bfSKuninori Morimoto return ret;
337f38df5bfSKuninori Morimoto }
338f38df5bfSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_startup);
339f38df5bfSKuninori Morimoto
asoc_simple_shutdown(struct snd_pcm_substream * substream)340686911b4SKuninori Morimoto void asoc_simple_shutdown(struct snd_pcm_substream *substream)
341686911b4SKuninori Morimoto {
3429ae035e2SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
343686911b4SKuninori Morimoto struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
344fafc05aaSKuninori Morimoto struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
345fafc05aaSKuninori Morimoto struct asoc_simple_dai *dai;
346fafc05aaSKuninori Morimoto int i;
347686911b4SKuninori Morimoto
3485ca2ab45SRobert Hancock for_each_prop_dai_cpu(props, i, dai) {
3495bbe2918SSameer Pujar struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i);
3505bbe2918SSameer Pujar
3515bbe2918SSameer Pujar if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
3525bbe2918SSameer Pujar snd_soc_dai_set_sysclk(cpu_dai,
3533756aa16SOlivier Moysan 0, 0, SND_SOC_CLOCK_OUT);
3542458adb8SKatsuhiro Suzuki
355fafc05aaSKuninori Morimoto asoc_simple_clk_disable(dai);
3565ca2ab45SRobert Hancock }
3575ca2ab45SRobert Hancock for_each_prop_dai_codec(props, i, dai) {
3585bbe2918SSameer Pujar struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i);
3595bbe2918SSameer Pujar
3605bbe2918SSameer Pujar if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
3615bbe2918SSameer Pujar snd_soc_dai_set_sysclk(codec_dai,
3625ca2ab45SRobert Hancock 0, 0, SND_SOC_CLOCK_IN);
3635ca2ab45SRobert Hancock
364fafc05aaSKuninori Morimoto asoc_simple_clk_disable(dai);
365686911b4SKuninori Morimoto }
3665ca2ab45SRobert Hancock }
367686911b4SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
368686911b4SKuninori Morimoto
asoc_simple_set_clk_rate(struct device * dev,struct asoc_simple_dai * simple_dai,unsigned long rate)3695ca2ab45SRobert Hancock static int asoc_simple_set_clk_rate(struct device *dev,
3705ca2ab45SRobert Hancock struct asoc_simple_dai *simple_dai,
371f48dcbb6SKuninori Morimoto unsigned long rate)
372f48dcbb6SKuninori Morimoto {
373f48dcbb6SKuninori Morimoto if (!simple_dai)
374f48dcbb6SKuninori Morimoto return 0;
375f48dcbb6SKuninori Morimoto
3765ca2ab45SRobert Hancock if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
3775ca2ab45SRobert Hancock dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
3785ca2ab45SRobert Hancock return -EINVAL;
3795ca2ab45SRobert Hancock }
3805ca2ab45SRobert Hancock
381f48dcbb6SKuninori Morimoto if (!simple_dai->clk)
382f48dcbb6SKuninori Morimoto return 0;
383f48dcbb6SKuninori Morimoto
384f48dcbb6SKuninori Morimoto if (clk_get_rate(simple_dai->clk) == rate)
385f48dcbb6SKuninori Morimoto return 0;
386f48dcbb6SKuninori Morimoto
387f48dcbb6SKuninori Morimoto return clk_set_rate(simple_dai->clk, rate);
388f48dcbb6SKuninori Morimoto }
389f48dcbb6SKuninori Morimoto
asoc_simple_set_tdm(struct snd_soc_dai * dai,struct asoc_simple_dai * simple_dai,struct snd_pcm_hw_params * params)3901e974e5bSRichard Fitzgerald static int asoc_simple_set_tdm(struct snd_soc_dai *dai,
3911e974e5bSRichard Fitzgerald struct asoc_simple_dai *simple_dai,
3921e974e5bSRichard Fitzgerald struct snd_pcm_hw_params *params)
3931e974e5bSRichard Fitzgerald {
3941e974e5bSRichard Fitzgerald int sample_bits = params_width(params);
39551a630a7SRichard Fitzgerald int slot_width, slot_count;
3961e974e5bSRichard Fitzgerald int i, ret;
3971e974e5bSRichard Fitzgerald
3981e974e5bSRichard Fitzgerald if (!simple_dai || !simple_dai->tdm_width_map)
3991e974e5bSRichard Fitzgerald return 0;
4001e974e5bSRichard Fitzgerald
40151a630a7SRichard Fitzgerald slot_width = simple_dai->slot_width;
40251a630a7SRichard Fitzgerald slot_count = simple_dai->slots;
40351a630a7SRichard Fitzgerald
4041e974e5bSRichard Fitzgerald if (slot_width == 0)
4051e974e5bSRichard Fitzgerald slot_width = sample_bits;
4061e974e5bSRichard Fitzgerald
4071e974e5bSRichard Fitzgerald for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
4081e974e5bSRichard Fitzgerald if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
4091e974e5bSRichard Fitzgerald slot_width = simple_dai->tdm_width_map[i].slot_width;
4101e974e5bSRichard Fitzgerald slot_count = simple_dai->tdm_width_map[i].slot_count;
4111e974e5bSRichard Fitzgerald break;
4121e974e5bSRichard Fitzgerald }
4131e974e5bSRichard Fitzgerald }
4141e974e5bSRichard Fitzgerald
4151e974e5bSRichard Fitzgerald ret = snd_soc_dai_set_tdm_slot(dai,
4161e974e5bSRichard Fitzgerald simple_dai->tx_slot_mask,
4171e974e5bSRichard Fitzgerald simple_dai->rx_slot_mask,
4181e974e5bSRichard Fitzgerald slot_count,
4191e974e5bSRichard Fitzgerald slot_width);
4201e974e5bSRichard Fitzgerald if (ret && ret != -ENOTSUPP) {
4211e974e5bSRichard Fitzgerald dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
4221e974e5bSRichard Fitzgerald return ret;
4231e974e5bSRichard Fitzgerald }
4241e974e5bSRichard Fitzgerald
4251e974e5bSRichard Fitzgerald return 0;
4261e974e5bSRichard Fitzgerald }
4271e974e5bSRichard Fitzgerald
asoc_simple_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)428f48dcbb6SKuninori Morimoto int asoc_simple_hw_params(struct snd_pcm_substream *substream,
429f48dcbb6SKuninori Morimoto struct snd_pcm_hw_params *params)
430f48dcbb6SKuninori Morimoto {
4319ae035e2SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
432fafc05aaSKuninori Morimoto struct asoc_simple_dai *pdai;
433fafc05aaSKuninori Morimoto struct snd_soc_dai *sdai;
434f48dcbb6SKuninori Morimoto struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
435fafc05aaSKuninori Morimoto struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
436f48dcbb6SKuninori Morimoto unsigned int mclk, mclk_fs = 0;
437fafc05aaSKuninori Morimoto int i, ret;
438f48dcbb6SKuninori Morimoto
439fafc05aaSKuninori Morimoto if (props->mclk_fs)
440fafc05aaSKuninori Morimoto mclk_fs = props->mclk_fs;
441f48dcbb6SKuninori Morimoto
442f48dcbb6SKuninori Morimoto if (mclk_fs) {
443ce2f7b8dSRobert Hancock struct snd_soc_component *component;
444f48dcbb6SKuninori Morimoto mclk = params_rate(params) * mclk_fs;
445f48dcbb6SKuninori Morimoto
446fafc05aaSKuninori Morimoto for_each_prop_dai_codec(props, i, pdai) {
4475ca2ab45SRobert Hancock ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
448f48dcbb6SKuninori Morimoto if (ret < 0)
449f48dcbb6SKuninori Morimoto return ret;
450fafc05aaSKuninori Morimoto }
451ce2f7b8dSRobert Hancock
452fafc05aaSKuninori Morimoto for_each_prop_dai_cpu(props, i, pdai) {
4535ca2ab45SRobert Hancock ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
454f48dcbb6SKuninori Morimoto if (ret < 0)
455f48dcbb6SKuninori Morimoto return ret;
456fafc05aaSKuninori Morimoto }
457ce2f7b8dSRobert Hancock
458ce2f7b8dSRobert Hancock /* Ensure sysclk is set on all components in case any
459ce2f7b8dSRobert Hancock * (such as platform components) are missed by calls to
460ce2f7b8dSRobert Hancock * snd_soc_dai_set_sysclk.
461ce2f7b8dSRobert Hancock */
462ce2f7b8dSRobert Hancock for_each_rtd_components(rtd, i, component) {
463ce2f7b8dSRobert Hancock ret = snd_soc_component_set_sysclk(component, 0, 0,
464ce2f7b8dSRobert Hancock mclk, SND_SOC_CLOCK_IN);
465ce2f7b8dSRobert Hancock if (ret && ret != -ENOTSUPP)
466ce2f7b8dSRobert Hancock return ret;
467ce2f7b8dSRobert Hancock }
468ce2f7b8dSRobert Hancock
469fafc05aaSKuninori Morimoto for_each_rtd_codec_dais(rtd, i, sdai) {
470fafc05aaSKuninori Morimoto ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);
471f48dcbb6SKuninori Morimoto if (ret && ret != -ENOTSUPP)
472fafc05aaSKuninori Morimoto return ret;
473fafc05aaSKuninori Morimoto }
474ce2f7b8dSRobert Hancock
475fafc05aaSKuninori Morimoto for_each_rtd_cpu_dais(rtd, i, sdai) {
476fafc05aaSKuninori Morimoto ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);
477f48dcbb6SKuninori Morimoto if (ret && ret != -ENOTSUPP)
478fafc05aaSKuninori Morimoto return ret;
479fafc05aaSKuninori Morimoto }
480f48dcbb6SKuninori Morimoto }
4811e974e5bSRichard Fitzgerald
4821e974e5bSRichard Fitzgerald for_each_prop_dai_codec(props, i, pdai) {
4831e974e5bSRichard Fitzgerald sdai = asoc_rtd_to_codec(rtd, i);
4841e974e5bSRichard Fitzgerald ret = asoc_simple_set_tdm(sdai, pdai, params);
4851e974e5bSRichard Fitzgerald if (ret < 0)
4861e974e5bSRichard Fitzgerald return ret;
4871e974e5bSRichard Fitzgerald }
4881e974e5bSRichard Fitzgerald
4891e974e5bSRichard Fitzgerald for_each_prop_dai_cpu(props, i, pdai) {
4901e974e5bSRichard Fitzgerald sdai = asoc_rtd_to_cpu(rtd, i);
4911e974e5bSRichard Fitzgerald ret = asoc_simple_set_tdm(sdai, pdai, params);
4921e974e5bSRichard Fitzgerald if (ret < 0)
4931e974e5bSRichard Fitzgerald return ret;
4941e974e5bSRichard Fitzgerald }
4951e974e5bSRichard Fitzgerald
496f48dcbb6SKuninori Morimoto return 0;
497f48dcbb6SKuninori Morimoto }
498f48dcbb6SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
499f48dcbb6SKuninori Morimoto
asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)500629f7544SKuninori Morimoto int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
501629f7544SKuninori Morimoto struct snd_pcm_hw_params *params)
502629f7544SKuninori Morimoto {
503629f7544SKuninori Morimoto struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
504629f7544SKuninori Morimoto struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
5055c065eafSKuninori Morimoto struct asoc_simple_data *data = &dai_props->adata;
5065c065eafSKuninori Morimoto struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
5075c065eafSKuninori Morimoto struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
508629f7544SKuninori Morimoto
5095c065eafSKuninori Morimoto if (data->convert_rate)
5105c065eafSKuninori Morimoto rate->min =
5115c065eafSKuninori Morimoto rate->max = data->convert_rate;
5125c065eafSKuninori Morimoto
5135c065eafSKuninori Morimoto if (data->convert_channels)
5145c065eafSKuninori Morimoto channels->min =
5155c065eafSKuninori Morimoto channels->max = data->convert_channels;
5165c065eafSKuninori Morimoto
5175c065eafSKuninori Morimoto if (data->convert_sample_format)
5185c065eafSKuninori Morimoto asoc_simple_fixup_sample_fmt(data, params);
519629f7544SKuninori Morimoto
520629f7544SKuninori Morimoto return 0;
521629f7544SKuninori Morimoto }
522629f7544SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
523629f7544SKuninori Morimoto
asoc_simple_init_dai(struct snd_soc_dai * dai,struct asoc_simple_dai * simple_dai)524ad11e59fSKuninori Morimoto static int asoc_simple_init_dai(struct snd_soc_dai *dai,
52521ba62f8SKuninori Morimoto struct asoc_simple_dai *simple_dai)
52621ba62f8SKuninori Morimoto {
52721ba62f8SKuninori Morimoto int ret;
52821ba62f8SKuninori Morimoto
529f31a1710SKuninori Morimoto if (!simple_dai)
530f31a1710SKuninori Morimoto return 0;
531f31a1710SKuninori Morimoto
53221ba62f8SKuninori Morimoto if (simple_dai->sysclk) {
533a728f560SVitaly Wool ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
534a728f560SVitaly Wool simple_dai->clk_direction);
53521ba62f8SKuninori Morimoto if (ret && ret != -ENOTSUPP) {
53621ba62f8SKuninori Morimoto dev_err(dai->dev, "simple-card: set_sysclk error\n");
53721ba62f8SKuninori Morimoto return ret;
53821ba62f8SKuninori Morimoto }
53921ba62f8SKuninori Morimoto }
54021ba62f8SKuninori Morimoto
54121ba62f8SKuninori Morimoto if (simple_dai->slots) {
54221ba62f8SKuninori Morimoto ret = snd_soc_dai_set_tdm_slot(dai,
54321ba62f8SKuninori Morimoto simple_dai->tx_slot_mask,
54421ba62f8SKuninori Morimoto simple_dai->rx_slot_mask,
54521ba62f8SKuninori Morimoto simple_dai->slots,
54621ba62f8SKuninori Morimoto simple_dai->slot_width);
54721ba62f8SKuninori Morimoto if (ret && ret != -ENOTSUPP) {
54821ba62f8SKuninori Morimoto dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
54921ba62f8SKuninori Morimoto return ret;
55021ba62f8SKuninori Morimoto }
55121ba62f8SKuninori Morimoto }
55221ba62f8SKuninori Morimoto
55321ba62f8SKuninori Morimoto return 0;
55421ba62f8SKuninori Morimoto }
555ad934ca8SKuninori Morimoto
asoc_simple_component_is_codec(struct snd_soc_component * component)55628086d05SCharles Keepax static inline int asoc_simple_component_is_codec(struct snd_soc_component *component)
55728086d05SCharles Keepax {
55828086d05SCharles Keepax return component->driver->endianness;
55928086d05SCharles Keepax }
56028086d05SCharles Keepax
asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime * rtd,struct simple_dai_props * dai_props)561ff31753fSKuninori Morimoto static int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
56295cfc0a0SSamuel Holland struct simple_dai_props *dai_props)
56395cfc0a0SSamuel Holland {
56495cfc0a0SSamuel Holland struct snd_soc_dai_link *dai_link = rtd->dai_link;
56595cfc0a0SSamuel Holland struct snd_soc_component *component;
566a1cd7e80SKuninori Morimoto struct snd_soc_pcm_stream *c2c_params;
56795cfc0a0SSamuel Holland struct snd_pcm_hardware hw;
56895cfc0a0SSamuel Holland int i, ret, stream;
56995cfc0a0SSamuel Holland
57075d1b390SKuninori Morimoto /* Do nothing if it already has Codec2Codec settings */
571a1cd7e80SKuninori Morimoto if (dai_link->c2c_params)
57275d1b390SKuninori Morimoto return 0;
57375d1b390SKuninori Morimoto
57416b7ba9cSKuninori Morimoto /* Do nothing if it was DPCM :: BE */
57516b7ba9cSKuninori Morimoto if (dai_link->no_pcm)
57616b7ba9cSKuninori Morimoto return 0;
57716b7ba9cSKuninori Morimoto
57801e90ee1SKuninori Morimoto /* Only Codecs */
57995cfc0a0SSamuel Holland for_each_rtd_components(rtd, i, component) {
58028086d05SCharles Keepax if (!asoc_simple_component_is_codec(component))
58195cfc0a0SSamuel Holland return 0;
58295cfc0a0SSamuel Holland }
58395cfc0a0SSamuel Holland
58495cfc0a0SSamuel Holland /* Assumes the capabilities are the same for all supported streams */
58540a92dbcSKuninori Morimoto for_each_pcm_streams(stream) {
58695cfc0a0SSamuel Holland ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
58795cfc0a0SSamuel Holland if (ret == 0)
58895cfc0a0SSamuel Holland break;
58995cfc0a0SSamuel Holland }
59095cfc0a0SSamuel Holland
59195cfc0a0SSamuel Holland if (ret < 0) {
59295cfc0a0SSamuel Holland dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
59395cfc0a0SSamuel Holland return ret;
59495cfc0a0SSamuel Holland }
59595cfc0a0SSamuel Holland
596a1cd7e80SKuninori Morimoto c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL);
597a1cd7e80SKuninori Morimoto if (!c2c_params)
59895cfc0a0SSamuel Holland return -ENOMEM;
59995cfc0a0SSamuel Holland
600a1cd7e80SKuninori Morimoto c2c_params->formats = hw.formats;
601a1cd7e80SKuninori Morimoto c2c_params->rates = hw.rates;
602a1cd7e80SKuninori Morimoto c2c_params->rate_min = hw.rate_min;
603a1cd7e80SKuninori Morimoto c2c_params->rate_max = hw.rate_max;
604a1cd7e80SKuninori Morimoto c2c_params->channels_min = hw.channels_min;
605a1cd7e80SKuninori Morimoto c2c_params->channels_max = hw.channels_max;
60695cfc0a0SSamuel Holland
607a1cd7e80SKuninori Morimoto dai_link->c2c_params = c2c_params;
608a1cd7e80SKuninori Morimoto dai_link->num_c2c_params = 1;
60995cfc0a0SSamuel Holland
61095cfc0a0SSamuel Holland return 0;
61195cfc0a0SSamuel Holland }
61295cfc0a0SSamuel Holland
asoc_simple_dai_init(struct snd_soc_pcm_runtime * rtd)613ad934ca8SKuninori Morimoto int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
614ad934ca8SKuninori Morimoto {
615ad934ca8SKuninori Morimoto struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
616fafc05aaSKuninori Morimoto struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
617fafc05aaSKuninori Morimoto struct asoc_simple_dai *dai;
618fafc05aaSKuninori Morimoto int i, ret;
619ad934ca8SKuninori Morimoto
620fafc05aaSKuninori Morimoto for_each_prop_dai_codec(props, i, dai) {
621fafc05aaSKuninori Morimoto ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai);
622ad934ca8SKuninori Morimoto if (ret < 0)
623ad934ca8SKuninori Morimoto return ret;
624fafc05aaSKuninori Morimoto }
625fafc05aaSKuninori Morimoto for_each_prop_dai_cpu(props, i, dai) {
626fafc05aaSKuninori Morimoto ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai);
627ad934ca8SKuninori Morimoto if (ret < 0)
628ad934ca8SKuninori Morimoto return ret;
629fafc05aaSKuninori Morimoto }
630ad934ca8SKuninori Morimoto
631ff31753fSKuninori Morimoto ret = asoc_simple_init_for_codec2codec(rtd, props);
63295cfc0a0SSamuel Holland if (ret < 0)
63395cfc0a0SSamuel Holland return ret;
63495cfc0a0SSamuel Holland
635ad934ca8SKuninori Morimoto return 0;
636ad934ca8SKuninori Morimoto }
637ad934ca8SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
63821ba62f8SKuninori Morimoto
asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component * platforms,struct snd_soc_dai_link_component * cpus)639c826ec03SKuninori Morimoto void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
640c826ec03SKuninori Morimoto struct snd_soc_dai_link_component *cpus)
641c262c9abSKuninori Morimoto {
642dcf08424SKuninori Morimoto /*
643dcf08424SKuninori Morimoto * Assumes Platform == CPU
644dcf08424SKuninori Morimoto *
645dcf08424SKuninori Morimoto * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform
646dcf08424SKuninori Morimoto * are different Component, but are sharing same component->dev.
647dcf08424SKuninori Morimoto *
648dcf08424SKuninori Morimoto * Let's assume Platform is same as CPU if it doesn't identify Platform on DT.
649dcf08424SKuninori Morimoto * see
650dcf08424SKuninori Morimoto * simple-card.c :: simple_count_noml()
651dcf08424SKuninori Morimoto */
652c826ec03SKuninori Morimoto if (!platforms->of_node)
653988bad5eSKuninori Morimoto snd_soc_dlc_use_cpu_as_platform(platforms, cpus);
654c262c9abSKuninori Morimoto }
655ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
656c262c9abSKuninori Morimoto
asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component * cpus,int is_single_links)657c826ec03SKuninori Morimoto void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,
658983cebd6SKuninori Morimoto int is_single_links)
659983cebd6SKuninori Morimoto {
660983cebd6SKuninori Morimoto /*
661983cebd6SKuninori Morimoto * In soc_bind_dai_link() will check cpu name after
662983cebd6SKuninori Morimoto * of_node matching if dai_link has cpu_dai_name.
663983cebd6SKuninori Morimoto * but, it will never match if name was created by
664983cebd6SKuninori Morimoto * fmt_single_name() remove cpu_dai_name if cpu_args
665983cebd6SKuninori Morimoto * was 0. See:
666983cebd6SKuninori Morimoto * fmt_single_name()
667983cebd6SKuninori Morimoto * fmt_multiple_name()
668983cebd6SKuninori Morimoto */
669983cebd6SKuninori Morimoto if (is_single_links)
670c826ec03SKuninori Morimoto cpus->dai_name = NULL;
671983cebd6SKuninori Morimoto }
672ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
673983cebd6SKuninori Morimoto
asoc_simple_clean_reference(struct snd_soc_card * card)674e6f08af6SUwe Kleine-König void asoc_simple_clean_reference(struct snd_soc_card *card)
6750f4e0711SKuninori Morimoto {
6760f4e0711SKuninori Morimoto struct snd_soc_dai_link *dai_link;
6771cf68057SKuninori Morimoto struct snd_soc_dai_link_component *cpu;
6781cf68057SKuninori Morimoto struct snd_soc_dai_link_component *codec;
6791cf68057SKuninori Morimoto int i, j;
6800f4e0711SKuninori Morimoto
6817fe072b4SKuninori Morimoto for_each_card_prelinks(card, i, dai_link) {
6821cf68057SKuninori Morimoto for_each_link_cpus(dai_link, j, cpu)
6831cf68057SKuninori Morimoto of_node_put(cpu->of_node);
6841cf68057SKuninori Morimoto for_each_link_codecs(dai_link, j, codec)
6851cf68057SKuninori Morimoto of_node_put(codec->of_node);
6860f4e0711SKuninori Morimoto }
6870f4e0711SKuninori Morimoto }
688ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
6890f4e0711SKuninori Morimoto
asoc_simple_parse_routing(struct snd_soc_card * card,char * prefix)690ad11e59fSKuninori Morimoto int asoc_simple_parse_routing(struct snd_soc_card *card,
69133404f3fSKuninori Morimoto char *prefix)
6923296d078SKuninori Morimoto {
6933296d078SKuninori Morimoto struct device_node *node = card->dev->of_node;
6943296d078SKuninori Morimoto char prop[128];
6953296d078SKuninori Morimoto
6963296d078SKuninori Morimoto if (!prefix)
6973296d078SKuninori Morimoto prefix = "";
6983296d078SKuninori Morimoto
6993296d078SKuninori Morimoto snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
7003296d078SKuninori Morimoto
70133404f3fSKuninori Morimoto if (!of_property_read_bool(node, prop))
7023296d078SKuninori Morimoto return 0;
7033296d078SKuninori Morimoto
7043296d078SKuninori Morimoto return snd_soc_of_parse_audio_routing(card, prop);
7053296d078SKuninori Morimoto }
706ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
7073296d078SKuninori Morimoto
asoc_simple_parse_widgets(struct snd_soc_card * card,char * prefix)708ad11e59fSKuninori Morimoto int asoc_simple_parse_widgets(struct snd_soc_card *card,
709b31f11d0SKuninori Morimoto char *prefix)
710b31f11d0SKuninori Morimoto {
711b31f11d0SKuninori Morimoto struct device_node *node = card->dev->of_node;
712b31f11d0SKuninori Morimoto char prop[128];
713b31f11d0SKuninori Morimoto
714b31f11d0SKuninori Morimoto if (!prefix)
715b31f11d0SKuninori Morimoto prefix = "";
716b31f11d0SKuninori Morimoto
717b31f11d0SKuninori Morimoto snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
718b31f11d0SKuninori Morimoto
719b31f11d0SKuninori Morimoto if (of_property_read_bool(node, prop))
720b31f11d0SKuninori Morimoto return snd_soc_of_parse_audio_simple_widgets(card, prop);
721b31f11d0SKuninori Morimoto
722b31f11d0SKuninori Morimoto /* no widgets is not error */
723b31f11d0SKuninori Morimoto return 0;
724b31f11d0SKuninori Morimoto }
725ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
726b31f11d0SKuninori Morimoto
asoc_simple_parse_pin_switches(struct snd_soc_card * card,char * prefix)72790194281SPaul Cercueil int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
72890194281SPaul Cercueil char *prefix)
72990194281SPaul Cercueil {
73090194281SPaul Cercueil char prop[128];
73190194281SPaul Cercueil
73290194281SPaul Cercueil if (!prefix)
73390194281SPaul Cercueil prefix = "";
73490194281SPaul Cercueil
73590194281SPaul Cercueil snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
73690194281SPaul Cercueil
7373d4641a4SStephan Gerhold return snd_soc_of_parse_pin_switches(card, prop);
73890194281SPaul Cercueil }
73990194281SPaul Cercueil EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
74090194281SPaul Cercueil
asoc_simple_init_jack(struct snd_soc_card * card,struct asoc_simple_jack * sjack,int is_hp,char * prefix,char * pin)741ad11e59fSKuninori Morimoto int asoc_simple_init_jack(struct snd_soc_card *card,
74262c2c9fcSKatsuhiro Suzuki struct asoc_simple_jack *sjack,
743764aafdbSShengjiu Wang int is_hp, char *prefix,
744764aafdbSShengjiu Wang char *pin)
74562c2c9fcSKatsuhiro Suzuki {
74662c2c9fcSKatsuhiro Suzuki struct device *dev = card->dev;
747355beeedSDmitry Torokhov struct gpio_desc *desc;
74862c2c9fcSKatsuhiro Suzuki char prop[128];
74962c2c9fcSKatsuhiro Suzuki char *pin_name;
75062c2c9fcSKatsuhiro Suzuki char *gpio_name;
75162c2c9fcSKatsuhiro Suzuki int mask;
752355beeedSDmitry Torokhov int error;
75362c2c9fcSKatsuhiro Suzuki
75462c2c9fcSKatsuhiro Suzuki if (!prefix)
75562c2c9fcSKatsuhiro Suzuki prefix = "";
75662c2c9fcSKatsuhiro Suzuki
75762c2c9fcSKatsuhiro Suzuki sjack->gpio.gpio = -ENOENT;
75862c2c9fcSKatsuhiro Suzuki
75962c2c9fcSKatsuhiro Suzuki if (is_hp) {
760355beeedSDmitry Torokhov snprintf(prop, sizeof(prop), "%shp-det", prefix);
761764aafdbSShengjiu Wang pin_name = pin ? pin : "Headphones";
76262c2c9fcSKatsuhiro Suzuki gpio_name = "Headphone detection";
76362c2c9fcSKatsuhiro Suzuki mask = SND_JACK_HEADPHONE;
76462c2c9fcSKatsuhiro Suzuki } else {
765355beeedSDmitry Torokhov snprintf(prop, sizeof(prop), "%smic-det", prefix);
766764aafdbSShengjiu Wang pin_name = pin ? pin : "Mic Jack";
76762c2c9fcSKatsuhiro Suzuki gpio_name = "Mic detection";
76862c2c9fcSKatsuhiro Suzuki mask = SND_JACK_MICROPHONE;
76962c2c9fcSKatsuhiro Suzuki }
77062c2c9fcSKatsuhiro Suzuki
771355beeedSDmitry Torokhov desc = gpiod_get_optional(dev, prop, GPIOD_IN);
772355beeedSDmitry Torokhov error = PTR_ERR_OR_ZERO(desc);
773355beeedSDmitry Torokhov if (error)
774355beeedSDmitry Torokhov return error;
77562c2c9fcSKatsuhiro Suzuki
776355beeedSDmitry Torokhov if (desc) {
777355beeedSDmitry Torokhov error = gpiod_set_consumer_name(desc, gpio_name);
778355beeedSDmitry Torokhov if (error)
779355beeedSDmitry Torokhov return error;
780355beeedSDmitry Torokhov
78162c2c9fcSKatsuhiro Suzuki sjack->pin.pin = pin_name;
78262c2c9fcSKatsuhiro Suzuki sjack->pin.mask = mask;
78362c2c9fcSKatsuhiro Suzuki
78462c2c9fcSKatsuhiro Suzuki sjack->gpio.name = gpio_name;
78562c2c9fcSKatsuhiro Suzuki sjack->gpio.report = mask;
786355beeedSDmitry Torokhov sjack->gpio.desc = desc;
78762c2c9fcSKatsuhiro Suzuki sjack->gpio.debounce_time = 150;
78862c2c9fcSKatsuhiro Suzuki
78919aed2d6SAkihiko Odaki snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
79062c2c9fcSKatsuhiro Suzuki &sjack->pin, 1);
79162c2c9fcSKatsuhiro Suzuki
792355beeedSDmitry Torokhov snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio);
79362c2c9fcSKatsuhiro Suzuki }
79462c2c9fcSKatsuhiro Suzuki
79562c2c9fcSKatsuhiro Suzuki return 0;
79662c2c9fcSKatsuhiro Suzuki }
797ad11e59fSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
79862c2c9fcSKatsuhiro Suzuki
asoc_simple_init_aux_jacks(struct asoc_simple_priv * priv,char * prefix)7999b271207SAstrid Rost int asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv, char *prefix)
8009b271207SAstrid Rost {
8019b271207SAstrid Rost struct snd_soc_card *card = simple_priv_to_card(priv);
8029b271207SAstrid Rost struct snd_soc_component *component;
8039b271207SAstrid Rost int found_jack_index = 0;
8049b271207SAstrid Rost int type = 0;
8059b271207SAstrid Rost int num = 0;
8069b271207SAstrid Rost int ret;
8079b271207SAstrid Rost
8089b271207SAstrid Rost if (priv->aux_jacks)
8099b271207SAstrid Rost return 0;
8109b271207SAstrid Rost
8119b271207SAstrid Rost for_each_card_auxs(card, component) {
8129b271207SAstrid Rost type = snd_soc_component_get_jack_type(component);
8139b271207SAstrid Rost if (type > 0)
8149b271207SAstrid Rost num++;
8159b271207SAstrid Rost }
8169b271207SAstrid Rost if (num < 1)
8179b271207SAstrid Rost return 0;
8189b271207SAstrid Rost
8199b271207SAstrid Rost priv->aux_jacks = devm_kcalloc(card->dev, num,
8209b271207SAstrid Rost sizeof(struct snd_soc_jack), GFP_KERNEL);
8219b271207SAstrid Rost if (!priv->aux_jacks)
8229b271207SAstrid Rost return -ENOMEM;
8239b271207SAstrid Rost
8249b271207SAstrid Rost for_each_card_auxs(card, component) {
8259b271207SAstrid Rost char id[128];
8269b271207SAstrid Rost struct snd_soc_jack *jack;
8279b271207SAstrid Rost
8289b271207SAstrid Rost if (found_jack_index >= num)
8299b271207SAstrid Rost break;
8309b271207SAstrid Rost
8319b271207SAstrid Rost type = snd_soc_component_get_jack_type(component);
8329b271207SAstrid Rost if (type <= 0)
8339b271207SAstrid Rost continue;
8349b271207SAstrid Rost
8359b271207SAstrid Rost /* create jack */
8369b271207SAstrid Rost jack = &(priv->aux_jacks[found_jack_index++]);
8379b271207SAstrid Rost snprintf(id, sizeof(id), "%s-jack", component->name);
8389b271207SAstrid Rost ret = snd_soc_card_jack_new(card, id, type, jack);
8399b271207SAstrid Rost if (ret)
8409b271207SAstrid Rost continue;
8419b271207SAstrid Rost
8429b271207SAstrid Rost (void)snd_soc_component_set_jack(component, jack, NULL);
8439b271207SAstrid Rost }
8449b271207SAstrid Rost return 0;
8459b271207SAstrid Rost }
8469b271207SAstrid Rost EXPORT_SYMBOL_GPL(asoc_simple_init_aux_jacks);
8479b271207SAstrid Rost
asoc_simple_init_priv(struct asoc_simple_priv * priv,struct link_info * li)848ad11e59fSKuninori Morimoto int asoc_simple_init_priv(struct asoc_simple_priv *priv,
84965a5056bSKuninori Morimoto struct link_info *li)
85065a5056bSKuninori Morimoto {
85165a5056bSKuninori Morimoto struct snd_soc_card *card = simple_priv_to_card(priv);
85265a5056bSKuninori Morimoto struct device *dev = simple_priv_to_dev(priv);
85365a5056bSKuninori Morimoto struct snd_soc_dai_link *dai_link;
85465a5056bSKuninori Morimoto struct simple_dai_props *dai_props;
85565a5056bSKuninori Morimoto struct asoc_simple_dai *dais;
856050c7950SKuninori Morimoto struct snd_soc_dai_link_component *dlcs;
857008fe4e5SKuninori Morimoto struct snd_soc_codec_conf *cconf = NULL;
8586976ed01SKuninori Morimoto int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
85965a5056bSKuninori Morimoto
86065a5056bSKuninori Morimoto dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
86165a5056bSKuninori Morimoto dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
862f2138aedSKuninori Morimoto if (!dai_props || !dai_link)
863f2138aedSKuninori Morimoto return -ENOMEM;
864f2138aedSKuninori Morimoto
865f2138aedSKuninori Morimoto /*
866f2138aedSKuninori Morimoto * dais (= CPU+Codec)
867f2138aedSKuninori Morimoto * dlcs (= CPU+Codec+Platform)
868f2138aedSKuninori Morimoto */
869f2138aedSKuninori Morimoto for (i = 0; i < li->link; i++) {
870f2138aedSKuninori Morimoto int cc = li->num[i].cpus + li->num[i].codecs;
871f2138aedSKuninori Morimoto
872f2138aedSKuninori Morimoto dai_num += cc;
873f2138aedSKuninori Morimoto dlc_num += cc + li->num[i].platforms;
874f899006dSKuninori Morimoto
875f899006dSKuninori Morimoto if (!li->num[i].cpus)
876f899006dSKuninori Morimoto cnf_num += li->num[i].codecs;
877f2138aedSKuninori Morimoto }
878f2138aedSKuninori Morimoto
879f2138aedSKuninori Morimoto dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
88036a9d79eSSamuel Holland dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);
881f2138aedSKuninori Morimoto if (!dais || !dlcs)
88265a5056bSKuninori Morimoto return -ENOMEM;
88365a5056bSKuninori Morimoto
884f899006dSKuninori Morimoto if (cnf_num) {
885f899006dSKuninori Morimoto cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);
886008fe4e5SKuninori Morimoto if (!cconf)
887008fe4e5SKuninori Morimoto return -ENOMEM;
888008fe4e5SKuninori Morimoto }
889008fe4e5SKuninori Morimoto
890f899006dSKuninori Morimoto dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
891f899006dSKuninori Morimoto li->link, dai_num, cnf_num);
892f899006dSKuninori Morimoto
89365a5056bSKuninori Morimoto priv->dai_props = dai_props;
89465a5056bSKuninori Morimoto priv->dai_link = dai_link;
89565a5056bSKuninori Morimoto priv->dais = dais;
896050c7950SKuninori Morimoto priv->dlcs = dlcs;
89765a5056bSKuninori Morimoto priv->codec_conf = cconf;
89865a5056bSKuninori Morimoto
89965a5056bSKuninori Morimoto card->dai_link = priv->dai_link;
90065a5056bSKuninori Morimoto card->num_links = li->link;
90165a5056bSKuninori Morimoto card->codec_conf = cconf;
902f899006dSKuninori Morimoto card->num_configs = cnf_num;
90365a5056bSKuninori Morimoto
904f2138aedSKuninori Morimoto for (i = 0; i < li->link; i++) {
905f2138aedSKuninori Morimoto if (li->num[i].cpus) {
906f2138aedSKuninori Morimoto /* Normal CPU */
907f2138aedSKuninori Morimoto dai_link[i].cpus = dlcs;
908f2138aedSKuninori Morimoto dai_props[i].num.cpus =
909f2138aedSKuninori Morimoto dai_link[i].num_cpus = li->num[i].cpus;
91059f5cd96SKuninori Morimoto dai_props[i].cpu_dai = dais;
911f2138aedSKuninori Morimoto
912f2138aedSKuninori Morimoto dlcs += li->num[i].cpus;
91359f5cd96SKuninori Morimoto dais += li->num[i].cpus;
914205eb17eSKuninori Morimoto } else {
915205eb17eSKuninori Morimoto /* DPCM Be's CPU = dummy */
9164d626112SKuninori Morimoto dai_link[i].cpus = &asoc_dummy_dlc;
917205eb17eSKuninori Morimoto dai_props[i].num.cpus =
918205eb17eSKuninori Morimoto dai_link[i].num_cpus = 1;
919f2138aedSKuninori Morimoto }
920f2138aedSKuninori Morimoto
921f2138aedSKuninori Morimoto if (li->num[i].codecs) {
922f2138aedSKuninori Morimoto /* Normal Codec */
923f2138aedSKuninori Morimoto dai_link[i].codecs = dlcs;
924f2138aedSKuninori Morimoto dai_props[i].num.codecs =
925f2138aedSKuninori Morimoto dai_link[i].num_codecs = li->num[i].codecs;
92659f5cd96SKuninori Morimoto dai_props[i].codec_dai = dais;
927f2138aedSKuninori Morimoto
928f2138aedSKuninori Morimoto dlcs += li->num[i].codecs;
92959f5cd96SKuninori Morimoto dais += li->num[i].codecs;
93059f5cd96SKuninori Morimoto
93159f5cd96SKuninori Morimoto if (!li->num[i].cpus) {
93259f5cd96SKuninori Morimoto /* DPCM Be's Codec */
93359f5cd96SKuninori Morimoto dai_props[i].codec_conf = cconf;
93459f5cd96SKuninori Morimoto cconf += li->num[i].codecs;
93559f5cd96SKuninori Morimoto }
936205eb17eSKuninori Morimoto } else {
937205eb17eSKuninori Morimoto /* DPCM Fe's Codec = dummy */
9384d626112SKuninori Morimoto dai_link[i].codecs = &asoc_dummy_dlc;
939205eb17eSKuninori Morimoto dai_props[i].num.codecs =
940205eb17eSKuninori Morimoto dai_link[i].num_codecs = 1;
941f2138aedSKuninori Morimoto }
942f2138aedSKuninori Morimoto
943f2138aedSKuninori Morimoto if (li->num[i].platforms) {
944f2138aedSKuninori Morimoto /* Have Platform */
945f2138aedSKuninori Morimoto dai_link[i].platforms = dlcs;
946f2138aedSKuninori Morimoto dai_props[i].num.platforms =
947f2138aedSKuninori Morimoto dai_link[i].num_platforms = li->num[i].platforms;
948f2138aedSKuninori Morimoto
949f2138aedSKuninori Morimoto dlcs += li->num[i].platforms;
950205eb17eSKuninori Morimoto } else {
951205eb17eSKuninori Morimoto /* Doesn't have Platform */
952205eb17eSKuninori Morimoto dai_link[i].platforms = NULL;
953205eb17eSKuninori Morimoto dai_props[i].num.platforms =
954205eb17eSKuninori Morimoto dai_link[i].num_platforms = 0;
955f2138aedSKuninori Morimoto }
956f2138aedSKuninori Morimoto }
957f2138aedSKuninori Morimoto
95865a5056bSKuninori Morimoto return 0;
95965a5056bSKuninori Morimoto }
96065a5056bSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
96165a5056bSKuninori Morimoto
asoc_simple_remove(struct platform_device * pdev)962f6fcc820SKuninori Morimoto int asoc_simple_remove(struct platform_device *pdev)
963f6fcc820SKuninori Morimoto {
964f6fcc820SKuninori Morimoto struct snd_soc_card *card = platform_get_drvdata(pdev);
965f6fcc820SKuninori Morimoto
966e6f08af6SUwe Kleine-König asoc_simple_clean_reference(card);
967e6f08af6SUwe Kleine-König
968e6f08af6SUwe Kleine-König return 0;
969f6fcc820SKuninori Morimoto }
970f6fcc820SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_simple_remove);
971f6fcc820SKuninori Morimoto
asoc_graph_card_probe(struct snd_soc_card * card)9721a456b1cSKuninori Morimoto int asoc_graph_card_probe(struct snd_soc_card *card)
9731a456b1cSKuninori Morimoto {
9741a456b1cSKuninori Morimoto struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
9751a456b1cSKuninori Morimoto int ret;
9761a456b1cSKuninori Morimoto
9771a456b1cSKuninori Morimoto ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
9781a456b1cSKuninori Morimoto if (ret < 0)
9791a456b1cSKuninori Morimoto return ret;
9801a456b1cSKuninori Morimoto
9811a456b1cSKuninori Morimoto ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL);
9821a456b1cSKuninori Morimoto if (ret < 0)
9831a456b1cSKuninori Morimoto return ret;
9841a456b1cSKuninori Morimoto
9851a456b1cSKuninori Morimoto return 0;
9861a456b1cSKuninori Morimoto }
9871a456b1cSKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_graph_card_probe);
9881a456b1cSKuninori Morimoto
asoc_graph_is_ports0(struct device_node * np)98992939252SKuninori Morimoto int asoc_graph_is_ports0(struct device_node *np)
99092939252SKuninori Morimoto {
99192939252SKuninori Morimoto struct device_node *port, *ports, *ports0, *top;
99292939252SKuninori Morimoto int ret;
99392939252SKuninori Morimoto
99492939252SKuninori Morimoto /* np is "endpoint" or "port" */
99592939252SKuninori Morimoto if (of_node_name_eq(np, "endpoint")) {
99692939252SKuninori Morimoto port = of_get_parent(np);
99792939252SKuninori Morimoto } else {
99892939252SKuninori Morimoto port = np;
99992939252SKuninori Morimoto of_node_get(port);
100092939252SKuninori Morimoto }
100192939252SKuninori Morimoto
100292939252SKuninori Morimoto ports = of_get_parent(port);
100392939252SKuninori Morimoto top = of_get_parent(ports);
100492939252SKuninori Morimoto ports0 = of_get_child_by_name(top, "ports");
100592939252SKuninori Morimoto
100692939252SKuninori Morimoto ret = ports0 == ports;
100792939252SKuninori Morimoto
100892939252SKuninori Morimoto of_node_put(port);
100992939252SKuninori Morimoto of_node_put(ports);
101092939252SKuninori Morimoto of_node_put(ports0);
101192939252SKuninori Morimoto of_node_put(top);
101292939252SKuninori Morimoto
101392939252SKuninori Morimoto return ret;
101492939252SKuninori Morimoto }
101592939252SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
101692939252SKuninori Morimoto
graph_get_dai_id(struct device_node * ep)1017fed4be31SKuninori Morimoto static int graph_get_dai_id(struct device_node *ep)
1018fed4be31SKuninori Morimoto {
1019fed4be31SKuninori Morimoto struct device_node *node;
1020fed4be31SKuninori Morimoto struct device_node *endpoint;
1021fed4be31SKuninori Morimoto struct of_endpoint info;
1022fed4be31SKuninori Morimoto int i, id;
1023fed4be31SKuninori Morimoto int ret;
1024fed4be31SKuninori Morimoto
1025fed4be31SKuninori Morimoto /* use driver specified DAI ID if exist */
1026fed4be31SKuninori Morimoto ret = snd_soc_get_dai_id(ep);
1027fed4be31SKuninori Morimoto if (ret != -ENOTSUPP)
1028fed4be31SKuninori Morimoto return ret;
1029fed4be31SKuninori Morimoto
1030fed4be31SKuninori Morimoto /* use endpoint/port reg if exist */
1031fed4be31SKuninori Morimoto ret = of_graph_parse_endpoint(ep, &info);
1032fed4be31SKuninori Morimoto if (ret == 0) {
1033fed4be31SKuninori Morimoto /*
1034fed4be31SKuninori Morimoto * Because it will count port/endpoint if it doesn't have "reg".
1035fed4be31SKuninori Morimoto * But, we can't judge whether it has "no reg", or "reg = <0>"
1036fed4be31SKuninori Morimoto * only of_graph_parse_endpoint().
1037fed4be31SKuninori Morimoto * We need to check "reg" property
1038fed4be31SKuninori Morimoto */
1039fed4be31SKuninori Morimoto if (of_property_present(ep, "reg"))
1040fed4be31SKuninori Morimoto return info.id;
1041fed4be31SKuninori Morimoto
1042fed4be31SKuninori Morimoto node = of_get_parent(ep);
1043fed4be31SKuninori Morimoto ret = of_property_present(node, "reg");
1044fed4be31SKuninori Morimoto of_node_put(node);
1045fed4be31SKuninori Morimoto if (ret)
1046fed4be31SKuninori Morimoto return info.port;
1047fed4be31SKuninori Morimoto }
1048fed4be31SKuninori Morimoto node = of_graph_get_port_parent(ep);
1049fed4be31SKuninori Morimoto
1050fed4be31SKuninori Morimoto /*
1051fed4be31SKuninori Morimoto * Non HDMI sound case, counting port/endpoint on its DT
1052fed4be31SKuninori Morimoto * is enough. Let's count it.
1053fed4be31SKuninori Morimoto */
1054fed4be31SKuninori Morimoto i = 0;
1055fed4be31SKuninori Morimoto id = -1;
1056fed4be31SKuninori Morimoto for_each_endpoint_of_node(node, endpoint) {
1057fed4be31SKuninori Morimoto if (endpoint == ep)
1058fed4be31SKuninori Morimoto id = i;
1059fed4be31SKuninori Morimoto i++;
1060fed4be31SKuninori Morimoto }
1061fed4be31SKuninori Morimoto
1062fed4be31SKuninori Morimoto of_node_put(node);
1063fed4be31SKuninori Morimoto
1064fed4be31SKuninori Morimoto if (id < 0)
1065fed4be31SKuninori Morimoto return -ENODEV;
1066fed4be31SKuninori Morimoto
1067fed4be31SKuninori Morimoto return id;
1068fed4be31SKuninori Morimoto }
1069fed4be31SKuninori Morimoto
asoc_graph_parse_dai(struct device * dev,struct device_node * ep,struct snd_soc_dai_link_component * dlc,int * is_single_link)107090de551cSKuninori Morimoto int asoc_graph_parse_dai(struct device *dev, struct device_node *ep,
107190de551cSKuninori Morimoto struct snd_soc_dai_link_component *dlc, int *is_single_link)
1072fed4be31SKuninori Morimoto {
1073fed4be31SKuninori Morimoto struct device_node *node;
1074fed4be31SKuninori Morimoto struct of_phandle_args args = {};
107590de551cSKuninori Morimoto struct snd_soc_dai *dai;
1076fed4be31SKuninori Morimoto int ret;
1077fed4be31SKuninori Morimoto
1078fed4be31SKuninori Morimoto if (!ep)
1079fed4be31SKuninori Morimoto return 0;
1080fed4be31SKuninori Morimoto
1081fed4be31SKuninori Morimoto node = of_graph_get_port_parent(ep);
1082fed4be31SKuninori Morimoto
108390de551cSKuninori Morimoto /*
108490de551cSKuninori Morimoto * Try to find from DAI node
108590de551cSKuninori Morimoto */
108690de551cSKuninori Morimoto args.np = ep;
108790de551cSKuninori Morimoto dai = snd_soc_get_dai_via_args(&args);
108890de551cSKuninori Morimoto if (dai) {
108990de551cSKuninori Morimoto dlc->dai_name = snd_soc_dai_name_get(dai);
109090de551cSKuninori Morimoto dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
109190de551cSKuninori Morimoto if (!dlc->dai_args)
109290de551cSKuninori Morimoto return -ENOMEM;
109390de551cSKuninori Morimoto
109490de551cSKuninori Morimoto goto parse_dai_end;
109590de551cSKuninori Morimoto }
109690de551cSKuninori Morimoto
1097fed4be31SKuninori Morimoto /* Get dai->name */
1098fed4be31SKuninori Morimoto args.np = node;
1099fed4be31SKuninori Morimoto args.args[0] = graph_get_dai_id(ep);
1100fed4be31SKuninori Morimoto args.args_count = (of_graph_get_endpoint_count(node) > 1);
1101fed4be31SKuninori Morimoto
1102fed4be31SKuninori Morimoto /*
1103fed4be31SKuninori Morimoto * FIXME
1104fed4be31SKuninori Morimoto *
1105fed4be31SKuninori Morimoto * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
1106fed4be31SKuninori Morimoto * If user unbinded CPU or Codec driver, but not for Sound Card,
1107fed4be31SKuninori Morimoto * dlc->dai_name is keeping unbinded CPU or Codec
1108fed4be31SKuninori Morimoto * driver's pointer.
1109fed4be31SKuninori Morimoto *
1110fed4be31SKuninori Morimoto * If user re-bind CPU or Codec driver again, ALSA SoC will try
1111fed4be31SKuninori Morimoto * to rebind Card via snd_soc_try_rebind_card(), but because of
1112fed4be31SKuninori Morimoto * above reason, it might can't bind Sound Card.
1113fed4be31SKuninori Morimoto * Because Sound Card is pointing to released dai_name pointer.
1114fed4be31SKuninori Morimoto *
1115fed4be31SKuninori Morimoto * To avoid this rebind Card issue,
1116fed4be31SKuninori Morimoto * 1) It needs to alloc memory to keep dai_name eventhough
1117fed4be31SKuninori Morimoto * CPU or Codec driver was unbinded, or
1118fed4be31SKuninori Morimoto * 2) user need to rebind Sound Card everytime
1119fed4be31SKuninori Morimoto * if he unbinded CPU or Codec.
1120fed4be31SKuninori Morimoto */
11210baa2c3aSKuninori Morimoto ret = snd_soc_get_dlc(&args, dlc);
1122fed4be31SKuninori Morimoto if (ret < 0) {
1123fed4be31SKuninori Morimoto of_node_put(node);
1124fed4be31SKuninori Morimoto return ret;
1125fed4be31SKuninori Morimoto }
1126fed4be31SKuninori Morimoto
112790de551cSKuninori Morimoto parse_dai_end:
1128fed4be31SKuninori Morimoto if (is_single_link)
1129fed4be31SKuninori Morimoto *is_single_link = of_graph_get_endpoint_count(node) == 1;
1130fed4be31SKuninori Morimoto
1131fed4be31SKuninori Morimoto return 0;
1132fed4be31SKuninori Morimoto }
1133fed4be31SKuninori Morimoto EXPORT_SYMBOL_GPL(asoc_graph_parse_dai);
1134fed4be31SKuninori Morimoto
11351f85e118SKuninori Morimoto /* Module information */
11361f85e118SKuninori Morimoto MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
11371f85e118SKuninori Morimoto MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
11381f85e118SKuninori Morimoto MODULE_LICENSE("GPL v2");
1139