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