linux/sound/soc/generic/audio-graph-card.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// ASoC audio graph sound card support
   4//
   5// Copyright (C) 2016 Renesas Solutions Corp.
   6// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
   7//
   8// based on ${LINUX}/sound/soc/generic/simple-card.c
   9
  10#include <linux/cleanup.h>
  11#include <linux/clk.h>
  12#include <linux/device.h>
  13#include <linux/gpio/consumer.h>
  14#include <linux/module.h>
  15#include <linux/of.h>
  16#include <linux/of_graph.h>
  17#include <linux/platform_device.h>
  18#include <linux/string.h>
  19#include <sound/graph_card.h>
  20
  21#define DPCM_SELECTABLE 1
  22
  23#define graph_ret(priv, ret) _graph_ret(priv, __func__, ret)
  24static inline int _graph_ret(struct simple_util_priv *priv,
  25                             const char *func, int ret)
  26{
  27        return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
  28}
  29
  30#define ep_to_port(ep)  of_get_parent(ep)
  31static struct device_node *port_to_ports(struct device_node *port)
  32{
  33        struct device_node *ports = of_get_parent(port);
  34
  35        if (!of_node_name_eq(ports, "ports")) {
  36                of_node_put(ports);
  37                return NULL;
  38        }
  39        return ports;
  40}
  41
  42static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
  43                              struct snd_kcontrol *kcontrol,
  44                              int event)
  45{
  46        struct snd_soc_dapm_context *dapm = w->dapm;
  47        struct simple_util_priv *priv = snd_soc_card_get_drvdata(dapm->card);
  48
  49        switch (event) {
  50        case SND_SOC_DAPM_POST_PMU:
  51                gpiod_set_value_cansleep(priv->pa_gpio, 1);
  52                break;
  53        case SND_SOC_DAPM_PRE_PMD:
  54                gpiod_set_value_cansleep(priv->pa_gpio, 0);
  55                break;
  56        default:
  57                return -EINVAL;
  58        }
  59
  60        return 0;
  61}
  62
  63static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
  64        SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
  65                               0, 0, NULL, 0, graph_outdrv_event,
  66                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
  67};
  68
  69static const struct snd_soc_ops graph_ops = {
  70        .startup        = simple_util_startup,
  71        .shutdown       = simple_util_shutdown,
  72        .hw_params      = simple_util_hw_params,
  73};
  74
  75static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
  76{
  77        struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
  78
  79        if (dai && (dai->component->driver->pcm_construct ||
  80                    (dai->driver->ops && dai->driver->ops->pcm_new)))
  81                return true;
  82
  83        return false;
  84}
  85
  86static void graph_parse_convert(struct device *dev,
  87                                struct device_node *ep,
  88                                struct simple_util_data *adata)
  89{
  90        struct device_node *top = dev->of_node;
  91        struct device_node *port  __free(device_node) = ep_to_port(ep);
  92        struct device_node *ports __free(device_node) = port_to_ports(port);
  93        struct device_node *node  __free(device_node) = of_graph_get_port_parent(ep);
  94
  95        simple_util_parse_convert(top,   NULL,   adata);
  96        simple_util_parse_convert(ports, NULL,   adata);
  97        simple_util_parse_convert(port,  NULL,   adata);
  98        simple_util_parse_convert(ep,    NULL,   adata);
  99}
 100
 101static int graph_parse_node(struct simple_util_priv *priv,
 102                            struct device_node *ep,
 103                            struct link_info *li,
 104                            int *cpu)
 105{
 106        struct device *dev = simple_priv_to_dev(priv);
 107        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
 108        struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
 109        struct snd_soc_dai_link_component *dlc;
 110        struct simple_util_dai *dai;
 111        int ret;
 112
 113        if (cpu) {
 114                dlc = snd_soc_link_to_cpu(dai_link, 0);
 115                dai = simple_props_to_dai_cpu(dai_props, 0);
 116        } else {
 117                dlc = snd_soc_link_to_codec(dai_link, 0);
 118                dai = simple_props_to_dai_codec(dai_props, 0);
 119        }
 120
 121        ret = graph_util_parse_dai(priv, ep, dlc, cpu);
 122        if (ret < 0)
 123                goto end;
 124
 125        ret = simple_util_parse_tdm(ep, dai);
 126        if (ret < 0)
 127                goto end;
 128
 129        ret = simple_util_parse_clk(dev, ep, dai, dlc);
 130end:
 131        return graph_ret(priv, ret);
 132}
 133
 134static int graph_link_init(struct simple_util_priv *priv,
 135                           struct device_node *ep_cpu,
 136                           struct device_node *ep_codec,
 137                           struct link_info *li,
 138                           char *name)
 139{
 140        struct device *dev = simple_priv_to_dev(priv);
 141        struct device_node *top = dev->of_node;
 142        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
 143        struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
 144        struct device_node *port_cpu    __free(device_node) = ep_to_port(ep_cpu);
 145        struct device_node *port_codec  __free(device_node) = ep_to_port(ep_codec);
 146        struct device_node *ports_cpu   __free(device_node) = port_to_ports(port_cpu);
 147        struct device_node *ports_codec __free(device_node) = port_to_ports(port_codec);
 148        enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT;
 149        enum snd_soc_trigger_order trigger_stop  = SND_SOC_TRIGGER_ORDER_DEFAULT;
 150        bool playback_only = 0, capture_only = 0;
 151        int ret;
 152
 153        ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec,
 154                                       NULL, &dai_link->dai_fmt);
 155        if (ret < 0)
 156                goto end;
 157
 158        graph_util_parse_link_direction(top,            &playback_only, &capture_only);
 159        graph_util_parse_link_direction(port_cpu,       &playback_only, &capture_only);
 160        graph_util_parse_link_direction(port_codec,     &playback_only, &capture_only);
 161        graph_util_parse_link_direction(ep_cpu,         &playback_only, &capture_only);
 162        graph_util_parse_link_direction(ep_codec,       &playback_only, &capture_only);
 163
 164        of_property_read_u32(top,               "mclk-fs", &dai_props->mclk_fs);
 165        of_property_read_u32(ports_cpu,         "mclk-fs", &dai_props->mclk_fs);
 166        of_property_read_u32(ports_codec,       "mclk-fs", &dai_props->mclk_fs);
 167        of_property_read_u32(port_cpu,          "mclk-fs", &dai_props->mclk_fs);
 168        of_property_read_u32(port_codec,        "mclk-fs", &dai_props->mclk_fs);
 169        of_property_read_u32(ep_cpu,            "mclk-fs", &dai_props->mclk_fs);
 170        of_property_read_u32(ep_codec,          "mclk-fs", &dai_props->mclk_fs);
 171
 172        graph_util_parse_trigger_order(priv, top,               &trigger_start, &trigger_stop);
 173        graph_util_parse_trigger_order(priv, ports_cpu,         &trigger_start, &trigger_stop);
 174        graph_util_parse_trigger_order(priv, ports_codec,       &trigger_start, &trigger_stop);
 175        graph_util_parse_trigger_order(priv, port_cpu,          &trigger_start, &trigger_stop);
 176        graph_util_parse_trigger_order(priv, port_cpu,          &trigger_start, &trigger_stop);
 177        graph_util_parse_trigger_order(priv, ep_cpu,            &trigger_start, &trigger_stop);
 178        graph_util_parse_trigger_order(priv, ep_codec,          &trigger_start, &trigger_stop);
 179
 180        dai_link->playback_only = playback_only;
 181        dai_link->capture_only  = capture_only;
 182
 183        dai_link->trigger_start = trigger_start;
 184        dai_link->trigger_stop  = trigger_stop;
 185
 186        dai_link->init          = simple_util_dai_init;
 187        dai_link->ops           = &graph_ops;
 188        if (priv->ops)
 189                dai_link->ops   = priv->ops;
 190
 191        ret = simple_util_set_dailink_name(priv, dai_link, name);
 192end:
 193        return graph_ret(priv, ret);
 194}
 195
 196static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
 197                                  struct device_node *cpu_ep,
 198                                  struct device_node *codec_ep,
 199                                  struct link_info *li)
 200{
 201        struct device *dev = simple_priv_to_dev(priv);
 202        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
 203        struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
 204        struct device_node *top = dev->of_node;
 205        struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
 206        char dai_name[64];
 207        int ret;
 208
 209        dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
 210
 211        if (li->cpu) {
 212                struct snd_soc_card *card = simple_priv_to_card(priv);
 213                struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
 214                struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
 215                int is_single_links = 0;
 216
 217                /* Codec is dummy */
 218
 219                /* FE settings */
 220                dai_link->dynamic               = 1;
 221                dai_link->dpcm_merged_format    = 1;
 222
 223                ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
 224                if (ret)
 225                        goto end;
 226
 227                snprintf(dai_name, sizeof(dai_name),
 228                         "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
 229                /*
 230                 * In BE<->BE connections it is not required to create
 231                 * PCM devices at CPU end of the dai link and thus 'no_pcm'
 232                 * flag needs to be set. It is useful when there are many
 233                 * BE components and some of these have to be connected to
 234                 * form a valid audio path.
 235                 *
 236                 * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
 237                 * there are 'n' BE components in the path.
 238                 */
 239                if (card->component_chaining && !soc_component_is_pcm(cpus)) {
 240                        dai_link->no_pcm = 1;
 241                        dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
 242                }
 243
 244                simple_util_canonicalize_cpu(cpus, is_single_links);
 245                simple_util_canonicalize_platform(platforms, cpus);
 246        } else {
 247                struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
 248                struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
 249
 250                /* CPU is dummy */
 251
 252                /* BE settings */
 253                dai_link->no_pcm                = 1;
 254                dai_link->be_hw_params_fixup    = simple_util_be_hw_params_fixup;
 255
 256                ret = graph_parse_node(priv, codec_ep, li, NULL);
 257                if (ret < 0)
 258                        goto end;
 259
 260                snprintf(dai_name, sizeof(dai_name),
 261                         "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
 262
 263                /* check "prefix" from top node */
 264                struct device_node *port  __free(device_node) = ep_to_port(ep);
 265                struct device_node *ports __free(device_node) = port_to_ports(port);
 266
 267                snd_soc_of_parse_node_prefix(top,   cconf, codecs->of_node, "prefix");
 268                snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
 269                snd_soc_of_parse_node_prefix(port,  cconf, codecs->of_node, "prefix");
 270        }
 271
 272        graph_parse_convert(dev, ep, &dai_props->adata);
 273
 274        ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
 275
 276        li->link++;
 277end:
 278        return graph_ret(priv, ret);
 279}
 280
 281static int graph_dai_link_of(struct simple_util_priv *priv,
 282                             struct device_node *cpu_ep,
 283                             struct device_node *codec_ep,
 284                             struct link_info *li)
 285{
 286        struct device *dev = simple_priv_to_dev(priv);
 287        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
 288        struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
 289        struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
 290        struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
 291        char dai_name[64];
 292        int ret, is_single_links = 0;
 293
 294        dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
 295
 296        ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
 297        if (ret < 0)
 298                goto end;
 299
 300        ret = graph_parse_node(priv, codec_ep, li, NULL);
 301        if (ret < 0)
 302                goto end;
 303
 304        snprintf(dai_name, sizeof(dai_name),
 305                 "%s-%s", cpus->dai_name, codecs->dai_name);
 306
 307        simple_util_canonicalize_cpu(cpus, is_single_links);
 308        simple_util_canonicalize_platform(platforms, cpus);
 309
 310        ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
 311        if (ret < 0)
 312                goto end;
 313
 314        li->link++;
 315end:
 316        return graph_ret(priv, ret);
 317}
 318
 319static inline bool parse_as_dpcm_link(struct simple_util_priv *priv,
 320                                      struct device_node *codec_port,
 321                                      struct simple_util_data *adata)
 322{
 323        if (priv->force_dpcm)
 324                return true;
 325
 326        if (!priv->dpcm_selectable)
 327                return false;
 328
 329        /*
 330         * It is DPCM
 331         * if Codec port has many endpoints,
 332         * or has convert-xxx property
 333         */
 334        if ((of_get_child_count(codec_port) > 1) ||
 335            simple_util_is_convert_required(adata))
 336                return true;
 337
 338        return false;
 339}
 340
 341static int __graph_for_each_link(struct simple_util_priv *priv,
 342                        struct link_info *li,
 343                        int (*func_noml)(struct simple_util_priv *priv,
 344                                         struct device_node *cpu_ep,
 345                                         struct device_node *codec_ep,
 346                                         struct link_info *li),
 347                        int (*func_dpcm)(struct simple_util_priv *priv,
 348                                         struct device_node *cpu_ep,
 349                                         struct device_node *codec_ep,
 350                                         struct link_info *li))
 351{
 352        struct of_phandle_iterator it;
 353        struct device *dev = simple_priv_to_dev(priv);
 354        struct device_node *node = dev->of_node;
 355        struct device_node *cpu_port;
 356        struct device_node *codec_port_old = NULL;
 357        struct simple_util_data adata;
 358        int rc, ret = 0;
 359
 360        /* loop for all listed CPU port */
 361        of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
 362                cpu_port = it.node;
 363
 364                /* loop for all CPU endpoint */
 365                for_each_of_graph_port_endpoint(cpu_port, cpu_ep) {
 366                        /* get codec */
 367                        struct device_node *codec_ep   __free(device_node) = of_graph_get_remote_endpoint(cpu_ep);
 368                        struct device_node *codec_port __free(device_node) = ep_to_port(codec_ep);
 369
 370                        /* get convert-xxx property */
 371                        memset(&adata, 0, sizeof(adata));
 372                        graph_parse_convert(dev, codec_ep, &adata);
 373                        graph_parse_convert(dev, cpu_ep,   &adata);
 374
 375                        /* check if link requires DPCM parsing */
 376                        if (parse_as_dpcm_link(priv, codec_port, &adata)) {
 377                                /*
 378                                 * Codec endpoint can be NULL for pluggable audio HW.
 379                                 * Platform DT can populate the Codec endpoint depending on the
 380                                 * plugged HW.
 381                                 */
 382                                /* Do it all CPU endpoint, and 1st Codec endpoint */
 383                                if (li->cpu ||
 384                                    ((codec_port_old != codec_port) && codec_ep))
 385                                        ret = func_dpcm(priv, cpu_ep, codec_ep, li);
 386                        /* else normal sound */
 387                        } else {
 388                                if (li->cpu)
 389                                        ret = func_noml(priv, cpu_ep, codec_ep, li);
 390                        }
 391
 392                        if (ret < 0)
 393                                goto end;
 394
 395                        codec_port_old = codec_port;
 396                }
 397        }
 398end:
 399        return graph_ret(priv, ret);
 400}
 401
 402static int graph_for_each_link(struct simple_util_priv *priv,
 403                               struct link_info *li,
 404                               int (*func_noml)(struct simple_util_priv *priv,
 405                                                struct device_node *cpu_ep,
 406                                                struct device_node *codec_ep,
 407                                                struct link_info *li),
 408                               int (*func_dpcm)(struct simple_util_priv *priv,
 409                                                struct device_node *cpu_ep,
 410                                                struct device_node *codec_ep,
 411                                                struct link_info *li))
 412{
 413        int ret;
 414        /*
 415         * Detect all CPU first, and Detect all Codec 2nd.
 416         *
 417         * In Normal sound case, all DAIs are detected
 418         * as "CPU-Codec".
 419         *
 420         * In DPCM sound case,
 421         * all CPUs   are detected as "CPU-dummy", and
 422         * all Codecs are detected as "dummy-Codec".
 423         * To avoid random sub-device numbering,
 424         * detect "dummy-Codec" in last;
 425         */
 426        for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
 427                ret = __graph_for_each_link(priv, li, func_noml, func_dpcm);
 428                if (ret < 0)
 429                        break;
 430        }
 431
 432        return graph_ret(priv, ret);
 433}
 434
 435static int graph_count_noml(struct simple_util_priv *priv,
 436                            struct device_node *cpu_ep,
 437                            struct device_node *codec_ep,
 438                            struct link_info *li)
 439{
 440        struct device *dev = simple_priv_to_dev(priv);
 441        int ret = -EINVAL;
 442
 443        if (li->link >= SNDRV_MAX_LINKS)
 444                goto end;
 445
 446        /*
 447         * DON'T REMOVE platforms
 448         * see
 449         *      simple-card.c :: simple_count_noml()
 450         */
 451        li->num[li->link].cpus          = 1;
 452        li->num[li->link].platforms     = 1;
 453
 454        li->num[li->link].codecs        = 1;
 455
 456        li->link += 1; /* 1xCPU-Codec */
 457
 458        dev_dbg(dev, "Count As Normal\n");
 459        ret = 0;
 460end:
 461        return graph_ret(priv, ret);
 462}
 463
 464static int graph_count_dpcm(struct simple_util_priv *priv,
 465                            struct device_node *cpu_ep,
 466                            struct device_node *codec_ep,
 467                            struct link_info *li)
 468{
 469        struct device *dev = simple_priv_to_dev(priv);
 470        int ret = -EINVAL;
 471
 472        if (li->link >= SNDRV_MAX_LINKS)
 473                goto end;
 474
 475        if (li->cpu) {
 476                /*
 477                 * DON'T REMOVE platforms
 478                 * see
 479                 *      simple-card.c :: simple_count_noml()
 480                 */
 481                li->num[li->link].cpus          = 1;
 482                li->num[li->link].platforms     = 1;
 483
 484                li->link++; /* 1xCPU-dummy */
 485        } else {
 486                li->num[li->link].codecs        = 1;
 487
 488                li->link++; /* 1xdummy-Codec */
 489        }
 490
 491        dev_dbg(dev, "Count As DPCM\n");
 492        ret = 0;
 493end:
 494        return graph_ret(priv, ret);
 495}
 496
 497static int graph_get_dais_count(struct simple_util_priv *priv,
 498                                struct link_info *li)
 499{
 500        /*
 501         * link_num :   number of links.
 502         *              CPU-Codec / CPU-dummy / dummy-Codec
 503         * dais_num :   number of DAIs
 504         * ccnf_num :   number of codec_conf
 505         *              same number for "dummy-Codec"
 506         *
 507         * ex1)
 508         * CPU0 --- Codec0      link : 5
 509         * CPU1 --- Codec1      dais : 7
 510         * CPU2 -/              ccnf : 1
 511         * CPU3 --- Codec2
 512         *
 513         *      => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
 514         *      => 7 DAIs  = 4xCPU + 3xCodec
 515         *      => 1 ccnf  = 1xdummy-Codec
 516         *
 517         * ex2)
 518         * CPU0 --- Codec0      link : 5
 519         * CPU1 --- Codec1      dais : 6
 520         * CPU2 -/              ccnf : 1
 521         * CPU3 -/
 522         *
 523         *      => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
 524         *      => 6 DAIs  = 4xCPU + 2xCodec
 525         *      => 1 ccnf  = 1xdummy-Codec
 526         *
 527         * ex3)
 528         * CPU0 --- Codec0      link : 6
 529         * CPU1 -/              dais : 6
 530         * CPU2 --- Codec1      ccnf : 2
 531         * CPU3 -/
 532         *
 533         *      => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
 534         *      => 6 DAIs  = 4xCPU + 2xCodec
 535         *      => 2 ccnf  = 2xdummy-Codec
 536         *
 537         * ex4)
 538         * CPU0 --- Codec0 (convert-rate)       link : 3
 539         * CPU1 --- Codec1                      dais : 4
 540         *                                      ccnf : 1
 541         *
 542         *      => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
 543         *      => 4 DAIs  = 2xCPU + 2xCodec
 544         *      => 1 ccnf  = 1xdummy-Codec
 545         */
 546        return graph_for_each_link(priv, li,
 547                                   graph_count_noml,
 548                                   graph_count_dpcm);
 549}
 550
 551int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev)
 552{
 553        struct snd_soc_card *card = simple_priv_to_card(priv);
 554        int ret = -ENOMEM;
 555
 556        struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
 557        if (!li)
 558                goto end;
 559
 560        card->owner = THIS_MODULE;
 561        card->dev = dev;
 562
 563        ret = graph_get_dais_count(priv, li);
 564        if (ret < 0)
 565                goto end;
 566
 567        ret = -EINVAL;
 568        if (!li->link)
 569                goto end;
 570
 571        ret = simple_util_init_priv(priv, li);
 572        if (ret < 0)
 573                goto end;
 574
 575        priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
 576        if (IS_ERR(priv->pa_gpio)) {
 577                ret = PTR_ERR(priv->pa_gpio);
 578                dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
 579                goto end;
 580        }
 581
 582        ret = simple_util_parse_widgets(card, NULL);
 583        if (ret < 0)
 584                goto end;
 585
 586        ret = simple_util_parse_routing(card, NULL);
 587        if (ret < 0)
 588                goto end;
 589
 590        memset(li, 0, sizeof(*li));
 591        ret = graph_for_each_link(priv, li,
 592                                  graph_dai_link_of,
 593                                  graph_dai_link_of_dpcm);
 594        if (ret < 0)
 595                goto err;
 596
 597        ret = simple_util_parse_card_name(priv, NULL);
 598        if (ret < 0)
 599                goto err;
 600
 601        snd_soc_card_set_drvdata(card, priv);
 602
 603        simple_util_debug_info(priv);
 604
 605        ret = devm_snd_soc_register_card(dev, card);
 606        if (ret < 0)
 607                goto err;
 608
 609        return 0;
 610err:
 611        simple_util_clean_reference(card);
 612end:
 613        return dev_err_probe(dev, ret, "parse error\n");
 614}
 615EXPORT_SYMBOL_GPL(audio_graph_parse_of);
 616
 617static int graph_probe(struct platform_device *pdev)
 618{
 619        struct simple_util_priv *priv;
 620        struct device *dev = &pdev->dev;
 621        struct snd_soc_card *card;
 622
 623        /* Allocate the private data and the DAI link array */
 624        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 625        if (!priv)
 626                return -ENOMEM;
 627
 628        card = simple_priv_to_card(priv);
 629        card->dapm_widgets      = graph_dapm_widgets;
 630        card->num_dapm_widgets  = ARRAY_SIZE(graph_dapm_widgets);
 631        card->probe             = graph_util_card_probe;
 632
 633        if (of_device_get_match_data(dev))
 634                priv->dpcm_selectable = 1;
 635
 636        return audio_graph_parse_of(priv, dev);
 637}
 638
 639static const struct of_device_id graph_of_match[] = {
 640        { .compatible = "audio-graph-card", },
 641        { .compatible = "audio-graph-scu-card",
 642          .data = (void *)DPCM_SELECTABLE },
 643        {},
 644};
 645MODULE_DEVICE_TABLE(of, graph_of_match);
 646
 647static struct platform_driver graph_card = {
 648        .driver = {
 649                .name = "asoc-audio-graph-card",
 650                .pm = &snd_soc_pm_ops,
 651                .of_match_table = graph_of_match,
 652        },
 653        .probe = graph_probe,
 654        .remove = simple_util_remove,
 655};
 656module_platform_driver(graph_card);
 657
 658MODULE_ALIAS("platform:asoc-audio-graph-card");
 659MODULE_LICENSE("GPL v2");
 660MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
 661MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
 662