linux/sound/soc/samsung/aries_wm8994.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2#include <linux/extcon.h>
   3#include <linux/iio/consumer.h>
   4#include <linux/iio/iio.h>
   5#include <linux/input-event-codes.h>
   6#include <linux/mfd/wm8994/registers.h>
   7#include <linux/module.h>
   8#include <linux/of.h>
   9#include <linux/of_device.h>
  10#include <linux/of_gpio.h>
  11#include <linux/regulator/consumer.h>
  12#include <sound/jack.h>
  13#include <sound/pcm_params.h>
  14#include <sound/soc.h>
  15
  16#include "i2s.h"
  17#include "../codecs/wm8994.h"
  18
  19#define ARIES_MCLK1_FREQ 24000000
  20
  21struct aries_wm8994_variant {
  22        unsigned int modem_dai_fmt;
  23        bool has_fm_radio;
  24};
  25
  26struct aries_wm8994_data {
  27        struct extcon_dev *usb_extcon;
  28        struct regulator *reg_main_micbias;
  29        struct regulator *reg_headset_micbias;
  30        struct gpio_desc *gpio_headset_detect;
  31        struct gpio_desc *gpio_headset_key;
  32        struct gpio_desc *gpio_earpath_sel;
  33        struct iio_channel *adc;
  34        const struct aries_wm8994_variant *variant;
  35};
  36
  37/* USB dock */
  38static struct snd_soc_jack aries_dock;
  39
  40static struct snd_soc_jack_pin dock_pins[] = {
  41        {
  42                .pin = "LINE",
  43                .mask = SND_JACK_LINEOUT,
  44        },
  45};
  46
  47static int aries_extcon_notifier(struct notifier_block *this,
  48                                 unsigned long connected, void *_cmd)
  49{
  50        if (connected)
  51                snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
  52                                SND_JACK_LINEOUT);
  53        else
  54                snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
  55
  56        return NOTIFY_DONE;
  57}
  58
  59static struct notifier_block aries_extcon_notifier_block = {
  60        .notifier_call = aries_extcon_notifier,
  61};
  62
  63/* Headset jack */
  64static struct snd_soc_jack aries_headset;
  65
  66static struct snd_soc_jack_pin jack_pins[] = {
  67        {
  68                .pin = "HP",
  69                .mask = SND_JACK_HEADPHONE,
  70        }, {
  71                .pin = "Headset Mic",
  72                .mask = SND_JACK_MICROPHONE,
  73        },
  74};
  75
  76static struct snd_soc_jack_zone headset_zones[] = {
  77        {
  78                .min_mv = 0,
  79                .max_mv = 241,
  80                .jack_type = SND_JACK_HEADPHONE,
  81        }, {
  82                .min_mv = 242,
  83                .max_mv = 2980,
  84                .jack_type = SND_JACK_HEADSET,
  85        }, {
  86                .min_mv = 2981,
  87                .max_mv = UINT_MAX,
  88                .jack_type = SND_JACK_HEADPHONE,
  89        },
  90};
  91
  92static irqreturn_t headset_det_irq_thread(int irq, void *data)
  93{
  94        struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
  95        int ret = 0;
  96        int time_left_ms = 300;
  97        int adc;
  98
  99        while (time_left_ms > 0) {
 100                if (!gpiod_get_value(priv->gpio_headset_detect)) {
 101                        snd_soc_jack_report(&aries_headset, 0,
 102                                        SND_JACK_HEADSET);
 103                        gpiod_set_value(priv->gpio_earpath_sel, 0);
 104                        return IRQ_HANDLED;
 105                }
 106                msleep(20);
 107                time_left_ms -= 20;
 108        }
 109
 110        /* Temporarily enable micbias and earpath selector */
 111        ret = regulator_enable(priv->reg_headset_micbias);
 112        if (ret)
 113                pr_err("%s failed to enable micbias: %d", __func__, ret);
 114
 115        gpiod_set_value(priv->gpio_earpath_sel, 1);
 116
 117        ret = iio_read_channel_processed(priv->adc, &adc);
 118        if (ret < 0) {
 119                /* failed to read ADC, so assume headphone */
 120                pr_err("%s failed to read ADC, assuming headphones", __func__);
 121                snd_soc_jack_report(&aries_headset, SND_JACK_HEADPHONE,
 122                                SND_JACK_HEADSET);
 123        } else {
 124                snd_soc_jack_report(&aries_headset,
 125                                snd_soc_jack_get_type(&aries_headset, adc),
 126                                SND_JACK_HEADSET);
 127        }
 128
 129        ret = regulator_disable(priv->reg_headset_micbias);
 130        if (ret)
 131                pr_err("%s failed disable micbias: %d", __func__, ret);
 132
 133        /* Disable earpath selector when no mic connected */
 134        if (!(aries_headset.status & SND_JACK_MICROPHONE))
 135                gpiod_set_value(priv->gpio_earpath_sel, 0);
 136
 137        return IRQ_HANDLED;
 138}
 139
 140static int headset_button_check(void *data)
 141{
 142        struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
 143
 144        /* Filter out keypresses when 4 pole jack not detected */
 145        if (gpiod_get_value_cansleep(priv->gpio_headset_key) &&
 146                        aries_headset.status & SND_JACK_MICROPHONE)
 147                return SND_JACK_BTN_0;
 148
 149        return 0;
 150}
 151
 152static struct snd_soc_jack_gpio headset_button_gpio[] = {
 153        {
 154                .name = "Media Button",
 155                .report = SND_JACK_BTN_0,
 156                .debounce_time  = 30,
 157                .jack_status_check = headset_button_check,
 158        },
 159};
 160
 161static int aries_spk_cfg(struct snd_soc_dapm_widget *w,
 162                        struct snd_kcontrol *kcontrol, int event)
 163{
 164        struct snd_soc_card *card = w->dapm->card;
 165        struct snd_soc_pcm_runtime *rtd;
 166        struct snd_soc_component *component;
 167        int ret = 0;
 168
 169        rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
 170        component = asoc_rtd_to_codec(rtd, 0)->component;
 171
 172        /**
 173         * We have an odd setup - the SPKMODE pin is pulled up so
 174         * we only have access to the left side SPK configs,
 175         * but SPKOUTR isn't bridged so when playing back in
 176         * stereo, we only get the left hand channel.  The only
 177         * option we're left with is to force the AIF into mono
 178         * mode.
 179         */
 180        switch (event) {
 181        case SND_SOC_DAPM_POST_PMU:
 182                ret = snd_soc_component_update_bits(component,
 183                                WM8994_AIF1_DAC1_FILTERS_1,
 184                                WM8994_AIF1DAC1_MONO, WM8994_AIF1DAC1_MONO);
 185                break;
 186        case SND_SOC_DAPM_PRE_PMD:
 187                ret = snd_soc_component_update_bits(component,
 188                                WM8994_AIF1_DAC1_FILTERS_1,
 189                                WM8994_AIF1DAC1_MONO, 0);
 190                break;
 191        }
 192
 193        return ret;
 194}
 195
 196static int aries_main_bias(struct snd_soc_dapm_widget *w,
 197                          struct snd_kcontrol *kcontrol, int event)
 198{
 199        struct snd_soc_card *card = w->dapm->card;
 200        struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
 201        int ret = 0;
 202
 203        switch (event) {
 204        case SND_SOC_DAPM_PRE_PMU:
 205                ret = regulator_enable(priv->reg_main_micbias);
 206                break;
 207        case SND_SOC_DAPM_POST_PMD:
 208                ret = regulator_disable(priv->reg_main_micbias);
 209                break;
 210        }
 211
 212        return ret;
 213}
 214
 215static int aries_headset_bias(struct snd_soc_dapm_widget *w,
 216                          struct snd_kcontrol *kcontrol, int event)
 217{
 218        struct snd_soc_card *card = w->dapm->card;
 219        struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
 220        int ret = 0;
 221
 222        switch (event) {
 223        case SND_SOC_DAPM_PRE_PMU:
 224                ret = regulator_enable(priv->reg_headset_micbias);
 225                break;
 226        case SND_SOC_DAPM_POST_PMD:
 227                ret = regulator_disable(priv->reg_headset_micbias);
 228                break;
 229        }
 230
 231        return ret;
 232}
 233
 234static const struct snd_kcontrol_new aries_controls[] = {
 235        SOC_DAPM_PIN_SWITCH("Modem In"),
 236        SOC_DAPM_PIN_SWITCH("Modem Out"),
 237};
 238
 239static const struct snd_soc_dapm_widget aries_dapm_widgets[] = {
 240        SND_SOC_DAPM_HP("HP", NULL),
 241
 242        SND_SOC_DAPM_SPK("SPK", aries_spk_cfg),
 243        SND_SOC_DAPM_SPK("RCV", NULL),
 244
 245        SND_SOC_DAPM_LINE("LINE", NULL),
 246
 247        SND_SOC_DAPM_MIC("Main Mic", aries_main_bias),
 248        SND_SOC_DAPM_MIC("Headset Mic", aries_headset_bias),
 249
 250        SND_SOC_DAPM_MIC("Bluetooth Mic", NULL),
 251        SND_SOC_DAPM_SPK("Bluetooth SPK", NULL),
 252
 253        SND_SOC_DAPM_LINE("Modem In", NULL),
 254        SND_SOC_DAPM_LINE("Modem Out", NULL),
 255
 256        /* This must be last as it is conditionally not used */
 257        SND_SOC_DAPM_LINE("FM In", NULL),
 258};
 259
 260static int aries_hw_params(struct snd_pcm_substream *substream,
 261        struct snd_pcm_hw_params *params)
 262{
 263        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 264        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 265        unsigned int pll_out;
 266        int ret;
 267
 268        /* AIF1CLK should be >=3MHz for optimal performance */
 269        if (params_width(params) == 24)
 270                pll_out = params_rate(params) * 384;
 271        else if (params_rate(params) == 8000 || params_rate(params) == 11025)
 272                pll_out = params_rate(params) * 512;
 273        else
 274                pll_out = params_rate(params) * 256;
 275
 276        ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
 277                                ARIES_MCLK1_FREQ, pll_out);
 278        if (ret < 0)
 279                return ret;
 280
 281        ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
 282                                pll_out, SND_SOC_CLOCK_IN);
 283        if (ret < 0)
 284                return ret;
 285
 286        return 0;
 287}
 288
 289static int aries_hw_free(struct snd_pcm_substream *substream)
 290{
 291        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 292        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 293        int ret;
 294
 295        /* Switch sysclk to MCLK1 */
 296        ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
 297                                ARIES_MCLK1_FREQ, SND_SOC_CLOCK_IN);
 298        if (ret < 0)
 299                return ret;
 300
 301        /* Stop PLL */
 302        ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
 303                                ARIES_MCLK1_FREQ, 0);
 304        if (ret < 0)
 305                return ret;
 306
 307        return 0;
 308}
 309
 310/*
 311 * Main DAI operations
 312 */
 313static const struct snd_soc_ops aries_ops = {
 314        .hw_params = aries_hw_params,
 315        .hw_free = aries_hw_free,
 316};
 317
 318static int aries_baseband_init(struct snd_soc_pcm_runtime *rtd)
 319{
 320        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 321        unsigned int pll_out;
 322        int ret;
 323
 324        pll_out = 8000 * 512;
 325
 326        /* Set the codec FLL */
 327        ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1,
 328                        ARIES_MCLK1_FREQ, pll_out);
 329        if (ret < 0)
 330                return ret;
 331
 332        /* Set the codec system clock */
 333        ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
 334                        pll_out, SND_SOC_CLOCK_IN);
 335        if (ret < 0)
 336                return ret;
 337
 338        return 0;
 339}
 340
 341static int aries_late_probe(struct snd_soc_card *card)
 342{
 343        struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
 344        int ret, irq;
 345
 346        ret = snd_soc_card_jack_new(card, "Dock", SND_JACK_LINEOUT,
 347                        &aries_dock, dock_pins, ARRAY_SIZE(dock_pins));
 348        if (ret)
 349                return ret;
 350
 351        ret = devm_extcon_register_notifier(card->dev,
 352                        priv->usb_extcon, EXTCON_JACK_LINE_OUT,
 353                        &aries_extcon_notifier_block);
 354        if (ret)
 355                return ret;
 356
 357        if (extcon_get_state(priv->usb_extcon,
 358                        EXTCON_JACK_LINE_OUT) > 0)
 359                snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
 360                                SND_JACK_LINEOUT);
 361        else
 362                snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
 363
 364        ret = snd_soc_card_jack_new(card, "Headset",
 365                        SND_JACK_HEADSET | SND_JACK_BTN_0,
 366                        &aries_headset,
 367                        jack_pins, ARRAY_SIZE(jack_pins));
 368        if (ret)
 369                return ret;
 370
 371        ret = snd_soc_jack_add_zones(&aries_headset, ARRAY_SIZE(headset_zones),
 372                        headset_zones);
 373        if (ret)
 374                return ret;
 375
 376        irq = gpiod_to_irq(priv->gpio_headset_detect);
 377        if (irq < 0) {
 378                dev_err(card->dev, "Failed to map headset detect gpio to irq");
 379                return -EINVAL;
 380        }
 381
 382        ret = devm_request_threaded_irq(card->dev, irq, NULL,
 383                        headset_det_irq_thread,
 384                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
 385                        IRQF_ONESHOT, "headset_detect", priv);
 386        if (ret) {
 387                dev_err(card->dev, "Failed to request headset detect irq");
 388                return ret;
 389        }
 390
 391        headset_button_gpio[0].data = priv;
 392        headset_button_gpio[0].desc = priv->gpio_headset_key;
 393
 394        snd_jack_set_key(aries_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
 395
 396        return snd_soc_jack_add_gpios(&aries_headset,
 397                        ARRAY_SIZE(headset_button_gpio), headset_button_gpio);
 398}
 399
 400static const struct snd_soc_pcm_stream baseband_params = {
 401        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 402        .rate_min = 8000,
 403        .rate_max = 8000,
 404        .channels_min = 1,
 405        .channels_max = 1,
 406};
 407
 408static const struct snd_soc_pcm_stream bluetooth_params = {
 409        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 410        .rate_min = 8000,
 411        .rate_max = 8000,
 412        .channels_min = 1,
 413        .channels_max = 2,
 414};
 415
 416static const struct snd_soc_dapm_widget aries_modem_widgets[] = {
 417        SND_SOC_DAPM_INPUT("Modem RX"),
 418        SND_SOC_DAPM_OUTPUT("Modem TX"),
 419};
 420
 421static const struct snd_soc_dapm_route aries_modem_routes[] = {
 422        { "Modem Capture", NULL, "Modem RX" },
 423        { "Modem TX", NULL, "Modem Playback" },
 424};
 425
 426static const struct snd_soc_component_driver aries_component = {
 427        .name                   = "aries-audio",
 428        .dapm_widgets           = aries_modem_widgets,
 429        .num_dapm_widgets       = ARRAY_SIZE(aries_modem_widgets),
 430        .dapm_routes            = aries_modem_routes,
 431        .num_dapm_routes        = ARRAY_SIZE(aries_modem_routes),
 432        .idle_bias_on           = 1,
 433        .use_pmdown_time        = 1,
 434        .endianness             = 1,
 435        .non_legacy_dai_naming  = 1,
 436};
 437
 438static struct snd_soc_dai_driver aries_ext_dai[] = {
 439        {
 440                .name = "Voice call",
 441                .playback = {
 442                        .stream_name = "Modem Playback",
 443                        .channels_min = 1,
 444                        .channels_max = 1,
 445                        .rate_min = 8000,
 446                        .rate_max = 8000,
 447                        .rates = SNDRV_PCM_RATE_8000,
 448                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 449                },
 450                .capture = {
 451                        .stream_name = "Modem Capture",
 452                        .channels_min = 1,
 453                        .channels_max = 1,
 454                        .rate_min = 8000,
 455                        .rate_max = 8000,
 456                        .rates = SNDRV_PCM_RATE_8000,
 457                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 458                },
 459        },
 460};
 461
 462SND_SOC_DAILINK_DEFS(aif1,
 463        DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),
 464        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
 465        DAILINK_COMP_ARRAY(COMP_EMPTY()));
 466
 467SND_SOC_DAILINK_DEFS(baseband,
 468        DAILINK_COMP_ARRAY(COMP_CPU("Voice call")),
 469        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2")));
 470
 471SND_SOC_DAILINK_DEFS(bluetooth,
 472        DAILINK_COMP_ARRAY(COMP_CPU("bt-sco-pcm")),
 473        DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3")));
 474
 475static struct snd_soc_dai_link aries_dai[] = {
 476        {
 477                .name = "WM8994 AIF1",
 478                .stream_name = "HiFi",
 479                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 480                        SND_SOC_DAIFMT_CBM_CFM,
 481                .ops = &aries_ops,
 482                SND_SOC_DAILINK_REG(aif1),
 483        },
 484        {
 485                .name = "WM8994 AIF2",
 486                .stream_name = "Baseband",
 487                .init = &aries_baseband_init,
 488                .params = &baseband_params,
 489                .ignore_suspend = 1,
 490                SND_SOC_DAILINK_REG(baseband),
 491        },
 492        {
 493                .name = "WM8994 AIF3",
 494                .stream_name = "Bluetooth",
 495                .params = &bluetooth_params,
 496                .ignore_suspend = 1,
 497                SND_SOC_DAILINK_REG(bluetooth),
 498        },
 499};
 500
 501static struct snd_soc_card aries_card = {
 502        .name = "ARIES",
 503        .owner = THIS_MODULE,
 504        .dai_link = aries_dai,
 505        .num_links = ARRAY_SIZE(aries_dai),
 506        .controls = aries_controls,
 507        .num_controls = ARRAY_SIZE(aries_controls),
 508        .dapm_widgets = aries_dapm_widgets,
 509        .num_dapm_widgets = ARRAY_SIZE(aries_dapm_widgets),
 510        .late_probe = aries_late_probe,
 511};
 512
 513static const struct aries_wm8994_variant fascinate4g_variant = {
 514        .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS
 515                | SND_SOC_DAIFMT_IB_NF,
 516        .has_fm_radio = false,
 517};
 518
 519static const struct aries_wm8994_variant aries_variant = {
 520        .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM
 521                | SND_SOC_DAIFMT_IB_NF,
 522        .has_fm_radio = true,
 523};
 524
 525static const struct of_device_id samsung_wm8994_of_match[] = {
 526        {
 527                .compatible = "samsung,fascinate4g-wm8994",
 528                .data = &fascinate4g_variant,
 529        },
 530        {
 531                .compatible = "samsung,aries-wm8994",
 532                .data = &aries_variant,
 533        },
 534        { /* sentinel */ },
 535};
 536MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
 537
 538static int aries_audio_probe(struct platform_device *pdev)
 539{
 540        struct device_node *np = pdev->dev.of_node;
 541        struct device_node *cpu, *codec, *extcon_np;
 542        struct device *dev = &pdev->dev;
 543        struct snd_soc_card *card = &aries_card;
 544        struct aries_wm8994_data *priv;
 545        struct snd_soc_dai_link *dai_link;
 546        const struct of_device_id *match;
 547        int ret, i;
 548
 549        if (!np)
 550                return -EINVAL;
 551
 552        card->dev = dev;
 553
 554        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 555        if (!priv)
 556                return -ENOMEM;
 557
 558        snd_soc_card_set_drvdata(card, priv);
 559
 560        match = of_match_node(samsung_wm8994_of_match, np);
 561        priv->variant = match->data;
 562
 563        /* Remove FM widget if not present */
 564        if (!priv->variant->has_fm_radio)
 565                card->num_dapm_widgets--;
 566
 567        priv->reg_main_micbias = devm_regulator_get(dev, "main-micbias");
 568        if (IS_ERR(priv->reg_main_micbias)) {
 569                dev_err(dev, "Failed to get main micbias regulator\n");
 570                return PTR_ERR(priv->reg_main_micbias);
 571        }
 572
 573        priv->reg_headset_micbias = devm_regulator_get(dev, "headset-micbias");
 574        if (IS_ERR(priv->reg_headset_micbias)) {
 575                dev_err(dev, "Failed to get headset micbias regulator\n");
 576                return PTR_ERR(priv->reg_headset_micbias);
 577        }
 578
 579        priv->gpio_earpath_sel = devm_gpiod_get(dev, "earpath-sel",
 580                        GPIOD_OUT_LOW);
 581        if (IS_ERR(priv->gpio_earpath_sel)) {
 582                dev_err(dev, "Failed to get earpath selector gpio");
 583                return PTR_ERR(priv->gpio_earpath_sel);
 584        }
 585
 586        extcon_np = of_parse_phandle(np, "extcon", 0);
 587        priv->usb_extcon = extcon_find_edev_by_node(extcon_np);
 588        if (IS_ERR(priv->usb_extcon)) {
 589                if (PTR_ERR(priv->usb_extcon) != -EPROBE_DEFER)
 590                        dev_err(dev, "Failed to get extcon device");
 591                return PTR_ERR(priv->usb_extcon);
 592        }
 593        of_node_put(extcon_np);
 594
 595        priv->adc = devm_iio_channel_get(dev, "headset-detect");
 596        if (IS_ERR(priv->adc)) {
 597                if (PTR_ERR(priv->adc) != -EPROBE_DEFER)
 598                        dev_err(dev, "Failed to get ADC channel");
 599                return PTR_ERR(priv->adc);
 600        }
 601        if (priv->adc->channel->type != IIO_VOLTAGE)
 602                return -EINVAL;
 603
 604        priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key",
 605                        GPIOD_IN);
 606        if (IS_ERR(priv->gpio_headset_key)) {
 607                dev_err(dev, "Failed to get headset key gpio");
 608                return PTR_ERR(priv->gpio_headset_key);
 609        }
 610
 611        priv->gpio_headset_detect = devm_gpiod_get(dev,
 612                        "headset-detect", GPIOD_IN);
 613        if (IS_ERR(priv->gpio_headset_detect)) {
 614                dev_err(dev, "Failed to get headset detect gpio");
 615                return PTR_ERR(priv->gpio_headset_detect);
 616        }
 617
 618        /* Update card-name if provided through DT, else use default name */
 619        snd_soc_of_parse_card_name(card, "model");
 620
 621        ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
 622        if (ret < 0) {
 623                dev_err(dev, "Audio routing invalid/unspecified\n");
 624                return ret;
 625        }
 626
 627        aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt;
 628
 629        cpu = of_get_child_by_name(dev->of_node, "cpu");
 630        if (!cpu)
 631                return -EINVAL;
 632
 633        codec = of_get_child_by_name(dev->of_node, "codec");
 634        if (!codec)
 635                return -EINVAL;
 636
 637        for_each_card_prelinks(card, i, dai_link) {
 638                dai_link->codecs->of_node = of_parse_phandle(codec,
 639                                "sound-dai", 0);
 640                if (!dai_link->codecs->of_node) {
 641                        ret = -EINVAL;
 642                        goto out;
 643                }
 644        }
 645
 646        /* Set CPU and platform of_node for main DAI */
 647        aries_dai[0].cpus->of_node = of_parse_phandle(cpu,
 648                        "sound-dai", 0);
 649        if (!aries_dai[0].cpus->of_node) {
 650                ret = -EINVAL;
 651                goto out;
 652        }
 653
 654        aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node;
 655
 656        /* Set CPU of_node for BT DAI */
 657        aries_dai[2].cpus->of_node = of_parse_phandle(cpu,
 658                        "sound-dai", 1);
 659        if (!aries_dai[2].cpus->of_node) {
 660                ret = -EINVAL;
 661                goto out;
 662        }
 663
 664        ret = devm_snd_soc_register_component(dev, &aries_component,
 665                                aries_ext_dai, ARRAY_SIZE(aries_ext_dai));
 666        if (ret < 0) {
 667                dev_err(dev, "Failed to register component: %d\n", ret);
 668                goto out;
 669        }
 670
 671        ret = devm_snd_soc_register_card(dev, card);
 672        if (ret)
 673                dev_err(dev, "snd_soc_register_card() failed:%d\n", ret);
 674
 675out:
 676        of_node_put(cpu);
 677        of_node_put(codec);
 678
 679        return ret;
 680}
 681
 682static struct platform_driver aries_audio_driver = {
 683        .driver         = {
 684                .name   = "aries-audio-wm8994",
 685                .of_match_table = of_match_ptr(samsung_wm8994_of_match),
 686                .pm     = &snd_soc_pm_ops,
 687        },
 688        .probe          = aries_audio_probe,
 689};
 690
 691module_platform_driver(aries_audio_driver);
 692
 693MODULE_DESCRIPTION("ALSA SoC ARIES WM8994");
 694MODULE_LICENSE("GPL");
 695MODULE_ALIAS("platform:aries-audio-wm8994");
 696