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 21 struct aries_wm8994_variant { 22 unsigned int modem_dai_fmt; 23 bool has_fm_radio; 24 }; 25 26 struct 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 */ 38 static struct snd_soc_jack aries_dock; 39 40 static struct snd_soc_jack_pin dock_pins[] = { 41 { 42 .pin = "LINE", 43 .mask = SND_JACK_LINEOUT, 44 }, 45 }; 46 47 static 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 59 static struct notifier_block aries_extcon_notifier_block = { 60 .notifier_call = aries_extcon_notifier, 61 }; 62 63 /* Headset jack */ 64 static struct snd_soc_jack aries_headset; 65 66 static 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 76 static 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 92 static 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 140 static 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 152 static 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 161 static 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 196 static 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 215 static 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 234 static const struct snd_kcontrol_new aries_controls[] = { 235 SOC_DAPM_PIN_SWITCH("Modem In"), 236 SOC_DAPM_PIN_SWITCH("Modem Out"), 237 }; 238 239 static 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 260 static 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 289 static 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 */ 313 static const struct snd_soc_ops aries_ops = { 314 .hw_params = aries_hw_params, 315 .hw_free = aries_hw_free, 316 }; 317 318 static 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 341 static 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 400 static 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 408 static 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 416 static 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 421 static const struct snd_soc_dapm_route aries_modem_routes[] = { 422 { "Modem Capture", NULL, "Modem RX" }, 423 { "Modem TX", NULL, "Modem Playback" }, 424 }; 425 426 static 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 438 static 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 462 SND_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 467 SND_SOC_DAILINK_DEFS(baseband, 468 DAILINK_COMP_ARRAY(COMP_CPU("Voice call")), 469 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2"))); 470 471 SND_SOC_DAILINK_DEFS(bluetooth, 472 DAILINK_COMP_ARRAY(COMP_CPU("bt-sco-pcm")), 473 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3"))); 474 475 static 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 501 static 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 513 static 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 519 static 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 525 static 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 }; 536 MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); 537 538 static 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 675 out: 676 of_node_put(cpu); 677 of_node_put(codec); 678 679 return ret; 680 } 681 682 static 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 691 module_platform_driver(aries_audio_driver); 692 693 MODULE_DESCRIPTION("ALSA SoC ARIES WM8994"); 694 MODULE_LICENSE("GPL"); 695 MODULE_ALIAS("platform:aries-audio-wm8994"); 696