linux/sound/soc/generic/simple-scu-card.c
<<
>>
Prefs
   1/*
   2 * ASoC simple SCU sound card support
   3 *
   4 * Copyright (C) 2015 Renesas Solutions Corp.
   5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
   6 *
   7 * based on ${LINUX}/sound/soc/generic/simple-card.c
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13#include <linux/clk.h>
  14#include <linux/device.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/platform_device.h>
  19#include <linux/string.h>
  20#include <sound/jack.h>
  21#include <sound/soc.h>
  22#include <sound/soc-dai.h>
  23#include <sound/simple_card_utils.h>
  24
  25struct simple_card_data {
  26        struct snd_soc_card snd_card;
  27        struct snd_soc_codec_conf codec_conf;
  28        struct asoc_simple_dai *dai_props;
  29        struct snd_soc_dai_link *dai_link;
  30        struct asoc_simple_card_data adata;
  31};
  32
  33#define simple_priv_to_card(priv) (&(priv)->snd_card)
  34#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
  35#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
  36#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
  37
  38#define DAI     "sound-dai"
  39#define CELL    "#sound-dai-cells"
  40#define PREFIX  "simple-audio-card,"
  41
  42static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
  43{
  44        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  45        struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  46        struct asoc_simple_dai *dai_props =
  47                simple_priv_to_props(priv, rtd->num);
  48
  49        return asoc_simple_card_clk_enable(dai_props);
  50}
  51
  52static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
  53{
  54        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  55        struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  56        struct asoc_simple_dai *dai_props =
  57                simple_priv_to_props(priv, rtd->num);
  58
  59        asoc_simple_card_clk_disable(dai_props);
  60}
  61
  62static const struct snd_soc_ops asoc_simple_card_ops = {
  63        .startup = asoc_simple_card_startup,
  64        .shutdown = asoc_simple_card_shutdown,
  65};
  66
  67static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
  68{
  69        struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  70        struct snd_soc_dai *dai;
  71        struct snd_soc_dai_link *dai_link;
  72        struct asoc_simple_dai *dai_props;
  73        int num = rtd->num;
  74
  75        dai_link        = simple_priv_to_link(priv, num);
  76        dai_props       = simple_priv_to_props(priv, num);
  77        dai             = dai_link->dynamic ?
  78                                rtd->cpu_dai :
  79                                rtd->codec_dai;
  80
  81        return asoc_simple_card_init_dai(dai, dai_props);
  82}
  83
  84static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
  85                                        struct snd_pcm_hw_params *params)
  86{
  87        struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
  88
  89        asoc_simple_card_convert_fixup(&priv->adata, params);
  90
  91        return 0;
  92}
  93
  94static int asoc_simple_card_dai_link_of(struct device_node *np,
  95                                        struct simple_card_data *priv,
  96                                        unsigned int daifmt,
  97                                        int idx, bool is_fe)
  98{
  99        struct device *dev = simple_priv_to_dev(priv);
 100        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
 101        struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx);
 102        struct snd_soc_card *card = simple_priv_to_card(priv);
 103        int ret;
 104
 105        if (is_fe) {
 106                int is_single_links = 0;
 107
 108                /* BE is dummy */
 109                dai_link->codec_of_node         = NULL;
 110                dai_link->codec_dai_name        = "snd-soc-dummy-dai";
 111                dai_link->codec_name            = "snd-soc-dummy";
 112
 113                /* FE settings */
 114                dai_link->dynamic               = 1;
 115                dai_link->dpcm_merged_format    = 1;
 116
 117                ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
 118                                                 &is_single_links);
 119                if (ret)
 120                        return ret;
 121
 122                ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai_props);
 123                if (ret < 0)
 124                        return ret;
 125
 126                ret = asoc_simple_card_set_dailink_name(dev, dai_link,
 127                                                        "fe.%s",
 128                                                        dai_link->cpu_dai_name);
 129                if (ret < 0)
 130                        return ret;
 131
 132                asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
 133        } else {
 134                /* FE is dummy */
 135                dai_link->cpu_of_node           = NULL;
 136                dai_link->cpu_dai_name          = "snd-soc-dummy-dai";
 137                dai_link->cpu_name              = "snd-soc-dummy";
 138
 139                /* BE settings */
 140                dai_link->no_pcm                = 1;
 141                dai_link->be_hw_params_fixup    = asoc_simple_card_be_hw_params_fixup;
 142
 143                ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
 144                if (ret < 0)
 145                        return ret;
 146
 147                ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai_props);
 148                if (ret < 0)
 149                        return ret;
 150
 151                ret = asoc_simple_card_set_dailink_name(dev, dai_link,
 152                                                        "be.%s",
 153                                                        dai_link->codec_dai_name);
 154                if (ret < 0)
 155                        return ret;
 156
 157                snd_soc_of_parse_audio_prefix(card,
 158                                              &priv->codec_conf,
 159                                              dai_link->codec_of_node,
 160                                              PREFIX "prefix");
 161        }
 162
 163        ret = asoc_simple_card_of_parse_tdm(np, dai_props);
 164        if (ret)
 165                return ret;
 166
 167        ret = asoc_simple_card_canonicalize_dailink(dai_link);
 168        if (ret < 0)
 169                return ret;
 170
 171        dai_link->dai_fmt               = daifmt;
 172        dai_link->dpcm_playback         = 1;
 173        dai_link->dpcm_capture          = 1;
 174        dai_link->ops                   = &asoc_simple_card_ops;
 175        dai_link->init                  = asoc_simple_card_dai_init;
 176
 177        return 0;
 178}
 179
 180static int asoc_simple_card_parse_of(struct simple_card_data *priv)
 181
 182{
 183        struct device *dev = simple_priv_to_dev(priv);
 184        struct device_node *np;
 185        struct snd_soc_card *card = simple_priv_to_card(priv);
 186        struct device_node *node = dev->of_node;
 187        unsigned int daifmt = 0;
 188        bool is_fe;
 189        int ret, i;
 190
 191        if (!node)
 192                return -EINVAL;
 193
 194        ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
 195        if (ret < 0)
 196                return ret;
 197
 198        ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0);
 199        if (ret < 0)
 200                return ret;
 201
 202        asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata);
 203
 204        /* find 1st codec */
 205        np = of_get_child_by_name(node, PREFIX "codec");
 206        if (!np)
 207                return -ENODEV;
 208
 209        ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt);
 210        if (ret < 0)
 211                return ret;
 212
 213        i = 0;
 214        for_each_child_of_node(node, np) {
 215                is_fe = false;
 216                if (strcmp(np->name, PREFIX "cpu") == 0)
 217                        is_fe = true;
 218
 219                ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe);
 220                if (ret < 0)
 221                        return ret;
 222                i++;
 223        }
 224
 225        ret = asoc_simple_card_parse_card_name(card, PREFIX);
 226        if (ret < 0)
 227                return ret;
 228
 229        return 0;
 230}
 231
 232static int asoc_simple_card_probe(struct platform_device *pdev)
 233{
 234        struct simple_card_data *priv;
 235        struct snd_soc_dai_link *dai_link;
 236        struct asoc_simple_dai *dai_props;
 237        struct snd_soc_card *card;
 238        struct device *dev = &pdev->dev;
 239        struct device_node *np = dev->of_node;
 240        int num, ret;
 241
 242        /* Allocate the private data */
 243        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 244        if (!priv)
 245                return -ENOMEM;
 246
 247        num = of_get_child_count(np);
 248
 249        dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
 250        dai_link  = devm_kzalloc(dev, sizeof(*dai_link)  * num, GFP_KERNEL);
 251        if (!dai_props || !dai_link)
 252                return -ENOMEM;
 253
 254        priv->dai_props                         = dai_props;
 255        priv->dai_link                          = dai_link;
 256
 257        /* Init snd_soc_card */
 258        card = simple_priv_to_card(priv);
 259        card->owner             = THIS_MODULE;
 260        card->dev               = dev;
 261        card->dai_link          = priv->dai_link;
 262        card->num_links         = num;
 263        card->codec_conf        = &priv->codec_conf;
 264        card->num_configs       = 1;
 265
 266        ret = asoc_simple_card_parse_of(priv);
 267        if (ret < 0) {
 268                if (ret != -EPROBE_DEFER)
 269                        dev_err(dev, "parse error %d\n", ret);
 270                goto err;
 271        }
 272
 273        snd_soc_card_set_drvdata(card, priv);
 274
 275        ret = devm_snd_soc_register_card(dev, card);
 276        if (ret < 0)
 277                goto err;
 278
 279        return 0;
 280err:
 281        asoc_simple_card_clean_reference(card);
 282
 283        return ret;
 284}
 285
 286static int asoc_simple_card_remove(struct platform_device *pdev)
 287{
 288        struct snd_soc_card *card = platform_get_drvdata(pdev);
 289
 290        return asoc_simple_card_clean_reference(card);
 291}
 292
 293static const struct of_device_id asoc_simple_of_match[] = {
 294        { .compatible = "renesas,rsrc-card", },
 295        { .compatible = "simple-scu-audio-card", },
 296        {},
 297};
 298MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
 299
 300static struct platform_driver asoc_simple_card = {
 301        .driver = {
 302                .name = "simple-scu-audio-card",
 303                .pm = &snd_soc_pm_ops,
 304                .of_match_table = asoc_simple_of_match,
 305        },
 306        .probe = asoc_simple_card_probe,
 307        .remove = asoc_simple_card_remove,
 308};
 309
 310module_platform_driver(asoc_simple_card);
 311
 312MODULE_ALIAS("platform:asoc-simple-scu-card");
 313MODULE_LICENSE("GPL v2");
 314MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
 315MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
 316