linux/sound/soc/generic/simple-card-utils.c
<<
>>
Prefs
   1/*
   2 * simple-card-core.c
   3 *
   4 * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10#include <linux/clk.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <sound/simple_card_utils.h>
  14
  15int asoc_simple_card_parse_daifmt(struct device *dev,
  16                                  struct device_node *node,
  17                                  struct device_node *codec,
  18                                  char *prefix,
  19                                  unsigned int *retfmt)
  20{
  21        struct device_node *bitclkmaster = NULL;
  22        struct device_node *framemaster = NULL;
  23        int prefix_len = prefix ? strlen(prefix) : 0;
  24        unsigned int daifmt;
  25
  26        daifmt = snd_soc_of_parse_daifmt(node, prefix,
  27                                         &bitclkmaster, &framemaster);
  28        daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
  29
  30        if (prefix_len && !bitclkmaster && !framemaster) {
  31                /*
  32                 * No dai-link level and master setting was not found from
  33                 * sound node level, revert back to legacy DT parsing and
  34                 * take the settings from codec node.
  35                 */
  36                dev_dbg(dev, "Revert to legacy daifmt parsing\n");
  37
  38                daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
  39                        (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
  40        } else {
  41                if (codec == bitclkmaster)
  42                        daifmt |= (codec == framemaster) ?
  43                                SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
  44                else
  45                        daifmt |= (codec == framemaster) ?
  46                                SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
  47        }
  48
  49        of_node_put(bitclkmaster);
  50        of_node_put(framemaster);
  51
  52        *retfmt = daifmt;
  53
  54        return 0;
  55}
  56EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
  57
  58int asoc_simple_card_set_dailink_name(struct device *dev,
  59                                      struct snd_soc_dai_link *dai_link,
  60                                      const char *fmt, ...)
  61{
  62        va_list ap;
  63        char *name = NULL;
  64        int ret = -ENOMEM;
  65
  66        va_start(ap, fmt);
  67        name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
  68        va_end(ap);
  69
  70        if (name) {
  71                ret = 0;
  72
  73                dai_link->name          = name;
  74                dai_link->stream_name   = name;
  75        }
  76
  77        return ret;
  78}
  79EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
  80
  81int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
  82                                     char *prefix)
  83{
  84        char prop[128];
  85        int ret;
  86
  87        snprintf(prop, sizeof(prop), "%sname", prefix);
  88
  89        /* Parse the card name from DT */
  90        ret = snd_soc_of_parse_card_name(card, prop);
  91        if (ret < 0)
  92                return ret;
  93
  94        if (!card->name && card->dai_link)
  95                card->name = card->dai_link->name;
  96
  97        return 0;
  98}
  99EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
 100
 101int asoc_simple_card_parse_clk(struct device_node *node,
 102                               struct device_node *dai_of_node,
 103                               struct asoc_simple_dai *simple_dai)
 104{
 105        struct clk *clk;
 106        u32 val;
 107
 108        /*
 109         * Parse dai->sysclk come from "clocks = <&xxx>"
 110         * (if system has common clock)
 111         *  or "system-clock-frequency = <xxx>"
 112         *  or device's module clock.
 113         */
 114        clk = of_clk_get(node, 0);
 115        if (!IS_ERR(clk)) {
 116                simple_dai->sysclk = clk_get_rate(clk);
 117                simple_dai->clk = clk;
 118        } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
 119                simple_dai->sysclk = val;
 120        } else {
 121                clk = of_clk_get(dai_of_node, 0);
 122                if (!IS_ERR(clk))
 123                        simple_dai->sysclk = clk_get_rate(clk);
 124        }
 125
 126        return 0;
 127}
 128EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);
 129
 130int asoc_simple_card_parse_dai(struct device_node *node,
 131                                    struct device_node **dai_of_node,
 132                                    const char **dai_name,
 133                                    const char *list_name,
 134                                    const char *cells_name,
 135                                    int *is_single_link)
 136{
 137        struct of_phandle_args args;
 138        int ret;
 139
 140        if (!node)
 141                return 0;
 142
 143        /*
 144         * Get node via "sound-dai = <&phandle port>"
 145         * it will be used as xxx_of_node on soc_bind_dai_link()
 146         */
 147        ret = of_parse_phandle_with_args(node, list_name, cells_name, 0, &args);
 148        if (ret)
 149                return ret;
 150
 151        /* Get dai->name */
 152        if (dai_name) {
 153                ret = snd_soc_of_get_dai_name(node, dai_name);
 154                if (ret < 0)
 155                        return ret;
 156        }
 157
 158        *dai_of_node = args.np;
 159
 160        if (is_single_link)
 161                *is_single_link = !args.args_count;
 162
 163        return 0;
 164}
 165EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
 166
 167int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
 168                              struct asoc_simple_dai *simple_dai)
 169{
 170        int ret;
 171
 172        if (simple_dai->sysclk) {
 173                ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 0);
 174                if (ret && ret != -ENOTSUPP) {
 175                        dev_err(dai->dev, "simple-card: set_sysclk error\n");
 176                        return ret;
 177                }
 178        }
 179
 180        if (simple_dai->slots) {
 181                ret = snd_soc_dai_set_tdm_slot(dai,
 182                                               simple_dai->tx_slot_mask,
 183                                               simple_dai->rx_slot_mask,
 184                                               simple_dai->slots,
 185                                               simple_dai->slot_width);
 186                if (ret && ret != -ENOTSUPP) {
 187                        dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
 188                        return ret;
 189                }
 190        }
 191
 192        return 0;
 193}
 194EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai);
 195
 196int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link)
 197{
 198        if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name)
 199                return -EINVAL;
 200
 201        /* Assumes platform == cpu */
 202        if (!dai_link->platform_of_node)
 203                dai_link->platform_of_node = dai_link->cpu_of_node;
 204
 205        return 0;
 206}
 207EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink);
 208
 209void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
 210                                       int is_single_links)
 211{
 212        /*
 213         * In soc_bind_dai_link() will check cpu name after
 214         * of_node matching if dai_link has cpu_dai_name.
 215         * but, it will never match if name was created by
 216         * fmt_single_name() remove cpu_dai_name if cpu_args
 217         * was 0. See:
 218         *      fmt_single_name()
 219         *      fmt_multiple_name()
 220         */
 221        if (is_single_links)
 222                dai_link->cpu_dai_name = NULL;
 223}
 224EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu);
 225
 226int asoc_simple_card_clean_reference(struct snd_soc_card *card)
 227{
 228        struct snd_soc_dai_link *dai_link;
 229        int num_links;
 230
 231        for (num_links = 0, dai_link = card->dai_link;
 232             num_links < card->num_links;
 233             num_links++, dai_link++) {
 234                of_node_put(dai_link->cpu_of_node);
 235                of_node_put(dai_link->codec_of_node);
 236        }
 237        return 0;
 238}
 239EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
 240
 241/* Module information */
 242MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
 243MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
 244MODULE_LICENSE("GPL v2");
 245