linux/sound/soc/generic/simple-card-utils.c
<<
>>
Prefs
   1/*
   2 * simple-card-utils.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 <linux/of_graph.h>
  14#include <sound/simple_card_utils.h>
  15
  16void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
  17                                    struct snd_pcm_hw_params *params)
  18{
  19        struct snd_interval *rate = hw_param_interval(params,
  20                                                SNDRV_PCM_HW_PARAM_RATE);
  21        struct snd_interval *channels = hw_param_interval(params,
  22                                                SNDRV_PCM_HW_PARAM_CHANNELS);
  23
  24        if (data->convert_rate)
  25                rate->min =
  26                rate->max = data->convert_rate;
  27
  28        if (data->convert_channels)
  29                channels->min =
  30                channels->max = data->convert_channels;
  31}
  32EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup);
  33
  34void asoc_simple_card_parse_convert(struct device *dev, char *prefix,
  35                                    struct asoc_simple_card_data *data)
  36{
  37        struct device_node *np = dev->of_node;
  38        char prop[128];
  39
  40        if (!prefix)
  41                prefix = "";
  42
  43        /* sampling rate convert */
  44        snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
  45        of_property_read_u32(np, prop, &data->convert_rate);
  46
  47        /* channels transfer */
  48        snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
  49        of_property_read_u32(np, prop, &data->convert_channels);
  50
  51        dev_dbg(dev, "convert_rate     %d\n", data->convert_rate);
  52        dev_dbg(dev, "convert_channels %d\n", data->convert_channels);
  53}
  54EXPORT_SYMBOL_GPL(asoc_simple_card_parse_convert);
  55
  56int asoc_simple_card_parse_daifmt(struct device *dev,
  57                                  struct device_node *node,
  58                                  struct device_node *codec,
  59                                  char *prefix,
  60                                  unsigned int *retfmt)
  61{
  62        struct device_node *bitclkmaster = NULL;
  63        struct device_node *framemaster = NULL;
  64        unsigned int daifmt;
  65
  66        daifmt = snd_soc_of_parse_daifmt(node, prefix,
  67                                         &bitclkmaster, &framemaster);
  68        daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
  69
  70        if (!bitclkmaster && !framemaster) {
  71                /*
  72                 * No dai-link level and master setting was not found from
  73                 * sound node level, revert back to legacy DT parsing and
  74                 * take the settings from codec node.
  75                 */
  76                dev_dbg(dev, "Revert to legacy daifmt parsing\n");
  77
  78                daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
  79                        (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
  80        } else {
  81                if (codec == bitclkmaster)
  82                        daifmt |= (codec == framemaster) ?
  83                                SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
  84                else
  85                        daifmt |= (codec == framemaster) ?
  86                                SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
  87        }
  88
  89        of_node_put(bitclkmaster);
  90        of_node_put(framemaster);
  91
  92        *retfmt = daifmt;
  93
  94        dev_dbg(dev, "format : %04x\n", daifmt);
  95
  96        return 0;
  97}
  98EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
  99
 100int asoc_simple_card_set_dailink_name(struct device *dev,
 101                                      struct snd_soc_dai_link *dai_link,
 102                                      const char *fmt, ...)
 103{
 104        va_list ap;
 105        char *name = NULL;
 106        int ret = -ENOMEM;
 107
 108        va_start(ap, fmt);
 109        name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
 110        va_end(ap);
 111
 112        if (name) {
 113                ret = 0;
 114
 115                dai_link->name          = name;
 116                dai_link->stream_name   = name;
 117
 118                dev_dbg(dev, "name : %s\n", name);
 119        }
 120
 121        return ret;
 122}
 123EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
 124
 125int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
 126                                     char *prefix)
 127{
 128        int ret;
 129
 130        if (!prefix)
 131                prefix = "";
 132
 133        /* Parse the card name from DT */
 134        ret = snd_soc_of_parse_card_name(card, "label");
 135        if (ret < 0 || !card->name) {
 136                char prop[128];
 137
 138                snprintf(prop, sizeof(prop), "%sname", prefix);
 139                ret = snd_soc_of_parse_card_name(card, prop);
 140                if (ret < 0)
 141                        return ret;
 142        }
 143
 144        if (!card->name && card->dai_link)
 145                card->name = card->dai_link->name;
 146
 147        dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : "");
 148
 149        return 0;
 150}
 151EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
 152
 153static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai,
 154                                          struct clk *clk)
 155{
 156        dai->clk = clk;
 157}
 158
 159int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai)
 160{
 161        return clk_prepare_enable(dai->clk);
 162}
 163EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable);
 164
 165void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai)
 166{
 167        clk_disable_unprepare(dai->clk);
 168}
 169EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable);
 170
 171int asoc_simple_card_parse_clk(struct device *dev,
 172                               struct device_node *node,
 173                               struct device_node *dai_of_node,
 174                               struct asoc_simple_dai *simple_dai,
 175                               const char *name)
 176{
 177        struct clk *clk;
 178        u32 val;
 179
 180        /*
 181         * Parse dai->sysclk come from "clocks = <&xxx>"
 182         * (if system has common clock)
 183         *  or "system-clock-frequency = <xxx>"
 184         *  or device's module clock.
 185         */
 186        clk = devm_get_clk_from_child(dev, node, NULL);
 187        if (!IS_ERR(clk)) {
 188                simple_dai->sysclk = clk_get_rate(clk);
 189
 190                asoc_simple_card_clk_register(simple_dai, clk);
 191        } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
 192                simple_dai->sysclk = val;
 193        } else {
 194                clk = devm_get_clk_from_child(dev, dai_of_node, NULL);
 195                if (!IS_ERR(clk))
 196                        simple_dai->sysclk = clk_get_rate(clk);
 197        }
 198
 199        if (of_property_read_bool(node, "system-clock-direction-out"))
 200                simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
 201
 202        dev_dbg(dev, "%s : sysclk = %d, direction %d\n", name,
 203                simple_dai->sysclk, simple_dai->clk_direction);
 204
 205        return 0;
 206}
 207EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);
 208
 209int asoc_simple_card_parse_dai(struct device_node *node,
 210                                    struct device_node **dai_of_node,
 211                                    const char **dai_name,
 212                                    const char *list_name,
 213                                    const char *cells_name,
 214                                    int *is_single_link)
 215{
 216        struct of_phandle_args args;
 217        int ret;
 218
 219        if (!node)
 220                return 0;
 221
 222        /*
 223         * Get node via "sound-dai = <&phandle port>"
 224         * it will be used as xxx_of_node on soc_bind_dai_link()
 225         */
 226        ret = of_parse_phandle_with_args(node, list_name, cells_name, 0, &args);
 227        if (ret)
 228                return ret;
 229
 230        /* Get dai->name */
 231        if (dai_name) {
 232                ret = snd_soc_of_get_dai_name(node, dai_name);
 233                if (ret < 0)
 234                        return ret;
 235        }
 236
 237        *dai_of_node = args.np;
 238
 239        if (is_single_link)
 240                *is_single_link = !args.args_count;
 241
 242        return 0;
 243}
 244EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
 245
 246static int asoc_simple_card_get_dai_id(struct device_node *ep)
 247{
 248        struct device_node *node;
 249        struct device_node *endpoint;
 250        int i, id;
 251        int ret;
 252
 253        ret = snd_soc_get_dai_id(ep);
 254        if (ret != -ENOTSUPP)
 255                return ret;
 256
 257        node = of_graph_get_port_parent(ep);
 258
 259        /*
 260         * Non HDMI sound case, counting port/endpoint on its DT
 261         * is enough. Let's count it.
 262         */
 263        i = 0;
 264        id = -1;
 265        for_each_endpoint_of_node(node, endpoint) {
 266                if (endpoint == ep)
 267                        id = i;
 268                i++;
 269        }
 270
 271        of_node_put(node);
 272
 273        if (id < 0)
 274                return -ENODEV;
 275
 276        return id;
 277}
 278
 279int asoc_simple_card_parse_graph_dai(struct device_node *ep,
 280                                     struct device_node **dai_of_node,
 281                                     const char **dai_name)
 282{
 283        struct device_node *node;
 284        struct of_phandle_args args;
 285        int ret;
 286
 287        if (!ep)
 288                return 0;
 289        if (!dai_name)
 290                return 0;
 291
 292        node = of_graph_get_port_parent(ep);
 293
 294        /* Get dai->name */
 295        args.np         = node;
 296        args.args[0]    = asoc_simple_card_get_dai_id(ep);
 297        args.args_count = (of_graph_get_endpoint_count(node) > 1);
 298
 299        ret = snd_soc_get_dai_name(&args, dai_name);
 300        if (ret < 0)
 301                return ret;
 302
 303        *dai_of_node = node;
 304
 305        return 0;
 306}
 307EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
 308
 309int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
 310                              struct asoc_simple_dai *simple_dai)
 311{
 312        int ret;
 313
 314        if (simple_dai->sysclk) {
 315                ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
 316                                             simple_dai->clk_direction);
 317                if (ret && ret != -ENOTSUPP) {
 318                        dev_err(dai->dev, "simple-card: set_sysclk error\n");
 319                        return ret;
 320                }
 321        }
 322
 323        if (simple_dai->slots) {
 324                ret = snd_soc_dai_set_tdm_slot(dai,
 325                                               simple_dai->tx_slot_mask,
 326                                               simple_dai->rx_slot_mask,
 327                                               simple_dai->slots,
 328                                               simple_dai->slot_width);
 329                if (ret && ret != -ENOTSUPP) {
 330                        dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
 331                        return ret;
 332                }
 333        }
 334
 335        return 0;
 336}
 337EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai);
 338
 339int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link)
 340{
 341        /* Assumes platform == cpu */
 342        if (!dai_link->platform_of_node)
 343                dai_link->platform_of_node = dai_link->cpu_of_node;
 344
 345        return 0;
 346}
 347EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink);
 348
 349void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
 350                                       int is_single_links)
 351{
 352        /*
 353         * In soc_bind_dai_link() will check cpu name after
 354         * of_node matching if dai_link has cpu_dai_name.
 355         * but, it will never match if name was created by
 356         * fmt_single_name() remove cpu_dai_name if cpu_args
 357         * was 0. See:
 358         *      fmt_single_name()
 359         *      fmt_multiple_name()
 360         */
 361        if (is_single_links)
 362                dai_link->cpu_dai_name = NULL;
 363}
 364EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu);
 365
 366int asoc_simple_card_clean_reference(struct snd_soc_card *card)
 367{
 368        struct snd_soc_dai_link *dai_link;
 369        int num_links;
 370
 371        for (num_links = 0, dai_link = card->dai_link;
 372             num_links < card->num_links;
 373             num_links++, dai_link++) {
 374                of_node_put(dai_link->cpu_of_node);
 375                of_node_put(dai_link->codec_of_node);
 376        }
 377        return 0;
 378}
 379EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
 380
 381int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
 382                                      char *prefix,
 383                                      int optional)
 384{
 385        struct device_node *node = card->dev->of_node;
 386        char prop[128];
 387
 388        if (!prefix)
 389                prefix = "";
 390
 391        snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
 392
 393        if (!of_property_read_bool(node, prop)) {
 394                if (optional)
 395                        return 0;
 396                return -EINVAL;
 397        }
 398
 399        return snd_soc_of_parse_audio_routing(card, prop);
 400}
 401EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_routing);
 402
 403int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
 404                                      char *prefix)
 405{
 406        struct device_node *node = card->dev->of_node;
 407        char prop[128];
 408
 409        if (!prefix)
 410                prefix = "";
 411
 412        snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
 413
 414        if (of_property_read_bool(node, prop))
 415                return snd_soc_of_parse_audio_simple_widgets(card, prop);
 416
 417        /* no widgets is not error */
 418        return 0;
 419}
 420EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets);
 421
 422/* Module information */
 423MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
 424MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
 425MODULE_LICENSE("GPL v2");
 426