Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // simple-card-utils.c
0004 //
0005 // Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
0006 
0007 #include <linux/clk.h>
0008 #include <linux/gpio.h>
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/of_gpio.h>
0013 #include <linux/of_graph.h>
0014 #include <sound/jack.h>
0015 #include <sound/pcm_params.h>
0016 #include <sound/simple_card_utils.h>
0017 
0018 void asoc_simple_convert_fixup(struct asoc_simple_data *data,
0019                    struct snd_pcm_hw_params *params)
0020 {
0021     struct snd_interval *rate = hw_param_interval(params,
0022                         SNDRV_PCM_HW_PARAM_RATE);
0023     struct snd_interval *channels = hw_param_interval(params,
0024                         SNDRV_PCM_HW_PARAM_CHANNELS);
0025 
0026     if (data->convert_rate)
0027         rate->min =
0028         rate->max = data->convert_rate;
0029 
0030     if (data->convert_channels)
0031         channels->min =
0032         channels->max = data->convert_channels;
0033 }
0034 EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
0035 
0036 void asoc_simple_parse_convert(struct device_node *np,
0037                    char *prefix,
0038                    struct asoc_simple_data *data)
0039 {
0040     char prop[128];
0041 
0042     if (!prefix)
0043         prefix = "";
0044 
0045     /* sampling rate convert */
0046     snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
0047     of_property_read_u32(np, prop, &data->convert_rate);
0048 
0049     /* channels transfer */
0050     snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
0051     of_property_read_u32(np, prop, &data->convert_channels);
0052 }
0053 EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
0054 
0055 int asoc_simple_parse_daifmt(struct device *dev,
0056                  struct device_node *node,
0057                  struct device_node *codec,
0058                  char *prefix,
0059                  unsigned int *retfmt)
0060 {
0061     struct device_node *bitclkmaster = NULL;
0062     struct device_node *framemaster = NULL;
0063     unsigned int daifmt;
0064 
0065     daifmt = snd_soc_daifmt_parse_format(node, prefix);
0066 
0067     snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);
0068     if (!bitclkmaster && !framemaster) {
0069         /*
0070          * No dai-link level and master setting was not found from
0071          * sound node level, revert back to legacy DT parsing and
0072          * take the settings from codec node.
0073          */
0074         dev_dbg(dev, "Revert to legacy daifmt parsing\n");
0075 
0076         daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);
0077     } else {
0078         daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(
0079                 ((codec == bitclkmaster) << 4) | (codec == framemaster));
0080     }
0081 
0082     of_node_put(bitclkmaster);
0083     of_node_put(framemaster);
0084 
0085     *retfmt = daifmt;
0086 
0087     return 0;
0088 }
0089 EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
0090 
0091 int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
0092                     struct asoc_simple_dai *dai)
0093 {
0094     u32 *array_values, *p;
0095     int n, i, ret;
0096 
0097     if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))
0098         return 0;
0099 
0100     n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
0101     if (n % 3) {
0102         dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
0103         return -EINVAL;
0104     }
0105 
0106     dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
0107     if (!dai->tdm_width_map)
0108         return -ENOMEM;
0109 
0110     array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
0111     if (!array_values)
0112         return -ENOMEM;
0113 
0114     ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
0115     if (ret < 0) {
0116         dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
0117         goto out;
0118     }
0119 
0120     p = array_values;
0121     for (i = 0; i < n / 3; ++i) {
0122         dai->tdm_width_map[i].sample_bits = *p++;
0123         dai->tdm_width_map[i].slot_width = *p++;
0124         dai->tdm_width_map[i].slot_count = *p++;
0125     }
0126 
0127     dai->n_tdm_widths = i;
0128     ret = 0;
0129 out:
0130     kfree(array_values);
0131 
0132     return ret;
0133 }
0134 EXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map);
0135 
0136 int asoc_simple_set_dailink_name(struct device *dev,
0137                  struct snd_soc_dai_link *dai_link,
0138                  const char *fmt, ...)
0139 {
0140     va_list ap;
0141     char *name = NULL;
0142     int ret = -ENOMEM;
0143 
0144     va_start(ap, fmt);
0145     name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
0146     va_end(ap);
0147 
0148     if (name) {
0149         ret = 0;
0150 
0151         dai_link->name      = name;
0152         dai_link->stream_name   = name;
0153     }
0154 
0155     return ret;
0156 }
0157 EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
0158 
0159 int asoc_simple_parse_card_name(struct snd_soc_card *card,
0160                 char *prefix)
0161 {
0162     int ret;
0163 
0164     if (!prefix)
0165         prefix = "";
0166 
0167     /* Parse the card name from DT */
0168     ret = snd_soc_of_parse_card_name(card, "label");
0169     if (ret < 0 || !card->name) {
0170         char prop[128];
0171 
0172         snprintf(prop, sizeof(prop), "%sname", prefix);
0173         ret = snd_soc_of_parse_card_name(card, prop);
0174         if (ret < 0)
0175             return ret;
0176     }
0177 
0178     if (!card->name && card->dai_link)
0179         card->name = card->dai_link->name;
0180 
0181     return 0;
0182 }
0183 EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
0184 
0185 static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
0186 {
0187     if (dai)
0188         return clk_prepare_enable(dai->clk);
0189 
0190     return 0;
0191 }
0192 
0193 static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
0194 {
0195     if (dai)
0196         clk_disable_unprepare(dai->clk);
0197 }
0198 
0199 int asoc_simple_parse_clk(struct device *dev,
0200               struct device_node *node,
0201               struct asoc_simple_dai *simple_dai,
0202               struct snd_soc_dai_link_component *dlc)
0203 {
0204     struct clk *clk;
0205     u32 val;
0206 
0207     /*
0208      * Parse dai->sysclk come from "clocks = <&xxx>"
0209      * (if system has common clock)
0210      *  or "system-clock-frequency = <xxx>"
0211      *  or device's module clock.
0212      */
0213     clk = devm_get_clk_from_child(dev, node, NULL);
0214     simple_dai->clk_fixed = of_property_read_bool(
0215         node, "system-clock-fixed");
0216     if (!IS_ERR(clk)) {
0217         simple_dai->sysclk = clk_get_rate(clk);
0218 
0219         simple_dai->clk = clk;
0220     } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
0221         simple_dai->sysclk = val;
0222         simple_dai->clk_fixed = true;
0223     } else {
0224         clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
0225         if (!IS_ERR(clk))
0226             simple_dai->sysclk = clk_get_rate(clk);
0227     }
0228 
0229     if (of_property_read_bool(node, "system-clock-direction-out"))
0230         simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
0231 
0232     return 0;
0233 }
0234 EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
0235 
0236 static int asoc_simple_check_fixed_sysclk(struct device *dev,
0237                       struct asoc_simple_dai *dai,
0238                       unsigned int *fixed_sysclk)
0239 {
0240     if (dai->clk_fixed) {
0241         if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {
0242             dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",
0243                 *fixed_sysclk, dai->sysclk);
0244             return -EINVAL;
0245         }
0246         *fixed_sysclk = dai->sysclk;
0247     }
0248 
0249     return 0;
0250 }
0251 
0252 int asoc_simple_startup(struct snd_pcm_substream *substream)
0253 {
0254     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0255     struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
0256     struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
0257     struct asoc_simple_dai *dai;
0258     unsigned int fixed_sysclk = 0;
0259     int i1, i2, i;
0260     int ret;
0261 
0262     for_each_prop_dai_cpu(props, i1, dai) {
0263         ret = asoc_simple_clk_enable(dai);
0264         if (ret)
0265             goto cpu_err;
0266         ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
0267         if (ret)
0268             goto cpu_err;
0269     }
0270 
0271     for_each_prop_dai_codec(props, i2, dai) {
0272         ret = asoc_simple_clk_enable(dai);
0273         if (ret)
0274             goto codec_err;
0275         ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
0276         if (ret)
0277             goto codec_err;
0278     }
0279 
0280     if (fixed_sysclk && props->mclk_fs) {
0281         unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;
0282 
0283         if (fixed_sysclk % props->mclk_fs) {
0284             dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",
0285                 fixed_sysclk, props->mclk_fs);
0286             return -EINVAL;
0287         }
0288         ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,
0289             fixed_rate, fixed_rate);
0290         if (ret)
0291             goto codec_err;
0292     }
0293 
0294     return 0;
0295 
0296 codec_err:
0297     for_each_prop_dai_codec(props, i, dai) {
0298         if (i >= i2)
0299             break;
0300         asoc_simple_clk_disable(dai);
0301     }
0302 cpu_err:
0303     for_each_prop_dai_cpu(props, i, dai) {
0304         if (i >= i1)
0305             break;
0306         asoc_simple_clk_disable(dai);
0307     }
0308     return ret;
0309 }
0310 EXPORT_SYMBOL_GPL(asoc_simple_startup);
0311 
0312 void asoc_simple_shutdown(struct snd_pcm_substream *substream)
0313 {
0314     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0315     struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
0316     struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
0317     struct asoc_simple_dai *dai;
0318     int i;
0319 
0320     for_each_prop_dai_cpu(props, i, dai) {
0321         struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i);
0322 
0323         if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
0324             snd_soc_dai_set_sysclk(cpu_dai,
0325                            0, 0, SND_SOC_CLOCK_OUT);
0326 
0327         asoc_simple_clk_disable(dai);
0328     }
0329     for_each_prop_dai_codec(props, i, dai) {
0330         struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i);
0331 
0332         if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
0333             snd_soc_dai_set_sysclk(codec_dai,
0334                            0, 0, SND_SOC_CLOCK_IN);
0335 
0336         asoc_simple_clk_disable(dai);
0337     }
0338 }
0339 EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
0340 
0341 static int asoc_simple_set_clk_rate(struct device *dev,
0342                     struct asoc_simple_dai *simple_dai,
0343                     unsigned long rate)
0344 {
0345     if (!simple_dai)
0346         return 0;
0347 
0348     if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
0349         dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
0350         return -EINVAL;
0351     }
0352 
0353     if (!simple_dai->clk)
0354         return 0;
0355 
0356     if (clk_get_rate(simple_dai->clk) == rate)
0357         return 0;
0358 
0359     return clk_set_rate(simple_dai->clk, rate);
0360 }
0361 
0362 static int asoc_simple_set_tdm(struct snd_soc_dai *dai,
0363                 struct asoc_simple_dai *simple_dai,
0364                 struct snd_pcm_hw_params *params)
0365 {
0366     int sample_bits = params_width(params);
0367     int slot_width, slot_count;
0368     int i, ret;
0369 
0370     if (!simple_dai || !simple_dai->tdm_width_map)
0371         return 0;
0372 
0373     slot_width = simple_dai->slot_width;
0374     slot_count = simple_dai->slots;
0375 
0376     if (slot_width == 0)
0377         slot_width = sample_bits;
0378 
0379     for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
0380         if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
0381             slot_width = simple_dai->tdm_width_map[i].slot_width;
0382             slot_count = simple_dai->tdm_width_map[i].slot_count;
0383             break;
0384         }
0385     }
0386 
0387     ret = snd_soc_dai_set_tdm_slot(dai,
0388                        simple_dai->tx_slot_mask,
0389                        simple_dai->rx_slot_mask,
0390                        slot_count,
0391                        slot_width);
0392     if (ret && ret != -ENOTSUPP) {
0393         dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
0394         return ret;
0395     }
0396 
0397     return 0;
0398 }
0399 
0400 int asoc_simple_hw_params(struct snd_pcm_substream *substream,
0401               struct snd_pcm_hw_params *params)
0402 {
0403     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0404     struct asoc_simple_dai *pdai;
0405     struct snd_soc_dai *sdai;
0406     struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
0407     struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
0408     unsigned int mclk, mclk_fs = 0;
0409     int i, ret;
0410 
0411     if (props->mclk_fs)
0412         mclk_fs = props->mclk_fs;
0413 
0414     if (mclk_fs) {
0415         struct snd_soc_component *component;
0416         mclk = params_rate(params) * mclk_fs;
0417 
0418         for_each_prop_dai_codec(props, i, pdai) {
0419             ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
0420             if (ret < 0)
0421                 return ret;
0422         }
0423 
0424         for_each_prop_dai_cpu(props, i, pdai) {
0425             ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
0426             if (ret < 0)
0427                 return ret;
0428         }
0429 
0430         /* Ensure sysclk is set on all components in case any
0431          * (such as platform components) are missed by calls to
0432          * snd_soc_dai_set_sysclk.
0433          */
0434         for_each_rtd_components(rtd, i, component) {
0435             ret = snd_soc_component_set_sysclk(component, 0, 0,
0436                                mclk, SND_SOC_CLOCK_IN);
0437             if (ret && ret != -ENOTSUPP)
0438                 return ret;
0439         }
0440 
0441         for_each_rtd_codec_dais(rtd, i, sdai) {
0442             ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);
0443             if (ret && ret != -ENOTSUPP)
0444                 return ret;
0445         }
0446 
0447         for_each_rtd_cpu_dais(rtd, i, sdai) {
0448             ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);
0449             if (ret && ret != -ENOTSUPP)
0450                 return ret;
0451         }
0452     }
0453 
0454     for_each_prop_dai_codec(props, i, pdai) {
0455         sdai = asoc_rtd_to_codec(rtd, i);
0456         ret = asoc_simple_set_tdm(sdai, pdai, params);
0457         if (ret < 0)
0458             return ret;
0459     }
0460 
0461     for_each_prop_dai_cpu(props, i, pdai) {
0462         sdai = asoc_rtd_to_cpu(rtd, i);
0463         ret = asoc_simple_set_tdm(sdai, pdai, params);
0464         if (ret < 0)
0465             return ret;
0466     }
0467 
0468     return 0;
0469 }
0470 EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
0471 
0472 int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
0473                    struct snd_pcm_hw_params *params)
0474 {
0475     struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
0476     struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
0477 
0478     asoc_simple_convert_fixup(&dai_props->adata, params);
0479 
0480     return 0;
0481 }
0482 EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
0483 
0484 static int asoc_simple_init_dai(struct snd_soc_dai *dai,
0485                      struct asoc_simple_dai *simple_dai)
0486 {
0487     int ret;
0488 
0489     if (!simple_dai)
0490         return 0;
0491 
0492     if (simple_dai->sysclk) {
0493         ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
0494                          simple_dai->clk_direction);
0495         if (ret && ret != -ENOTSUPP) {
0496             dev_err(dai->dev, "simple-card: set_sysclk error\n");
0497             return ret;
0498         }
0499     }
0500 
0501     if (simple_dai->slots) {
0502         ret = snd_soc_dai_set_tdm_slot(dai,
0503                            simple_dai->tx_slot_mask,
0504                            simple_dai->rx_slot_mask,
0505                            simple_dai->slots,
0506                            simple_dai->slot_width);
0507         if (ret && ret != -ENOTSUPP) {
0508             dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
0509             return ret;
0510         }
0511     }
0512 
0513     return 0;
0514 }
0515 
0516 static inline int asoc_simple_component_is_codec(struct snd_soc_component *component)
0517 {
0518     return component->driver->endianness;
0519 }
0520 
0521 static int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
0522                         struct simple_dai_props *dai_props)
0523 {
0524     struct snd_soc_dai_link *dai_link = rtd->dai_link;
0525     struct snd_soc_component *component;
0526     struct snd_soc_pcm_stream *params;
0527     struct snd_pcm_hardware hw;
0528     int i, ret, stream;
0529 
0530     /* Do nothing if it already has Codec2Codec settings */
0531     if (dai_link->params)
0532         return 0;
0533 
0534     /* Do nothing if it was DPCM :: BE */
0535     if (dai_link->no_pcm)
0536         return 0;
0537 
0538     /* Only Codecs */
0539     for_each_rtd_components(rtd, i, component) {
0540         if (!asoc_simple_component_is_codec(component))
0541             return 0;
0542     }
0543 
0544     /* Assumes the capabilities are the same for all supported streams */
0545     for_each_pcm_streams(stream) {
0546         ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
0547         if (ret == 0)
0548             break;
0549     }
0550 
0551     if (ret < 0) {
0552         dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
0553         return ret;
0554     }
0555 
0556     params = devm_kzalloc(rtd->dev, sizeof(*params), GFP_KERNEL);
0557     if (!params)
0558         return -ENOMEM;
0559 
0560     params->formats = hw.formats;
0561     params->rates = hw.rates;
0562     params->rate_min = hw.rate_min;
0563     params->rate_max = hw.rate_max;
0564     params->channels_min = hw.channels_min;
0565     params->channels_max = hw.channels_max;
0566 
0567     dai_link->params = params;
0568     dai_link->num_params = 1;
0569 
0570     return 0;
0571 }
0572 
0573 int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
0574 {
0575     struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
0576     struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
0577     struct asoc_simple_dai *dai;
0578     int i, ret;
0579 
0580     for_each_prop_dai_codec(props, i, dai) {
0581         ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai);
0582         if (ret < 0)
0583             return ret;
0584     }
0585     for_each_prop_dai_cpu(props, i, dai) {
0586         ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai);
0587         if (ret < 0)
0588             return ret;
0589     }
0590 
0591     ret = asoc_simple_init_for_codec2codec(rtd, props);
0592     if (ret < 0)
0593         return ret;
0594 
0595     return 0;
0596 }
0597 EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
0598 
0599 void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
0600                        struct snd_soc_dai_link_component *cpus)
0601 {
0602     /* Assumes platform == cpu */
0603     if (!platforms->of_node)
0604         platforms->of_node = cpus->of_node;
0605 }
0606 EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
0607 
0608 void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,
0609                   int is_single_links)
0610 {
0611     /*
0612      * In soc_bind_dai_link() will check cpu name after
0613      * of_node matching if dai_link has cpu_dai_name.
0614      * but, it will never match if name was created by
0615      * fmt_single_name() remove cpu_dai_name if cpu_args
0616      * was 0. See:
0617      *  fmt_single_name()
0618      *  fmt_multiple_name()
0619      */
0620     if (is_single_links)
0621         cpus->dai_name = NULL;
0622 }
0623 EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
0624 
0625 void asoc_simple_clean_reference(struct snd_soc_card *card)
0626 {
0627     struct snd_soc_dai_link *dai_link;
0628     struct snd_soc_dai_link_component *cpu;
0629     struct snd_soc_dai_link_component *codec;
0630     int i, j;
0631 
0632     for_each_card_prelinks(card, i, dai_link) {
0633         for_each_link_cpus(dai_link, j, cpu)
0634             of_node_put(cpu->of_node);
0635         for_each_link_codecs(dai_link, j, codec)
0636             of_node_put(codec->of_node);
0637     }
0638 }
0639 EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
0640 
0641 int asoc_simple_parse_routing(struct snd_soc_card *card,
0642                   char *prefix)
0643 {
0644     struct device_node *node = card->dev->of_node;
0645     char prop[128];
0646 
0647     if (!prefix)
0648         prefix = "";
0649 
0650     snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
0651 
0652     if (!of_property_read_bool(node, prop))
0653         return 0;
0654 
0655     return snd_soc_of_parse_audio_routing(card, prop);
0656 }
0657 EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
0658 
0659 int asoc_simple_parse_widgets(struct snd_soc_card *card,
0660                   char *prefix)
0661 {
0662     struct device_node *node = card->dev->of_node;
0663     char prop[128];
0664 
0665     if (!prefix)
0666         prefix = "";
0667 
0668     snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
0669 
0670     if (of_property_read_bool(node, prop))
0671         return snd_soc_of_parse_audio_simple_widgets(card, prop);
0672 
0673     /* no widgets is not error */
0674     return 0;
0675 }
0676 EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
0677 
0678 int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
0679                    char *prefix)
0680 {
0681     char prop[128];
0682 
0683     if (!prefix)
0684         prefix = "";
0685 
0686     snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
0687 
0688     return snd_soc_of_parse_pin_switches(card, prop);
0689 }
0690 EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
0691 
0692 int asoc_simple_init_jack(struct snd_soc_card *card,
0693               struct asoc_simple_jack *sjack,
0694               int is_hp, char *prefix,
0695               char *pin)
0696 {
0697     struct device *dev = card->dev;
0698     enum of_gpio_flags flags;
0699     char prop[128];
0700     char *pin_name;
0701     char *gpio_name;
0702     int mask;
0703     int det;
0704 
0705     if (!prefix)
0706         prefix = "";
0707 
0708     sjack->gpio.gpio = -ENOENT;
0709 
0710     if (is_hp) {
0711         snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
0712         pin_name    = pin ? pin : "Headphones";
0713         gpio_name   = "Headphone detection";
0714         mask        = SND_JACK_HEADPHONE;
0715     } else {
0716         snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
0717         pin_name    = pin ? pin : "Mic Jack";
0718         gpio_name   = "Mic detection";
0719         mask        = SND_JACK_MICROPHONE;
0720     }
0721 
0722     det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
0723     if (det == -EPROBE_DEFER)
0724         return -EPROBE_DEFER;
0725 
0726     if (gpio_is_valid(det)) {
0727         sjack->pin.pin      = pin_name;
0728         sjack->pin.mask     = mask;
0729 
0730         sjack->gpio.name    = gpio_name;
0731         sjack->gpio.report  = mask;
0732         sjack->gpio.gpio    = det;
0733         sjack->gpio.invert  = !!(flags & OF_GPIO_ACTIVE_LOW);
0734         sjack->gpio.debounce_time = 150;
0735 
0736         snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack,
0737                        &sjack->pin, 1);
0738 
0739         snd_soc_jack_add_gpios(&sjack->jack, 1,
0740                        &sjack->gpio);
0741     }
0742 
0743     return 0;
0744 }
0745 EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
0746 
0747 int asoc_simple_init_priv(struct asoc_simple_priv *priv,
0748               struct link_info *li)
0749 {
0750     struct snd_soc_card *card = simple_priv_to_card(priv);
0751     struct device *dev = simple_priv_to_dev(priv);
0752     struct snd_soc_dai_link *dai_link;
0753     struct simple_dai_props *dai_props;
0754     struct asoc_simple_dai *dais;
0755     struct snd_soc_dai_link_component *dlcs;
0756     struct snd_soc_codec_conf *cconf = NULL;
0757     int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
0758 
0759     dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
0760     dai_link  = devm_kcalloc(dev, li->link, sizeof(*dai_link),  GFP_KERNEL);
0761     if (!dai_props || !dai_link)
0762         return -ENOMEM;
0763 
0764     /*
0765      * dais (= CPU+Codec)
0766      * dlcs (= CPU+Codec+Platform)
0767      */
0768     for (i = 0; i < li->link; i++) {
0769         int cc = li->num[i].cpus + li->num[i].codecs;
0770 
0771         dai_num += cc;
0772         dlc_num += cc + li->num[i].platforms;
0773 
0774         if (!li->num[i].cpus)
0775             cnf_num += li->num[i].codecs;
0776     }
0777 
0778     dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
0779     dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);
0780     if (!dais || !dlcs)
0781         return -ENOMEM;
0782 
0783     if (cnf_num) {
0784         cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);
0785         if (!cconf)
0786             return -ENOMEM;
0787     }
0788 
0789     dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
0790         li->link, dai_num, cnf_num);
0791 
0792     /* dummy CPU/Codec */
0793     priv->dummy.of_node = NULL;
0794     priv->dummy.dai_name    = "snd-soc-dummy-dai";
0795     priv->dummy.name    = "snd-soc-dummy";
0796 
0797     priv->dai_props     = dai_props;
0798     priv->dai_link      = dai_link;
0799     priv->dais      = dais;
0800     priv->dlcs      = dlcs;
0801     priv->codec_conf    = cconf;
0802 
0803     card->dai_link      = priv->dai_link;
0804     card->num_links     = li->link;
0805     card->codec_conf    = cconf;
0806     card->num_configs   = cnf_num;
0807 
0808     for (i = 0; i < li->link; i++) {
0809         if (li->num[i].cpus) {
0810             /* Normal CPU */
0811             dai_props[i].cpus   =
0812             dai_link[i].cpus    = dlcs;
0813             dai_props[i].num.cpus   =
0814             dai_link[i].num_cpus    = li->num[i].cpus;
0815             dai_props[i].cpu_dai    = dais;
0816 
0817             dlcs += li->num[i].cpus;
0818             dais += li->num[i].cpus;
0819         } else {
0820             /* DPCM Be's CPU = dummy */
0821             dai_props[i].cpus   =
0822             dai_link[i].cpus    = &priv->dummy;
0823             dai_props[i].num.cpus   =
0824             dai_link[i].num_cpus    = 1;
0825         }
0826 
0827         if (li->num[i].codecs) {
0828             /* Normal Codec */
0829             dai_props[i].codecs =
0830             dai_link[i].codecs  = dlcs;
0831             dai_props[i].num.codecs =
0832             dai_link[i].num_codecs  = li->num[i].codecs;
0833             dai_props[i].codec_dai  = dais;
0834 
0835             dlcs += li->num[i].codecs;
0836             dais += li->num[i].codecs;
0837 
0838             if (!li->num[i].cpus) {
0839                 /* DPCM Be's Codec */
0840                 dai_props[i].codec_conf = cconf;
0841                 cconf += li->num[i].codecs;
0842             }
0843         } else {
0844             /* DPCM Fe's Codec = dummy */
0845             dai_props[i].codecs =
0846             dai_link[i].codecs  = &priv->dummy;
0847             dai_props[i].num.codecs =
0848             dai_link[i].num_codecs  = 1;
0849         }
0850 
0851         if (li->num[i].platforms) {
0852             /* Have Platform */
0853             dai_props[i].platforms      =
0854             dai_link[i].platforms       = dlcs;
0855             dai_props[i].num.platforms  =
0856             dai_link[i].num_platforms   = li->num[i].platforms;
0857 
0858             dlcs += li->num[i].platforms;
0859         } else {
0860             /* Doesn't have Platform */
0861             dai_props[i].platforms      =
0862             dai_link[i].platforms       = NULL;
0863             dai_props[i].num.platforms  =
0864             dai_link[i].num_platforms   = 0;
0865         }
0866     }
0867 
0868     return 0;
0869 }
0870 EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
0871 
0872 int asoc_simple_remove(struct platform_device *pdev)
0873 {
0874     struct snd_soc_card *card = platform_get_drvdata(pdev);
0875 
0876     asoc_simple_clean_reference(card);
0877 
0878     return 0;
0879 }
0880 EXPORT_SYMBOL_GPL(asoc_simple_remove);
0881 
0882 int asoc_graph_card_probe(struct snd_soc_card *card)
0883 {
0884     struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
0885     int ret;
0886 
0887     ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
0888     if (ret < 0)
0889         return ret;
0890 
0891     ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL);
0892     if (ret < 0)
0893         return ret;
0894 
0895     return 0;
0896 }
0897 EXPORT_SYMBOL_GPL(asoc_graph_card_probe);
0898 
0899 int asoc_graph_is_ports0(struct device_node *np)
0900 {
0901     struct device_node *port, *ports, *ports0, *top;
0902     int ret;
0903 
0904     /* np is "endpoint" or "port" */
0905     if (of_node_name_eq(np, "endpoint")) {
0906         port = of_get_parent(np);
0907     } else {
0908         port = np;
0909         of_node_get(port);
0910     }
0911 
0912     ports   = of_get_parent(port);
0913     top = of_get_parent(ports);
0914     ports0  = of_get_child_by_name(top, "ports");
0915 
0916     ret = ports0 == ports;
0917 
0918     of_node_put(port);
0919     of_node_put(ports);
0920     of_node_put(ports0);
0921     of_node_put(top);
0922 
0923     return ret;
0924 }
0925 EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
0926 
0927 /* Module information */
0928 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
0929 MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
0930 MODULE_LICENSE("GPL v2");