linux/sound/soc/meson/meson-card-utils.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2020 BayLibre, SAS.
   4// Author: Jerome Brunet <jbrunet@baylibre.com>
   5
   6#include <linux/module.h>
   7#include <linux/of_platform.h>
   8#include <sound/soc.h>
   9
  10#include "meson-card.h"
  11
  12int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
  13                              struct snd_pcm_hw_params *params,
  14                              unsigned int mclk_fs)
  15{
  16        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  17        struct snd_soc_dai *codec_dai;
  18        unsigned int mclk;
  19        int ret, i;
  20
  21        if (!mclk_fs)
  22                return 0;
  23
  24        mclk = params_rate(params) * mclk_fs;
  25
  26        for_each_rtd_codec_dais(rtd, i, codec_dai) {
  27                ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
  28                                             SND_SOC_CLOCK_IN);
  29                if (ret && ret != -ENOTSUPP)
  30                        return ret;
  31        }
  32
  33        ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk,
  34                                     SND_SOC_CLOCK_OUT);
  35        if (ret && ret != -ENOTSUPP)
  36                return ret;
  37
  38        return 0;
  39}
  40EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk);
  41
  42int meson_card_reallocate_links(struct snd_soc_card *card,
  43                                unsigned int num_links)
  44{
  45        struct meson_card *priv = snd_soc_card_get_drvdata(card);
  46        struct snd_soc_dai_link *links;
  47        void **ldata;
  48
  49        links = krealloc(priv->card.dai_link,
  50                         num_links * sizeof(*priv->card.dai_link),
  51                         GFP_KERNEL | __GFP_ZERO);
  52        if (!links)
  53                goto err_links;
  54
  55        ldata = krealloc(priv->link_data,
  56                         num_links * sizeof(*priv->link_data),
  57                         GFP_KERNEL | __GFP_ZERO);
  58        if (!ldata)
  59                goto err_ldata;
  60
  61        priv->card.dai_link = links;
  62        priv->link_data = ldata;
  63        priv->card.num_links = num_links;
  64        return 0;
  65
  66err_ldata:
  67        kfree(links);
  68err_links:
  69        dev_err(priv->card.dev, "failed to allocate links\n");
  70        return -ENOMEM;
  71
  72}
  73EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
  74
  75int meson_card_parse_dai(struct snd_soc_card *card,
  76                         struct device_node *node,
  77                         struct device_node **dai_of_node,
  78                         const char **dai_name)
  79{
  80        struct of_phandle_args args;
  81        int ret;
  82
  83        if (!dai_name || !dai_of_node || !node)
  84                return -EINVAL;
  85
  86        ret = of_parse_phandle_with_args(node, "sound-dai",
  87                                         "#sound-dai-cells", 0, &args);
  88        if (ret) {
  89                if (ret != -EPROBE_DEFER)
  90                        dev_err(card->dev, "can't parse dai %d\n", ret);
  91                return ret;
  92        }
  93        *dai_of_node = args.np;
  94
  95        return snd_soc_get_dai_name(&args, dai_name);
  96}
  97EXPORT_SYMBOL_GPL(meson_card_parse_dai);
  98
  99static int meson_card_set_link_name(struct snd_soc_card *card,
 100                                    struct snd_soc_dai_link *link,
 101                                    struct device_node *node,
 102                                    const char *prefix)
 103{
 104        char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
 105                                    prefix, node->full_name);
 106        if (!name)
 107                return -ENOMEM;
 108
 109        link->name = name;
 110        link->stream_name = name;
 111
 112        return 0;
 113}
 114
 115unsigned int meson_card_parse_daifmt(struct device_node *node,
 116                                     struct device_node *cpu_node)
 117{
 118        struct device_node *bitclkmaster = NULL;
 119        struct device_node *framemaster = NULL;
 120        unsigned int daifmt;
 121
 122        daifmt = snd_soc_of_parse_daifmt(node, "",
 123                                         &bitclkmaster, &framemaster);
 124        daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
 125
 126        /* If no master is provided, default to cpu master */
 127        if (!bitclkmaster || bitclkmaster == cpu_node) {
 128                daifmt |= (!framemaster || framemaster == cpu_node) ?
 129                        SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM;
 130        } else {
 131                daifmt |= (!framemaster || framemaster == cpu_node) ?
 132                        SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM;
 133        }
 134
 135        of_node_put(bitclkmaster);
 136        of_node_put(framemaster);
 137
 138        return daifmt;
 139}
 140EXPORT_SYMBOL_GPL(meson_card_parse_daifmt);
 141
 142int meson_card_set_be_link(struct snd_soc_card *card,
 143                           struct snd_soc_dai_link *link,
 144                           struct device_node *node)
 145{
 146        struct snd_soc_dai_link_component *codec;
 147        struct device_node *np;
 148        int ret, num_codecs;
 149
 150        num_codecs = of_get_child_count(node);
 151        if (!num_codecs) {
 152                dev_err(card->dev, "be link %s has no codec\n",
 153                        node->full_name);
 154                return -EINVAL;
 155        }
 156
 157        codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL);
 158        if (!codec)
 159                return -ENOMEM;
 160
 161        link->codecs = codec;
 162        link->num_codecs = num_codecs;
 163
 164        for_each_child_of_node(node, np) {
 165                ret = meson_card_parse_dai(card, np, &codec->of_node,
 166                                           &codec->dai_name);
 167                if (ret) {
 168                        of_node_put(np);
 169                        return ret;
 170                }
 171
 172                codec++;
 173        }
 174
 175        ret = meson_card_set_link_name(card, link, node, "be");
 176        if (ret)
 177                dev_err(card->dev, "error setting %pOFn link name\n", np);
 178
 179        return ret;
 180}
 181EXPORT_SYMBOL_GPL(meson_card_set_be_link);
 182
 183int meson_card_set_fe_link(struct snd_soc_card *card,
 184                           struct snd_soc_dai_link *link,
 185                           struct device_node *node,
 186                           bool is_playback)
 187{
 188        struct snd_soc_dai_link_component *codec;
 189
 190        codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL);
 191        if (!codec)
 192                return -ENOMEM;
 193
 194        link->codecs = codec;
 195        link->num_codecs = 1;
 196
 197        link->dynamic = 1;
 198        link->dpcm_merged_format = 1;
 199        link->dpcm_merged_chan = 1;
 200        link->dpcm_merged_rate = 1;
 201        link->codecs->dai_name = "snd-soc-dummy-dai";
 202        link->codecs->name = "snd-soc-dummy";
 203
 204        if (is_playback)
 205                link->dpcm_playback = 1;
 206        else
 207                link->dpcm_capture = 1;
 208
 209        return meson_card_set_link_name(card, link, node, "fe");
 210}
 211EXPORT_SYMBOL_GPL(meson_card_set_fe_link);
 212
 213static int meson_card_add_links(struct snd_soc_card *card)
 214{
 215        struct meson_card *priv = snd_soc_card_get_drvdata(card);
 216        struct device_node *node = card->dev->of_node;
 217        struct device_node *np;
 218        int num, i, ret;
 219
 220        num = of_get_child_count(node);
 221        if (!num) {
 222                dev_err(card->dev, "card has no links\n");
 223                return -EINVAL;
 224        }
 225
 226        ret = meson_card_reallocate_links(card, num);
 227        if (ret)
 228                return ret;
 229
 230        i = 0;
 231        for_each_child_of_node(node, np) {
 232                ret = priv->match_data->add_link(card, np, &i);
 233                if (ret) {
 234                        of_node_put(np);
 235                        return ret;
 236                }
 237
 238                i++;
 239        }
 240
 241        return 0;
 242}
 243
 244static int meson_card_parse_of_optional(struct snd_soc_card *card,
 245                                        const char *propname,
 246                                        int (*func)(struct snd_soc_card *c,
 247                                                    const char *p))
 248{
 249        /* If property is not provided, don't fail ... */
 250        if (!of_property_read_bool(card->dev->of_node, propname))
 251                return 0;
 252
 253        /* ... but do fail if it is provided and the parsing fails */
 254        return func(card, propname);
 255}
 256
 257static int meson_card_add_aux_devices(struct snd_soc_card *card)
 258{
 259        struct device_node *node = card->dev->of_node;
 260        struct snd_soc_aux_dev *aux;
 261        int num, i;
 262
 263        num = of_count_phandle_with_args(node, "audio-aux-devs", NULL);
 264        if (num == -ENOENT) {
 265                return 0;
 266        } else if (num < 0) {
 267                dev_err(card->dev, "error getting auxiliary devices: %d\n",
 268                        num);
 269                return num;
 270        }
 271
 272        aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL);
 273        if (!aux)
 274                return -ENOMEM;
 275        card->aux_dev = aux;
 276        card->num_aux_devs = num;
 277
 278        for_each_card_pre_auxs(card, i, aux) {
 279                aux->dlc.of_node =
 280                        of_parse_phandle(node, "audio-aux-devs", i);
 281                if (!aux->dlc.of_node)
 282                        return -EINVAL;
 283        }
 284
 285        return 0;
 286}
 287
 288static void meson_card_clean_references(struct meson_card *priv)
 289{
 290        struct snd_soc_card *card = &priv->card;
 291        struct snd_soc_dai_link *link;
 292        struct snd_soc_dai_link_component *codec;
 293        struct snd_soc_aux_dev *aux;
 294        int i, j;
 295
 296        if (card->dai_link) {
 297                for_each_card_prelinks(card, i, link) {
 298                        if (link->cpus)
 299                                of_node_put(link->cpus->of_node);
 300                        for_each_link_codecs(link, j, codec)
 301                                of_node_put(codec->of_node);
 302                }
 303        }
 304
 305        if (card->aux_dev) {
 306                for_each_card_pre_auxs(card, i, aux)
 307                        of_node_put(aux->dlc.of_node);
 308        }
 309
 310        kfree(card->dai_link);
 311        kfree(priv->link_data);
 312}
 313
 314int meson_card_probe(struct platform_device *pdev)
 315{
 316        const struct meson_card_match_data *data;
 317        struct device *dev = &pdev->dev;
 318        struct meson_card *priv;
 319        int ret;
 320
 321        data = of_device_get_match_data(dev);
 322        if (!data) {
 323                dev_err(dev, "failed to match device\n");
 324                return -ENODEV;
 325        }
 326
 327        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 328        if (!priv)
 329                return -ENOMEM;
 330
 331        platform_set_drvdata(pdev, priv);
 332        snd_soc_card_set_drvdata(&priv->card, priv);
 333
 334        priv->card.owner = THIS_MODULE;
 335        priv->card.dev = dev;
 336        priv->match_data = data;
 337
 338        ret = snd_soc_of_parse_card_name(&priv->card, "model");
 339        if (ret < 0)
 340                return ret;
 341
 342        ret = meson_card_parse_of_optional(&priv->card, "audio-routing",
 343                                           snd_soc_of_parse_audio_routing);
 344        if (ret) {
 345                dev_err(dev, "error while parsing routing\n");
 346                return ret;
 347        }
 348
 349        ret = meson_card_parse_of_optional(&priv->card, "audio-widgets",
 350                                           snd_soc_of_parse_audio_simple_widgets);
 351        if (ret) {
 352                dev_err(dev, "error while parsing widgets\n");
 353                return ret;
 354        }
 355
 356        ret = meson_card_add_links(&priv->card);
 357        if (ret)
 358                goto out_err;
 359
 360        ret = meson_card_add_aux_devices(&priv->card);
 361        if (ret)
 362                goto out_err;
 363
 364        ret = devm_snd_soc_register_card(dev, &priv->card);
 365        if (ret)
 366                goto out_err;
 367
 368        return 0;
 369
 370out_err:
 371        meson_card_clean_references(priv);
 372        return ret;
 373}
 374EXPORT_SYMBOL_GPL(meson_card_probe);
 375
 376int meson_card_remove(struct platform_device *pdev)
 377{
 378        struct meson_card *priv = platform_get_drvdata(pdev);
 379
 380        meson_card_clean_references(priv);
 381
 382        return 0;
 383}
 384EXPORT_SYMBOL_GPL(meson_card_remove);
 385
 386MODULE_DESCRIPTION("Amlogic Sound Card Utils");
 387MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 388MODULE_LICENSE("GPL v2");
 389