aries_wm8994.c (17734B)
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_pins(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_pins(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 of_node_put(extcon_np); 589 if (IS_ERR(priv->usb_extcon)) 590 return dev_err_probe(dev, PTR_ERR(priv->usb_extcon), 591 "Failed to get extcon device"); 592 593 priv->adc = devm_iio_channel_get(dev, "headset-detect"); 594 if (IS_ERR(priv->adc)) 595 return dev_err_probe(dev, PTR_ERR(priv->adc), 596 "Failed to get ADC channel"); 597 598 if (priv->adc->channel->type != IIO_VOLTAGE) 599 return -EINVAL; 600 601 priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key", 602 GPIOD_IN); 603 if (IS_ERR(priv->gpio_headset_key)) { 604 dev_err(dev, "Failed to get headset key gpio"); 605 return PTR_ERR(priv->gpio_headset_key); 606 } 607 608 priv->gpio_headset_detect = devm_gpiod_get(dev, 609 "headset-detect", GPIOD_IN); 610 if (IS_ERR(priv->gpio_headset_detect)) { 611 dev_err(dev, "Failed to get headset detect gpio"); 612 return PTR_ERR(priv->gpio_headset_detect); 613 } 614 615 /* Update card-name if provided through DT, else use default name */ 616 snd_soc_of_parse_card_name(card, "model"); 617 618 ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); 619 if (ret < 0) { 620 dev_err(dev, "Audio routing invalid/unspecified\n"); 621 return ret; 622 } 623 624 aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt; 625 626 cpu = of_get_child_by_name(dev->of_node, "cpu"); 627 if (!cpu) 628 return -EINVAL; 629 630 codec = of_get_child_by_name(dev->of_node, "codec"); 631 if (!codec) 632 return -EINVAL; 633 634 for_each_card_prelinks(card, i, dai_link) { 635 dai_link->codecs->of_node = of_parse_phandle(codec, 636 "sound-dai", 0); 637 if (!dai_link->codecs->of_node) { 638 ret = -EINVAL; 639 goto out; 640 } 641 } 642 643 /* Set CPU and platform of_node for main DAI */ 644 aries_dai[0].cpus->of_node = of_parse_phandle(cpu, 645 "sound-dai", 0); 646 if (!aries_dai[0].cpus->of_node) { 647 ret = -EINVAL; 648 goto out; 649 } 650 651 aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node; 652 653 /* Set CPU of_node for BT DAI */ 654 aries_dai[2].cpus->of_node = of_parse_phandle(cpu, 655 "sound-dai", 1); 656 if (!aries_dai[2].cpus->of_node) { 657 ret = -EINVAL; 658 goto out; 659 } 660 661 ret = devm_snd_soc_register_component(dev, &aries_component, 662 aries_ext_dai, ARRAY_SIZE(aries_ext_dai)); 663 if (ret < 0) { 664 dev_err(dev, "Failed to register component: %d\n", ret); 665 goto out; 666 } 667 668 ret = devm_snd_soc_register_card(dev, card); 669 if (ret) 670 dev_err(dev, "snd_soc_register_card() failed:%d\n", ret); 671 672out: 673 of_node_put(cpu); 674 of_node_put(codec); 675 676 return ret; 677} 678 679static struct platform_driver aries_audio_driver = { 680 .driver = { 681 .name = "aries-audio-wm8994", 682 .of_match_table = of_match_ptr(samsung_wm8994_of_match), 683 .pm = &snd_soc_pm_ops, 684 }, 685 .probe = aries_audio_probe, 686}; 687 688module_platform_driver(aries_audio_driver); 689 690MODULE_DESCRIPTION("ALSA SoC ARIES WM8994"); 691MODULE_LICENSE("GPL"); 692MODULE_ALIAS("platform:aries-audio-wm8994");