linux/sound/soc/generic/simple-card-utils.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// simple-card-utils.c
   4//
   5// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
   6
   7#include <linux/clk.h>
   8#include <linux/gpio.h>
   9#include <linux/gpio/consumer.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/of_gpio.h>
  13#include <linux/of_graph.h>
  14#include <sound/jack.h>
  15#include <sound/simple_card_utils.h>
  16
  17void asoc_simple_convert_fixup(struct asoc_simple_data *data,
  18                               struct snd_pcm_hw_params *params)
  19{
  20        struct snd_interval *rate = hw_param_interval(params,
  21                                                SNDRV_PCM_HW_PARAM_RATE);
  22        struct snd_interval *channels = hw_param_interval(params,
  23                                                SNDRV_PCM_HW_PARAM_CHANNELS);
  24
  25        if (data->convert_rate)
  26                rate->min =
  27                rate->max = data->convert_rate;
  28
  29        if (data->convert_channels)
  30                channels->min =
  31                channels->max = data->convert_channels;
  32}
  33EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
  34
  35void asoc_simple_parse_convert(struct device *dev,
  36                               struct device_node *np,
  37                               char *prefix,
  38                               struct asoc_simple_data *data)
  39{
  40        char prop[128];
  41
  42        if (!prefix)
  43                prefix = "";
  44
  45        /* sampling rate convert */
  46        snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
  47        of_property_read_u32(np, prop, &data->convert_rate);
  48
  49        /* channels transfer */
  50        snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
  51        of_property_read_u32(np, prop, &data->convert_channels);
  52}
  53EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
  54
  55int asoc_simple_parse_daifmt(struct device *dev,
  56                             struct device_node *node,
  57                             struct device_node *codec,
  58                             char *prefix,
  59                             unsigned int *retfmt)
  60{
  61        struct device_node *bitclkmaster = NULL;
  62        struct device_node *framemaster = NULL;
  63        unsigned int daifmt;
  64
  65        daifmt = snd_soc_of_parse_daifmt(node, prefix,
  66                                         &bitclkmaster, &framemaster);
  67        daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
  68
  69        if (!bitclkmaster && !framemaster) {
  70                /*
  71                 * No dai-link level and master setting was not found from
  72                 * sound node level, revert back to legacy DT parsing and
  73                 * take the settings from codec node.
  74                 */
  75                dev_dbg(dev, "Revert to legacy daifmt parsing\n");
  76
  77                daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
  78                        (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
  79        } else {
  80                if (codec == bitclkmaster)
  81                        daifmt |= (codec == framemaster) ?
  82                                SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
  83                else
  84                        daifmt |= (codec == framemaster) ?
  85                                SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
  86        }
  87
  88        of_node_put(bitclkmaster);
  89        of_node_put(framemaster);
  90
  91        *retfmt = daifmt;
  92
  93        return 0;
  94}
  95EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
  96
  97int asoc_simple_set_dailink_name(struct device *dev,
  98                                 struct snd_soc_dai_link *dai_link,
  99                                 const char *fmt, ...)
 100{
 101        va_list ap;
 102        char *name = NULL;
 103        int ret = -ENOMEM;
 104
 105        va_start(ap, fmt);
 106        name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
 107        va_end(ap);
 108
 109        if (name) {
 110                ret = 0;
 111
 112                dai_link->name          = name;
 113                dai_link->stream_name   = name;
 114        }
 115
 116        return ret;
 117}
 118EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
 119
 120int asoc_simple_parse_card_name(struct snd_soc_card *card,
 121                                char *prefix)
 122{
 123        int ret;
 124
 125        if (!prefix)
 126                prefix = "";
 127
 128        /* Parse the card name from DT */
 129        ret = snd_soc_of_parse_card_name(card, "label");
 130        if (ret < 0 || !card->name) {
 131                char prop[128];
 132
 133                snprintf(prop, sizeof(prop), "%sname", prefix);
 134                ret = snd_soc_of_parse_card_name(card, prop);
 135                if (ret < 0)
 136                        return ret;
 137        }
 138
 139        if (!card->name && card->dai_link)
 140                card->name = card->dai_link->name;
 141
 142        return 0;
 143}
 144EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
 145
 146static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
 147{
 148        if (dai)
 149                return clk_prepare_enable(dai->clk);
 150
 151        return 0;
 152}
 153
 154static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
 155{
 156        if (dai)
 157                clk_disable_unprepare(dai->clk);
 158}
 159
 160int asoc_simple_parse_clk(struct device *dev,
 161                          struct device_node *node,
 162                          struct asoc_simple_dai *simple_dai,
 163                          struct snd_soc_dai_link_component *dlc)
 164{
 165        struct clk *clk;
 166        u32 val;
 167
 168        /*
 169         * Parse dai->sysclk come from "clocks = <&xxx>"
 170         * (if system has common clock)
 171         *  or "system-clock-frequency = <xxx>"
 172         *  or device's module clock.
 173         */
 174        clk = devm_get_clk_from_child(dev, node, NULL);
 175        if (!IS_ERR(clk)) {
 176                simple_dai->sysclk = clk_get_rate(clk);
 177
 178                simple_dai->clk = clk;
 179        } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
 180                simple_dai->sysclk = val;
 181        } else {
 182                clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
 183                if (!IS_ERR(clk))
 184                        simple_dai->sysclk = clk_get_rate(clk);
 185        }
 186
 187        if (of_property_read_bool(node, "system-clock-direction-out"))
 188                simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
 189
 190        return 0;
 191}
 192EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
 193
 194int asoc_simple_startup(struct snd_pcm_substream *substream)
 195{
 196        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 197        struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
 198        struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
 199        int ret;
 200
 201        ret = asoc_simple_clk_enable(dai_props->cpu_dai);
 202        if (ret)
 203                return ret;
 204
 205        ret = asoc_simple_clk_enable(dai_props->codec_dai);
 206        if (ret)
 207                asoc_simple_clk_disable(dai_props->cpu_dai);
 208
 209        return ret;
 210}
 211EXPORT_SYMBOL_GPL(asoc_simple_startup);
 212
 213void asoc_simple_shutdown(struct snd_pcm_substream *substream)
 214{
 215        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 216        struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
 217        struct simple_dai_props *dai_props =
 218                simple_priv_to_props(priv, rtd->num);
 219
 220        asoc_simple_clk_disable(dai_props->cpu_dai);
 221
 222        asoc_simple_clk_disable(dai_props->codec_dai);
 223}
 224EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
 225
 226static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
 227                                    unsigned long rate)
 228{
 229        if (!simple_dai)
 230                return 0;
 231
 232        if (!simple_dai->clk)
 233                return 0;
 234
 235        if (clk_get_rate(simple_dai->clk) == rate)
 236                return 0;
 237
 238        return clk_set_rate(simple_dai->clk, rate);
 239}
 240
 241int asoc_simple_hw_params(struct snd_pcm_substream *substream,
 242                          struct snd_pcm_hw_params *params)
 243{
 244        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 245        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 246        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 247        struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
 248        struct simple_dai_props *dai_props =
 249                simple_priv_to_props(priv, rtd->num);
 250        unsigned int mclk, mclk_fs = 0;
 251        int ret = 0;
 252
 253        if (dai_props->mclk_fs)
 254                mclk_fs = dai_props->mclk_fs;
 255
 256        if (mclk_fs) {
 257                mclk = params_rate(params) * mclk_fs;
 258
 259                ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk);
 260                if (ret < 0)
 261                        return ret;
 262
 263                ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk);
 264                if (ret < 0)
 265                        return ret;
 266
 267                ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
 268                                             SND_SOC_CLOCK_IN);
 269                if (ret && ret != -ENOTSUPP)
 270                        goto err;
 271
 272                ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
 273                                             SND_SOC_CLOCK_OUT);
 274                if (ret && ret != -ENOTSUPP)
 275                        goto err;
 276        }
 277        return 0;
 278err:
 279        return ret;
 280}
 281EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
 282
 283int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 284                                   struct snd_pcm_hw_params *params)
 285{
 286        struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
 287        struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
 288
 289        asoc_simple_convert_fixup(&dai_props->adata, params);
 290
 291        return 0;
 292}
 293EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
 294
 295static int asoc_simple_init_dai(struct snd_soc_dai *dai,
 296                                     struct asoc_simple_dai *simple_dai)
 297{
 298        int ret;
 299
 300        if (!simple_dai)
 301                return 0;
 302
 303        if (simple_dai->sysclk) {
 304                ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
 305                                             simple_dai->clk_direction);
 306                if (ret && ret != -ENOTSUPP) {
 307                        dev_err(dai->dev, "simple-card: set_sysclk error\n");
 308                        return ret;
 309                }
 310        }
 311
 312        if (simple_dai->slots) {
 313                ret = snd_soc_dai_set_tdm_slot(dai,
 314                                               simple_dai->tx_slot_mask,
 315                                               simple_dai->rx_slot_mask,
 316                                               simple_dai->slots,
 317                                               simple_dai->slot_width);
 318                if (ret && ret != -ENOTSUPP) {
 319                        dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
 320                        return ret;
 321                }
 322        }
 323
 324        return 0;
 325}
 326
 327int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
 328{
 329        struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
 330        struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
 331        int ret;
 332
 333        ret = asoc_simple_init_dai(rtd->codec_dai,
 334                                   dai_props->codec_dai);
 335        if (ret < 0)
 336                return ret;
 337
 338        ret = asoc_simple_init_dai(rtd->cpu_dai,
 339                                   dai_props->cpu_dai);
 340        if (ret < 0)
 341                return ret;
 342
 343        return 0;
 344}
 345EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
 346
 347void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link)
 348{
 349        /* Assumes platform == cpu */
 350        if (!dai_link->platforms->of_node)
 351                dai_link->platforms->of_node = dai_link->cpus->of_node;
 352
 353        /*
 354         * DPCM BE can be no platform.
 355         * Alloced memory will be waste, but not leak.
 356         */
 357        if (!dai_link->platforms->of_node)
 358                dai_link->num_platforms = 0;
 359}
 360EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
 361
 362void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
 363                                  int is_single_links)
 364{
 365        /*
 366         * In soc_bind_dai_link() will check cpu name after
 367         * of_node matching if dai_link has cpu_dai_name.
 368         * but, it will never match if name was created by
 369         * fmt_single_name() remove cpu_dai_name if cpu_args
 370         * was 0. See:
 371         *      fmt_single_name()
 372         *      fmt_multiple_name()
 373         */
 374        if (is_single_links)
 375                dai_link->cpus->dai_name = NULL;
 376}
 377EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
 378
 379int asoc_simple_clean_reference(struct snd_soc_card *card)
 380{
 381        struct snd_soc_dai_link *dai_link;
 382        int i;
 383
 384        for_each_card_prelinks(card, i, dai_link) {
 385                of_node_put(dai_link->cpus->of_node);
 386                of_node_put(dai_link->codecs->of_node);
 387        }
 388        return 0;
 389}
 390EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
 391
 392int asoc_simple_parse_routing(struct snd_soc_card *card,
 393                              char *prefix)
 394{
 395        struct device_node *node = card->dev->of_node;
 396        char prop[128];
 397
 398        if (!prefix)
 399                prefix = "";
 400
 401        snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
 402
 403        if (!of_property_read_bool(node, prop))
 404                return 0;
 405
 406        return snd_soc_of_parse_audio_routing(card, prop);
 407}
 408EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
 409
 410int asoc_simple_parse_widgets(struct snd_soc_card *card,
 411                              char *prefix)
 412{
 413        struct device_node *node = card->dev->of_node;
 414        char prop[128];
 415
 416        if (!prefix)
 417                prefix = "";
 418
 419        snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
 420
 421        if (of_property_read_bool(node, prop))
 422                return snd_soc_of_parse_audio_simple_widgets(card, prop);
 423
 424        /* no widgets is not error */
 425        return 0;
 426}
 427EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
 428
 429int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
 430                                   char *prefix)
 431{
 432        const unsigned int nb_controls_max = 16;
 433        const char **strings, *control_name;
 434        struct snd_kcontrol_new *controls;
 435        struct device *dev = card->dev;
 436        unsigned int i, nb_controls;
 437        char prop[128];
 438        int ret;
 439
 440        if (!prefix)
 441                prefix = "";
 442
 443        snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
 444
 445        if (!of_property_read_bool(dev->of_node, prop))
 446                return 0;
 447
 448        strings = devm_kcalloc(dev, nb_controls_max,
 449                               sizeof(*strings), GFP_KERNEL);
 450        if (!strings)
 451                return -ENOMEM;
 452
 453        ret = of_property_read_string_array(dev->of_node, prop,
 454                                            strings, nb_controls_max);
 455        if (ret < 0)
 456                return ret;
 457
 458        nb_controls = (unsigned int)ret;
 459
 460        controls = devm_kcalloc(dev, nb_controls,
 461                                sizeof(*controls), GFP_KERNEL);
 462        if (!controls)
 463                return -ENOMEM;
 464
 465        for (i = 0; i < nb_controls; i++) {
 466                control_name = devm_kasprintf(dev, GFP_KERNEL,
 467                                              "%s Switch", strings[i]);
 468                if (!control_name)
 469                        return -ENOMEM;
 470
 471                controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 472                controls[i].name = control_name;
 473                controls[i].info = snd_soc_dapm_info_pin_switch;
 474                controls[i].get = snd_soc_dapm_get_pin_switch;
 475                controls[i].put = snd_soc_dapm_put_pin_switch;
 476                controls[i].private_value = (unsigned long)strings[i];
 477        }
 478
 479        card->controls = controls;
 480        card->num_controls = nb_controls;
 481
 482        return 0;
 483}
 484EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
 485
 486int asoc_simple_init_jack(struct snd_soc_card *card,
 487                          struct asoc_simple_jack *sjack,
 488                          int is_hp, char *prefix)
 489{
 490        struct device *dev = card->dev;
 491        enum of_gpio_flags flags;
 492        char prop[128];
 493        char *pin_name;
 494        char *gpio_name;
 495        int mask;
 496        int det;
 497
 498        if (!prefix)
 499                prefix = "";
 500
 501        sjack->gpio.gpio = -ENOENT;
 502
 503        if (is_hp) {
 504                snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
 505                pin_name        = "Headphones";
 506                gpio_name       = "Headphone detection";
 507                mask            = SND_JACK_HEADPHONE;
 508        } else {
 509                snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
 510                pin_name        = "Mic Jack";
 511                gpio_name       = "Mic detection";
 512                mask            = SND_JACK_MICROPHONE;
 513        }
 514
 515        det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
 516        if (det == -EPROBE_DEFER)
 517                return -EPROBE_DEFER;
 518
 519        if (gpio_is_valid(det)) {
 520                sjack->pin.pin          = pin_name;
 521                sjack->pin.mask         = mask;
 522
 523                sjack->gpio.name        = gpio_name;
 524                sjack->gpio.report      = mask;
 525                sjack->gpio.gpio        = det;
 526                sjack->gpio.invert      = !!(flags & OF_GPIO_ACTIVE_LOW);
 527                sjack->gpio.debounce_time = 150;
 528
 529                snd_soc_card_jack_new(card, pin_name, mask,
 530                                      &sjack->jack,
 531                                      &sjack->pin, 1);
 532
 533                snd_soc_jack_add_gpios(&sjack->jack, 1,
 534                                       &sjack->gpio);
 535        }
 536
 537        return 0;
 538}
 539EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
 540
 541int asoc_simple_init_priv(struct asoc_simple_priv *priv,
 542                          struct link_info *li)
 543{
 544        struct snd_soc_card *card = simple_priv_to_card(priv);
 545        struct device *dev = simple_priv_to_dev(priv);
 546        struct snd_soc_dai_link *dai_link;
 547        struct simple_dai_props *dai_props;
 548        struct asoc_simple_dai *dais;
 549        struct snd_soc_codec_conf *cconf = NULL;
 550        int i;
 551
 552        dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
 553        dai_link  = devm_kcalloc(dev, li->link, sizeof(*dai_link),  GFP_KERNEL);
 554        dais      = devm_kcalloc(dev, li->dais, sizeof(*dais),      GFP_KERNEL);
 555        if (!dai_props || !dai_link || !dais)
 556                return -ENOMEM;
 557
 558        if (li->conf) {
 559                cconf = devm_kcalloc(dev, li->conf, sizeof(*cconf), GFP_KERNEL);
 560                if (!cconf)
 561                        return -ENOMEM;
 562        }
 563
 564        /*
 565         * Use snd_soc_dai_link_component instead of legacy style
 566         * It is codec only. but cpu/platform will be supported in the future.
 567         * see
 568         *      soc-core.c :: snd_soc_init_multicodec()
 569         *
 570         * "platform" might be removed
 571         * see
 572         *      simple-card-utils.c :: asoc_simple_canonicalize_platform()
 573         */
 574        for (i = 0; i < li->link; i++) {
 575                dai_link[i].cpus                = &dai_props[i].cpus;
 576                dai_link[i].num_cpus            = 1;
 577                dai_link[i].codecs              = &dai_props[i].codecs;
 578                dai_link[i].num_codecs          = 1;
 579                dai_link[i].platforms           = &dai_props[i].platforms;
 580                dai_link[i].num_platforms       = 1;
 581        }
 582
 583        priv->dai_props         = dai_props;
 584        priv->dai_link          = dai_link;
 585        priv->dais              = dais;
 586        priv->codec_conf        = cconf;
 587
 588        card->dai_link          = priv->dai_link;
 589        card->num_links         = li->link;
 590        card->codec_conf        = cconf;
 591        card->num_configs       = li->conf;
 592
 593        return 0;
 594}
 595EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
 596
 597/* Module information */
 598MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
 599MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
 600MODULE_LICENSE("GPL v2");
 601