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_daifmt_parse_format(node, NULL);
 123
 124        snd_soc_daifmt_parse_clock_provider_as_phandle(node, NULL, &bitclkmaster, &framemaster);
 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 void meson_card_clean_references(struct meson_card *priv)
 258{
 259        struct snd_soc_card *card = &priv->card;
 260        struct snd_soc_dai_link *link;
 261        struct snd_soc_dai_link_component *codec;
 262        struct snd_soc_aux_dev *aux;
 263        int i, j;
 264
 265        if (card->dai_link) {
 266                for_each_card_prelinks(card, i, link) {
 267                        if (link->cpus)
 268                                of_node_put(link->cpus->of_node);
 269                        for_each_link_codecs(link, j, codec)
 270                                of_node_put(codec->of_node);
 271                }
 272        }
 273
 274        if (card->aux_dev) {
 275                for_each_card_pre_auxs(card, i, aux)
 276                        of_node_put(aux->dlc.of_node);
 277        }
 278
 279        kfree(card->dai_link);
 280        kfree(priv->link_data);
 281}
 282
 283int meson_card_probe(struct platform_device *pdev)
 284{
 285        const struct meson_card_match_data *data;
 286        struct device *dev = &pdev->dev;
 287        struct meson_card *priv;
 288        int ret;
 289
 290        data = of_device_get_match_data(dev);
 291        if (!data) {
 292                dev_err(dev, "failed to match device\n");
 293                return -ENODEV;
 294        }
 295
 296        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 297        if (!priv)
 298                return -ENOMEM;
 299
 300        platform_set_drvdata(pdev, priv);
 301        snd_soc_card_set_drvdata(&priv->card, priv);
 302
 303        priv->card.owner = THIS_MODULE;
 304        priv->card.dev = dev;
 305        priv->match_data = data;
 306
 307        ret = snd_soc_of_parse_card_name(&priv->card, "model");
 308        if (ret < 0)
 309                return ret;
 310
 311        ret = meson_card_parse_of_optional(&priv->card, "audio-routing",
 312                                           snd_soc_of_parse_audio_routing);
 313        if (ret) {
 314                dev_err(dev, "error while parsing routing\n");
 315                return ret;
 316        }
 317
 318        ret = meson_card_parse_of_optional(&priv->card, "audio-widgets",
 319                                           snd_soc_of_parse_audio_simple_widgets);
 320        if (ret) {
 321                dev_err(dev, "error while parsing widgets\n");
 322                return ret;
 323        }
 324
 325        ret = meson_card_add_links(&priv->card);
 326        if (ret)
 327                goto out_err;
 328
 329        ret = snd_soc_of_parse_aux_devs(&priv->card, "audio-aux-devs");
 330        if (ret)
 331                goto out_err;
 332
 333        ret = devm_snd_soc_register_card(dev, &priv->card);
 334        if (ret)
 335                goto out_err;
 336
 337        return 0;
 338
 339out_err:
 340        meson_card_clean_references(priv);
 341        return ret;
 342}
 343EXPORT_SYMBOL_GPL(meson_card_probe);
 344
 345int meson_card_remove(struct platform_device *pdev)
 346{
 347        struct meson_card *priv = platform_get_drvdata(pdev);
 348
 349        meson_card_clean_references(priv);
 350
 351        return 0;
 352}
 353EXPORT_SYMBOL_GPL(meson_card_remove);
 354
 355MODULE_DESCRIPTION("Amlogic Sound Card Utils");
 356MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 357MODULE_LICENSE("GPL v2");
 358