Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/samsung/aries_wm8994.c
26428 views
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_CBP_CFP,
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_CBC_CFC
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_CBP_CFP
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
704