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