linux/sound/soc/generic/simple-card.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// ASoC simple sound card support
   4//
   5// Copyright (C) 2012 Renesas Solutions Corp.
   6// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
   7
   8#include <linux/clk.h>
   9#include <linux/device.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/of_device.h>
  13#include <linux/platform_device.h>
  14#include <linux/string.h>
  15#include <sound/simple_card.h>
  16#include <sound/soc-dai.h>
  17#include <sound/soc.h>
  18
  19#define DPCM_SELECTABLE 1
  20
  21#define DAI     "sound-dai"
  22#define CELL    "#sound-dai-cells"
  23#define PREFIX  "simple-audio-card,"
  24
  25static const struct snd_soc_ops simple_ops = {
  26        .startup        = asoc_simple_startup,
  27        .shutdown       = asoc_simple_shutdown,
  28        .hw_params      = asoc_simple_hw_params,
  29};
  30
  31static int asoc_simple_parse_dai(struct device_node *node,
  32                                 struct snd_soc_dai_link_component *dlc,
  33                                 int *is_single_link)
  34{
  35        struct of_phandle_args args;
  36        int ret;
  37
  38        if (!node)
  39                return 0;
  40
  41        /*
  42         * Get node via "sound-dai = <&phandle port>"
  43         * it will be used as xxx_of_node on soc_bind_dai_link()
  44         */
  45        ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
  46        if (ret)
  47                return ret;
  48
  49        /* Get dai->name */
  50        ret = snd_soc_of_get_dai_name(node, &dlc->dai_name);
  51        if (ret < 0)
  52                return ret;
  53
  54        dlc->of_node = args.np;
  55
  56        if (is_single_link)
  57                *is_single_link = !args.args_count;
  58
  59        return 0;
  60}
  61
  62static void simple_parse_convert(struct device *dev,
  63                                 struct device_node *np,
  64                                 struct asoc_simple_data *adata)
  65{
  66        struct device_node *top = dev->of_node;
  67        struct device_node *node = of_get_parent(np);
  68
  69        asoc_simple_parse_convert(dev, top,  PREFIX, adata);
  70        asoc_simple_parse_convert(dev, node, PREFIX, adata);
  71        asoc_simple_parse_convert(dev, node, NULL,   adata);
  72        asoc_simple_parse_convert(dev, np,   NULL,   adata);
  73
  74        of_node_put(node);
  75}
  76
  77static void simple_parse_mclk_fs(struct device_node *top,
  78                                 struct device_node *cpu,
  79                                 struct device_node *codec,
  80                                 struct simple_dai_props *props,
  81                                 char *prefix)
  82{
  83        struct device_node *node = of_get_parent(cpu);
  84        char prop[128];
  85
  86        snprintf(prop, sizeof(prop), "%smclk-fs", PREFIX);
  87        of_property_read_u32(top,       prop, &props->mclk_fs);
  88
  89        snprintf(prop, sizeof(prop), "%smclk-fs", prefix);
  90        of_property_read_u32(node,      prop, &props->mclk_fs);
  91        of_property_read_u32(cpu,       prop, &props->mclk_fs);
  92        of_property_read_u32(codec,     prop, &props->mclk_fs);
  93
  94        of_node_put(node);
  95}
  96
  97static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
  98                                   struct device_node *np,
  99                                   struct device_node *codec,
 100                                   struct link_info *li,
 101                                   bool is_top)
 102{
 103        struct device *dev = simple_priv_to_dev(priv);
 104        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
 105        struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
 106        struct asoc_simple_dai *dai;
 107        struct snd_soc_dai_link_component *cpus = dai_link->cpus;
 108        struct snd_soc_dai_link_component *codecs = dai_link->codecs;
 109        struct device_node *top = dev->of_node;
 110        struct device_node *node = of_get_parent(np);
 111        char *prefix = "";
 112        int ret;
 113
 114        /*
 115         *       |CPU   |Codec   : turn
 116         * CPU   |Pass  |return
 117         * Codec |return|Pass
 118         * np
 119         */
 120        if (li->cpu == (np == codec))
 121                return 0;
 122
 123        dev_dbg(dev, "link_of DPCM (%pOF)\n", np);
 124
 125        li->link++;
 126
 127        /* For single DAI link & old style of DT node */
 128        if (is_top)
 129                prefix = PREFIX;
 130
 131        if (li->cpu) {
 132                int is_single_links = 0;
 133
 134                /* BE is dummy */
 135                codecs->of_node         = NULL;
 136                codecs->dai_name        = "snd-soc-dummy-dai";
 137                codecs->name            = "snd-soc-dummy";
 138
 139                /* FE settings */
 140                dai_link->dynamic               = 1;
 141                dai_link->dpcm_merged_format    = 1;
 142
 143                dai =
 144                dai_props->cpu_dai      = &priv->dais[li->dais++];
 145
 146                ret = asoc_simple_parse_cpu(np, dai_link, &is_single_links);
 147                if (ret)
 148                        goto out_put_node;
 149
 150                ret = asoc_simple_parse_clk_cpu(dev, np, dai_link, dai);
 151                if (ret < 0)
 152                        goto out_put_node;
 153
 154                ret = asoc_simple_set_dailink_name(dev, dai_link,
 155                                                   "fe.%s",
 156                                                   cpus->dai_name);
 157                if (ret < 0)
 158                        goto out_put_node;
 159
 160                asoc_simple_canonicalize_cpu(dai_link, is_single_links);
 161        } else {
 162                struct snd_soc_codec_conf *cconf;
 163
 164                /* FE is dummy */
 165                cpus->of_node           = NULL;
 166                cpus->dai_name          = "snd-soc-dummy-dai";
 167                cpus->name              = "snd-soc-dummy";
 168
 169                /* BE settings */
 170                dai_link->no_pcm                = 1;
 171                dai_link->be_hw_params_fixup    = asoc_simple_be_hw_params_fixup;
 172
 173                dai =
 174                dai_props->codec_dai    = &priv->dais[li->dais++];
 175
 176                cconf =
 177                dai_props->codec_conf   = &priv->codec_conf[li->conf++];
 178
 179                ret = asoc_simple_parse_codec(np, dai_link);
 180                if (ret < 0)
 181                        goto out_put_node;
 182
 183                ret = asoc_simple_parse_clk_codec(dev, np, dai_link, dai);
 184                if (ret < 0)
 185                        goto out_put_node;
 186
 187                ret = asoc_simple_set_dailink_name(dev, dai_link,
 188                                                   "be.%s",
 189                                                   codecs->dai_name);
 190                if (ret < 0)
 191                        goto out_put_node;
 192
 193                /* check "prefix" from top node */
 194                snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
 195                                              PREFIX "prefix");
 196                snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node,
 197                                             "prefix");
 198                snd_soc_of_parse_node_prefix(np, cconf, codecs->of_node,
 199                                             "prefix");
 200        }
 201
 202        simple_parse_convert(dev, np, &dai_props->adata);
 203        simple_parse_mclk_fs(top, np, codec, dai_props, prefix);
 204
 205        asoc_simple_canonicalize_platform(dai_link);
 206
 207        ret = asoc_simple_parse_tdm(np, dai);
 208        if (ret)
 209                goto out_put_node;
 210
 211        ret = asoc_simple_parse_daifmt(dev, node, codec,
 212                                       prefix, &dai_link->dai_fmt);
 213        if (ret < 0)
 214                goto out_put_node;
 215
 216        dai_link->dpcm_playback         = 1;
 217        dai_link->dpcm_capture          = 1;
 218        dai_link->ops                   = &simple_ops;
 219        dai_link->init                  = asoc_simple_dai_init;
 220
 221out_put_node:
 222        of_node_put(node);
 223        return ret;
 224}
 225
 226static int simple_dai_link_of(struct asoc_simple_priv *priv,
 227                              struct device_node *np,
 228                              struct device_node *codec,
 229                              struct link_info *li,
 230                              bool is_top)
 231{
 232        struct device *dev = simple_priv_to_dev(priv);
 233        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
 234        struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
 235        struct asoc_simple_dai *cpu_dai;
 236        struct asoc_simple_dai *codec_dai;
 237        struct device_node *top = dev->of_node;
 238        struct device_node *cpu = NULL;
 239        struct device_node *node = NULL;
 240        struct device_node *plat = NULL;
 241        char prop[128];
 242        char *prefix = "";
 243        int ret, single_cpu;
 244
 245        /*
 246         *       |CPU   |Codec   : turn
 247         * CPU   |Pass  |return
 248         * Codec |return|return
 249         * np
 250         */
 251        if (!li->cpu || np == codec)
 252                return 0;
 253
 254        cpu  = np;
 255        node = of_get_parent(np);
 256        li->link++;
 257
 258        dev_dbg(dev, "link_of (%pOF)\n", node);
 259
 260        /* For single DAI link & old style of DT node */
 261        if (is_top)
 262                prefix = PREFIX;
 263
 264        snprintf(prop, sizeof(prop), "%splat", prefix);
 265        plat = of_get_child_by_name(node, prop);
 266
 267        cpu_dai                 =
 268        dai_props->cpu_dai      = &priv->dais[li->dais++];
 269        codec_dai               =
 270        dai_props->codec_dai    = &priv->dais[li->dais++];
 271
 272        ret = asoc_simple_parse_daifmt(dev, node, codec,
 273                                       prefix, &dai_link->dai_fmt);
 274        if (ret < 0)
 275                goto dai_link_of_err;
 276
 277        simple_parse_mclk_fs(top, cpu, codec, dai_props, prefix);
 278
 279        ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu);
 280        if (ret < 0)
 281                goto dai_link_of_err;
 282
 283        ret = asoc_simple_parse_codec(codec, dai_link);
 284        if (ret < 0)
 285                goto dai_link_of_err;
 286
 287        ret = asoc_simple_parse_platform(plat, dai_link);
 288        if (ret < 0)
 289                goto dai_link_of_err;
 290
 291        ret = asoc_simple_parse_tdm(cpu, cpu_dai);
 292        if (ret < 0)
 293                goto dai_link_of_err;
 294
 295        ret = asoc_simple_parse_tdm(codec, codec_dai);
 296        if (ret < 0)
 297                goto dai_link_of_err;
 298
 299        ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
 300        if (ret < 0)
 301                goto dai_link_of_err;
 302
 303        ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai);
 304        if (ret < 0)
 305                goto dai_link_of_err;
 306
 307        ret = asoc_simple_set_dailink_name(dev, dai_link,
 308                                           "%s-%s",
 309                                           dai_link->cpus->dai_name,
 310                                           dai_link->codecs->dai_name);
 311        if (ret < 0)
 312                goto dai_link_of_err;
 313
 314        dai_link->ops = &simple_ops;
 315        dai_link->init = asoc_simple_dai_init;
 316
 317        asoc_simple_canonicalize_cpu(dai_link, single_cpu);
 318        asoc_simple_canonicalize_platform(dai_link);
 319
 320dai_link_of_err:
 321        of_node_put(plat);
 322        of_node_put(node);
 323
 324        return ret;
 325}
 326
 327static int simple_for_each_link(struct asoc_simple_priv *priv,
 328                        struct link_info *li,
 329                        int (*func_noml)(struct asoc_simple_priv *priv,
 330                                         struct device_node *np,
 331                                         struct device_node *codec,
 332                                         struct link_info *li, bool is_top),
 333                        int (*func_dpcm)(struct asoc_simple_priv *priv,
 334                                         struct device_node *np,
 335                                         struct device_node *codec,
 336                                         struct link_info *li, bool is_top))
 337{
 338        struct device *dev = simple_priv_to_dev(priv);
 339        struct device_node *top = dev->of_node;
 340        struct device_node *node;
 341        uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev);
 342        bool is_top = 0;
 343        int ret = 0;
 344
 345        /* Check if it has dai-link */
 346        node = of_get_child_by_name(top, PREFIX "dai-link");
 347        if (!node) {
 348                node = of_node_get(top);
 349                is_top = 1;
 350        }
 351
 352        /* loop for all dai-link */
 353        do {
 354                struct asoc_simple_data adata;
 355                struct device_node *codec;
 356                struct device_node *np;
 357                int num = of_get_child_count(node);
 358
 359                /* get codec */
 360                codec = of_get_child_by_name(node, is_top ?
 361                                             PREFIX "codec" : "codec");
 362                if (!codec) {
 363                        ret = -ENODEV;
 364                        goto error;
 365                }
 366
 367                /* get convert-xxx property */
 368                memset(&adata, 0, sizeof(adata));
 369                for_each_child_of_node(node, np)
 370                        simple_parse_convert(dev, np, &adata);
 371
 372                /* loop for all CPU/Codec node */
 373                for_each_child_of_node(node, np) {
 374                        /*
 375                         * It is DPCM
 376                         * if it has many CPUs,
 377                         * or has convert-xxx property
 378                         */
 379                        if (dpcm_selectable &&
 380                            (num > 2 ||
 381                             adata.convert_rate || adata.convert_channels))
 382                                ret = func_dpcm(priv, np, codec, li, is_top);
 383                        /* else normal sound */
 384                        else
 385                                ret = func_noml(priv, np, codec, li, is_top);
 386
 387                        if (ret < 0) {
 388                                of_node_put(codec);
 389                                of_node_put(np);
 390                                goto error;
 391                        }
 392                }
 393
 394                of_node_put(codec);
 395                node = of_get_next_child(top, node);
 396        } while (!is_top && node);
 397
 398 error:
 399        of_node_put(node);
 400        return ret;
 401}
 402
 403static int simple_parse_aux_devs(struct device_node *node,
 404                                 struct asoc_simple_priv *priv)
 405{
 406        struct device *dev = simple_priv_to_dev(priv);
 407        struct device_node *aux_node;
 408        struct snd_soc_card *card = simple_priv_to_card(priv);
 409        int i, n, len;
 410
 411        if (!of_find_property(node, PREFIX "aux-devs", &len))
 412                return 0;               /* Ok to have no aux-devs */
 413
 414        n = len / sizeof(__be32);
 415        if (n <= 0)
 416                return -EINVAL;
 417
 418        card->aux_dev = devm_kcalloc(dev,
 419                        n, sizeof(*card->aux_dev), GFP_KERNEL);
 420        if (!card->aux_dev)
 421                return -ENOMEM;
 422
 423        for (i = 0; i < n; i++) {
 424                aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
 425                if (!aux_node)
 426                        return -EINVAL;
 427                card->aux_dev[i].codec_of_node = aux_node;
 428        }
 429
 430        card->num_aux_devs = n;
 431        return 0;
 432}
 433
 434static int simple_parse_of(struct asoc_simple_priv *priv)
 435{
 436        struct device *dev = simple_priv_to_dev(priv);
 437        struct device_node *top = dev->of_node;
 438        struct snd_soc_card *card = simple_priv_to_card(priv);
 439        struct link_info li;
 440        int ret;
 441
 442        if (!top)
 443                return -EINVAL;
 444
 445        ret = asoc_simple_parse_widgets(card, PREFIX);
 446        if (ret < 0)
 447                return ret;
 448
 449        ret = asoc_simple_parse_routing(card, PREFIX);
 450        if (ret < 0)
 451                return ret;
 452
 453        ret = asoc_simple_parse_pin_switches(card, PREFIX);
 454        if (ret < 0)
 455                return ret;
 456
 457        /* Single/Muti DAI link(s) & New style of DT node */
 458        memset(&li, 0, sizeof(li));
 459        for (li.cpu = 1; li.cpu >= 0; li.cpu--) {
 460                /*
 461                 * Detect all CPU first, and Detect all Codec 2nd.
 462                 *
 463                 * In Normal sound case, all DAIs are detected
 464                 * as "CPU-Codec".
 465                 *
 466                 * In DPCM sound case,
 467                 * all CPUs   are detected as "CPU-dummy", and
 468                 * all Codecs are detected as "dummy-Codec".
 469                 * To avoid random sub-device numbering,
 470                 * detect "dummy-Codec" in last;
 471                 */
 472                ret = simple_for_each_link(priv, &li,
 473                                           simple_dai_link_of,
 474                                           simple_dai_link_of_dpcm);
 475                if (ret < 0)
 476                        return ret;
 477        }
 478
 479        ret = asoc_simple_parse_card_name(card, PREFIX);
 480        if (ret < 0)
 481                return ret;
 482
 483        ret = simple_parse_aux_devs(top, priv);
 484
 485        return ret;
 486}
 487
 488static int simple_count_noml(struct asoc_simple_priv *priv,
 489                             struct device_node *np,
 490                             struct device_node *codec,
 491                             struct link_info *li, bool is_top)
 492{
 493        li->dais++; /* CPU or Codec */
 494        if (np != codec)
 495                li->link++; /* CPU-Codec */
 496
 497        return 0;
 498}
 499
 500static int simple_count_dpcm(struct asoc_simple_priv *priv,
 501                             struct device_node *np,
 502                             struct device_node *codec,
 503                             struct link_info *li, bool is_top)
 504{
 505        li->dais++; /* CPU or Codec */
 506        li->link++; /* CPU-dummy or dummy-Codec */
 507        if (np == codec)
 508                li->conf++;
 509
 510        return 0;
 511}
 512
 513static void simple_get_dais_count(struct asoc_simple_priv *priv,
 514                                  struct link_info *li)
 515{
 516        struct device *dev = simple_priv_to_dev(priv);
 517        struct device_node *top = dev->of_node;
 518
 519        /*
 520         * link_num :   number of links.
 521         *              CPU-Codec / CPU-dummy / dummy-Codec
 522         * dais_num :   number of DAIs
 523         * ccnf_num :   number of codec_conf
 524         *              same number for "dummy-Codec"
 525         *
 526         * ex1)
 527         * CPU0 --- Codec0      link : 5
 528         * CPU1 --- Codec1      dais : 7
 529         * CPU2 -/              ccnf : 1
 530         * CPU3 --- Codec2
 531         *
 532         *      => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
 533         *      => 7 DAIs  = 4xCPU + 3xCodec
 534         *      => 1 ccnf  = 1xdummy-Codec
 535         *
 536         * ex2)
 537         * CPU0 --- Codec0      link : 5
 538         * CPU1 --- Codec1      dais : 6
 539         * CPU2 -/              ccnf : 1
 540         * CPU3 -/
 541         *
 542         *      => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
 543         *      => 6 DAIs  = 4xCPU + 2xCodec
 544         *      => 1 ccnf  = 1xdummy-Codec
 545         *
 546         * ex3)
 547         * CPU0 --- Codec0      link : 6
 548         * CPU1 -/              dais : 6
 549         * CPU2 --- Codec1      ccnf : 2
 550         * CPU3 -/
 551         *
 552         *      => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
 553         *      => 6 DAIs  = 4xCPU + 2xCodec
 554         *      => 2 ccnf  = 2xdummy-Codec
 555         *
 556         * ex4)
 557         * CPU0 --- Codec0 (convert-rate)       link : 3
 558         * CPU1 --- Codec1                      dais : 4
 559         *                                      ccnf : 1
 560         *
 561         *      => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
 562         *      => 4 DAIs  = 2xCPU + 2xCodec
 563         *      => 1 ccnf  = 1xdummy-Codec
 564         */
 565        if (!top) {
 566                li->link = 1;
 567                li->dais = 2;
 568                li->conf = 0;
 569                return;
 570        }
 571
 572        simple_for_each_link(priv, li,
 573                             simple_count_noml,
 574                             simple_count_dpcm);
 575
 576        dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
 577                li->link, li->dais, li->conf);
 578}
 579
 580static int simple_soc_probe(struct snd_soc_card *card)
 581{
 582        struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
 583        int ret;
 584
 585        ret = asoc_simple_init_hp(card, &priv->hp_jack, PREFIX);
 586        if (ret < 0)
 587                return ret;
 588
 589        ret = asoc_simple_init_mic(card, &priv->mic_jack, PREFIX);
 590        if (ret < 0)
 591                return ret;
 592
 593        return 0;
 594}
 595
 596static int asoc_simple_probe(struct platform_device *pdev)
 597{
 598        struct asoc_simple_priv *priv;
 599        struct device *dev = &pdev->dev;
 600        struct device_node *np = dev->of_node;
 601        struct snd_soc_card *card;
 602        struct link_info li;
 603        int ret;
 604
 605        /* Allocate the private data and the DAI link array */
 606        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 607        if (!priv)
 608                return -ENOMEM;
 609
 610        card = simple_priv_to_card(priv);
 611        card->owner             = THIS_MODULE;
 612        card->dev               = dev;
 613        card->probe             = simple_soc_probe;
 614
 615        memset(&li, 0, sizeof(li));
 616        simple_get_dais_count(priv, &li);
 617        if (!li.link || !li.dais)
 618                return -EINVAL;
 619
 620        ret = asoc_simple_init_priv(priv, &li);
 621        if (ret < 0)
 622                return ret;
 623
 624        if (np && of_device_is_available(np)) {
 625
 626                ret = simple_parse_of(priv);
 627                if (ret < 0) {
 628                        if (ret != -EPROBE_DEFER)
 629                                dev_err(dev, "parse error %d\n", ret);
 630                        goto err;
 631                }
 632
 633        } else {
 634                struct asoc_simple_card_info *cinfo;
 635                struct snd_soc_dai_link_component *cpus;
 636                struct snd_soc_dai_link_component *codecs;
 637                struct snd_soc_dai_link_component *platform;
 638                struct snd_soc_dai_link *dai_link = priv->dai_link;
 639                struct simple_dai_props *dai_props = priv->dai_props;
 640
 641                int dai_idx = 0;
 642
 643                cinfo = dev->platform_data;
 644                if (!cinfo) {
 645                        dev_err(dev, "no info for asoc-simple-card\n");
 646                        return -EINVAL;
 647                }
 648
 649                if (!cinfo->name ||
 650                    !cinfo->codec_dai.name ||
 651                    !cinfo->codec ||
 652                    !cinfo->platform ||
 653                    !cinfo->cpu_dai.name) {
 654                        dev_err(dev, "insufficient asoc_simple_card_info settings\n");
 655                        return -EINVAL;
 656                }
 657
 658                dai_props->cpu_dai      = &priv->dais[dai_idx++];
 659                dai_props->codec_dai    = &priv->dais[dai_idx++];
 660
 661                cpus                    = dai_link->cpus;
 662                cpus->dai_name          = cinfo->cpu_dai.name;
 663
 664                codecs                  = dai_link->codecs;
 665                codecs->name            = cinfo->codec;
 666                codecs->dai_name        = cinfo->codec_dai.name;
 667
 668                platform                = dai_link->platforms;
 669                platform->name          = cinfo->platform;
 670
 671                card->name              = (cinfo->card) ? cinfo->card : cinfo->name;
 672                dai_link->name          = cinfo->name;
 673                dai_link->stream_name   = cinfo->name;
 674                dai_link->dai_fmt       = cinfo->daifmt;
 675                dai_link->init          = asoc_simple_dai_init;
 676                memcpy(dai_props->cpu_dai, &cinfo->cpu_dai,
 677                                        sizeof(*dai_props->cpu_dai));
 678                memcpy(dai_props->codec_dai, &cinfo->codec_dai,
 679                                        sizeof(*dai_props->codec_dai));
 680        }
 681
 682        snd_soc_card_set_drvdata(card, priv);
 683
 684        asoc_simple_debug_info(priv);
 685
 686        ret = devm_snd_soc_register_card(dev, card);
 687        if (ret < 0)
 688                goto err;
 689
 690        return 0;
 691err:
 692        asoc_simple_clean_reference(card);
 693
 694        return ret;
 695}
 696
 697static int asoc_simple_remove(struct platform_device *pdev)
 698{
 699        struct snd_soc_card *card = platform_get_drvdata(pdev);
 700
 701        return asoc_simple_clean_reference(card);
 702}
 703
 704static const struct of_device_id simple_of_match[] = {
 705        { .compatible = "simple-audio-card", },
 706        { .compatible = "simple-scu-audio-card",
 707          .data = (void *)DPCM_SELECTABLE },
 708        {},
 709};
 710MODULE_DEVICE_TABLE(of, simple_of_match);
 711
 712static struct platform_driver asoc_simple_card = {
 713        .driver = {
 714                .name = "asoc-simple-card",
 715                .pm = &snd_soc_pm_ops,
 716                .of_match_table = simple_of_match,
 717        },
 718        .probe = asoc_simple_probe,
 719        .remove = asoc_simple_remove,
 720};
 721
 722module_platform_driver(asoc_simple_card);
 723
 724MODULE_ALIAS("platform:asoc-simple-card");
 725MODULE_LICENSE("GPL v2");
 726MODULE_DESCRIPTION("ASoC Simple Sound Card");
 727MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
 728