Commit | Line | Data |
---|---|---|
c6eac8a3 XZ |
1 | /* |
2 | * Rockchip machine ASoC driver for boards using MAX98357A/RT5514/DA7219 | |
3 | * | |
4 | * Copyright (c) 2016, ROCKCHIP CORPORATION. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/gpio.h> | |
23 | #include <linux/of_gpio.h> | |
24 | #include <linux/delay.h> | |
25 | #include <linux/spi/spi.h> | |
7e0dc9ae | 26 | #include <linux/i2c.h> |
22b93eaf | 27 | #include <linux/input.h> |
c6eac8a3 XZ |
28 | #include <sound/core.h> |
29 | #include <sound/jack.h> | |
30 | #include <sound/pcm.h> | |
31 | #include <sound/pcm_params.h> | |
32 | #include <sound/soc.h> | |
33 | #include "rockchip_i2s.h" | |
34 | #include "../codecs/da7219.h" | |
35 | #include "../codecs/da7219-aad.h" | |
36 | #include "../codecs/rt5514.h" | |
37 | ||
38 | #define DRV_NAME "rk3399-gru-sound" | |
39 | ||
40 | #define SOUND_FS 256 | |
41 | ||
f628c4ed | 42 | static unsigned int dmic_wakeup_delay; |
3a6f9dce | 43 | |
c6eac8a3 XZ |
44 | static struct snd_soc_jack rockchip_sound_jack; |
45 | ||
46 | static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = { | |
47 | SND_SOC_DAPM_HP("Headphones", NULL), | |
48 | SND_SOC_DAPM_SPK("Speakers", NULL), | |
49 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
50 | SND_SOC_DAPM_MIC("Int Mic", NULL), | |
e7251484 | 51 | SND_SOC_DAPM_LINE("HDMI", NULL), |
c6eac8a3 XZ |
52 | }; |
53 | ||
c6eac8a3 XZ |
54 | static const struct snd_kcontrol_new rockchip_controls[] = { |
55 | SOC_DAPM_PIN_SWITCH("Headphones"), | |
56 | SOC_DAPM_PIN_SWITCH("Speakers"), | |
57 | SOC_DAPM_PIN_SWITCH("Headset Mic"), | |
58 | SOC_DAPM_PIN_SWITCH("Int Mic"), | |
e7251484 | 59 | SOC_DAPM_PIN_SWITCH("HDMI"), |
c6eac8a3 XZ |
60 | }; |
61 | ||
62 | static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream, | |
63 | struct snd_pcm_hw_params *params) | |
64 | { | |
65 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
66 | unsigned int mclk; | |
67 | int ret; | |
68 | ||
69 | /* max98357a supports these sample rates */ | |
70 | switch (params_rate(params)) { | |
71 | case 8000: | |
72 | case 16000: | |
73 | case 48000: | |
74 | case 96000: | |
75 | mclk = params_rate(params) * SOUND_FS; | |
76 | break; | |
77 | default: | |
78 | dev_err(rtd->card->dev, "%s() doesn't support this sample rate: %d\n", | |
79 | __func__, params_rate(params)); | |
80 | return -EINVAL; | |
81 | } | |
82 | ||
83 | ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, 0); | |
84 | if (ret) { | |
85 | dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n", | |
86 | __func__, mclk, ret); | |
87 | return ret; | |
88 | } | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | static int rockchip_sound_rt5514_hw_params(struct snd_pcm_substream *substream, | |
94 | struct snd_pcm_hw_params *params) | |
95 | { | |
96 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
97 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
98 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
99 | unsigned int mclk; | |
100 | int ret; | |
101 | ||
102 | mclk = params_rate(params) * SOUND_FS; | |
103 | ||
104 | ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, | |
105 | SND_SOC_CLOCK_OUT); | |
106 | if (ret < 0) { | |
107 | dev_err(rtd->card->dev, "Can't set cpu clock out %d\n", ret); | |
108 | return ret; | |
109 | } | |
110 | ||
111 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK, | |
112 | mclk, SND_SOC_CLOCK_IN); | |
113 | if (ret) { | |
114 | dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n", | |
115 | __func__, params_rate(params) * 512, ret); | |
116 | return ret; | |
117 | } | |
118 | ||
3a6f9dce | 119 | /* Wait for DMIC stable */ |
f628c4ed | 120 | msleep(dmic_wakeup_delay); |
3a6f9dce | 121 | |
c6eac8a3 XZ |
122 | return 0; |
123 | } | |
124 | ||
125 | static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream, | |
126 | struct snd_pcm_hw_params *params) | |
127 | { | |
128 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
129 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
130 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
131 | int mclk, ret; | |
132 | ||
133 | /* in bypass mode, the mclk has to be one of the frequencies below */ | |
134 | switch (params_rate(params)) { | |
135 | case 8000: | |
136 | case 16000: | |
137 | case 24000: | |
138 | case 32000: | |
139 | case 48000: | |
140 | case 64000: | |
141 | case 96000: | |
142 | mclk = 12288000; | |
143 | break; | |
144 | case 11025: | |
145 | case 22050: | |
146 | case 44100: | |
147 | case 88200: | |
148 | mclk = 11289600; | |
149 | break; | |
150 | default: | |
151 | return -EINVAL; | |
152 | } | |
153 | ||
154 | ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, | |
155 | SND_SOC_CLOCK_OUT); | |
156 | if (ret < 0) { | |
157 | dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret); | |
158 | return ret; | |
159 | } | |
160 | ||
161 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, | |
162 | SND_SOC_CLOCK_IN); | |
163 | if (ret < 0) { | |
164 | dev_err(codec_dai->dev, "Can't set codec clock in %d\n", ret); | |
165 | return ret; | |
166 | } | |
167 | ||
168 | ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0); | |
169 | if (ret < 0) { | |
170 | dev_err(codec_dai->dev, "Can't set pll sysclk mclk %d\n", ret); | |
171 | return ret; | |
172 | } | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) | |
178 | { | |
179 | struct snd_soc_codec *codec = rtd->codec_dais[0]->codec; | |
180 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
181 | int ret; | |
182 | ||
183 | /* We need default MCLK and PLL settings for the accessory detection */ | |
184 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12288000, | |
185 | SND_SOC_CLOCK_IN); | |
186 | if (ret < 0) { | |
187 | dev_err(codec_dai->dev, "Init can't set codec clock in %d\n", ret); | |
188 | return ret; | |
189 | } | |
190 | ||
191 | ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0); | |
192 | if (ret < 0) { | |
193 | dev_err(codec_dai->dev, "Init can't set pll sysclk mclk %d\n", ret); | |
194 | return ret; | |
195 | } | |
196 | ||
197 | /* Enable Headset and 4 Buttons Jack detection */ | |
198 | ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", | |
199 | SND_JACK_HEADSET | SND_JACK_LINEOUT | | |
200 | SND_JACK_BTN_0 | SND_JACK_BTN_1 | | |
201 | SND_JACK_BTN_2 | SND_JACK_BTN_3, | |
202 | &rockchip_sound_jack, NULL, 0); | |
203 | ||
204 | if (ret) { | |
205 | dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret); | |
206 | return ret; | |
207 | } | |
208 | ||
af1b1cef BL |
209 | snd_jack_set_key( |
210 | rockchip_sound_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); | |
22b93eaf CYC |
211 | snd_jack_set_key( |
212 | rockchip_sound_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); | |
213 | snd_jack_set_key( | |
214 | rockchip_sound_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); | |
215 | snd_jack_set_key( | |
216 | rockchip_sound_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); | |
217 | ||
c6eac8a3 XZ |
218 | da7219_aad_jack_det(codec, &rockchip_sound_jack); |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
3313faf1 JC |
223 | static int rockchip_sound_cdndp_hw_params(struct snd_pcm_substream *substream, |
224 | struct snd_pcm_hw_params *params) | |
225 | { | |
226 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
227 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
228 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
229 | int mclk, ret; | |
230 | ||
231 | /* in bypass mode, the mclk has to be one of the frequencies below */ | |
232 | switch (params_rate(params)) { | |
233 | case 8000: | |
234 | case 16000: | |
235 | case 24000: | |
236 | case 32000: | |
237 | case 48000: | |
238 | case 64000: | |
239 | case 96000: | |
240 | mclk = 12288000; | |
241 | break; | |
242 | case 11025: | |
243 | case 22050: | |
244 | case 44100: | |
245 | case 88200: | |
246 | mclk = 11289600; | |
247 | break; | |
248 | default: | |
249 | return -EINVAL; | |
250 | } | |
251 | ||
252 | ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, | |
253 | SND_SOC_CLOCK_OUT); | |
254 | if (ret < 0) { | |
255 | dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret); | |
256 | return ret; | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
626d84db JC |
262 | static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream, |
263 | struct snd_pcm_hw_params *params) | |
264 | { | |
265 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
266 | unsigned int mclk; | |
267 | int ret; | |
268 | ||
269 | mclk = params_rate(params) * SOUND_FS; | |
270 | ||
271 | ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, 0); | |
272 | if (ret) { | |
273 | dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n", | |
274 | __func__, mclk, ret); | |
275 | return ret; | |
276 | } | |
277 | ||
278 | /* Wait for DMIC stable */ | |
279 | msleep(dmic_wakeup_delay); | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
705e9994 | 284 | static const struct snd_soc_ops rockchip_sound_max98357a_ops = { |
c6eac8a3 XZ |
285 | .hw_params = rockchip_sound_max98357a_hw_params, |
286 | }; | |
287 | ||
705e9994 | 288 | static const struct snd_soc_ops rockchip_sound_rt5514_ops = { |
c6eac8a3 XZ |
289 | .hw_params = rockchip_sound_rt5514_hw_params, |
290 | }; | |
291 | ||
705e9994 | 292 | static const struct snd_soc_ops rockchip_sound_da7219_ops = { |
c6eac8a3 XZ |
293 | .hw_params = rockchip_sound_da7219_hw_params, |
294 | }; | |
295 | ||
1fe165b7 | 296 | static const struct snd_soc_ops rockchip_sound_cdndp_ops = { |
3313faf1 JC |
297 | .hw_params = rockchip_sound_cdndp_hw_params, |
298 | }; | |
299 | ||
1fe165b7 | 300 | static const struct snd_soc_ops rockchip_sound_dmic_ops = { |
626d84db JC |
301 | .hw_params = rockchip_sound_dmic_hw_params, |
302 | }; | |
303 | ||
0d52954f JC |
304 | static struct snd_soc_card rockchip_sound_card = { |
305 | .name = "rk3399-gru-sound", | |
306 | .owner = THIS_MODULE, | |
307 | .dapm_widgets = rockchip_dapm_widgets, | |
308 | .num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets), | |
0d52954f JC |
309 | .controls = rockchip_controls, |
310 | .num_controls = ARRAY_SIZE(rockchip_controls), | |
311 | }; | |
312 | ||
c6eac8a3 | 313 | enum { |
3313faf1 | 314 | DAILINK_CDNDP, |
0d52954f | 315 | DAILINK_DA7219, |
626d84db | 316 | DAILINK_DMIC, |
c6eac8a3 XZ |
317 | DAILINK_MAX98357A, |
318 | DAILINK_RT5514, | |
e5abe959 | 319 | DAILINK_RT5514_DSP, |
c6eac8a3 XZ |
320 | }; |
321 | ||
0d52954f | 322 | static const struct snd_soc_dai_link rockchip_dais[] = { |
3313faf1 JC |
323 | [DAILINK_CDNDP] = { |
324 | .name = "DP", | |
325 | .stream_name = "DP PCM", | |
326 | .codec_dai_name = "i2s-hifi", | |
327 | .ops = &rockchip_sound_cdndp_ops, | |
328 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
329 | SND_SOC_DAIFMT_CBS_CFS, | |
330 | }, | |
0d52954f JC |
331 | [DAILINK_DA7219] = { |
332 | .name = "DA7219", | |
333 | .stream_name = "DA7219 PCM", | |
334 | .codec_dai_name = "da7219-hifi", | |
335 | .init = rockchip_sound_da7219_init, | |
336 | .ops = &rockchip_sound_da7219_ops, | |
337 | /* set da7219 as slave */ | |
338 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
339 | SND_SOC_DAIFMT_CBS_CFS, | |
340 | }, | |
626d84db JC |
341 | [DAILINK_DMIC] = { |
342 | .name = "DMIC", | |
343 | .stream_name = "DMIC PCM", | |
344 | .codec_dai_name = "dmic-hifi", | |
345 | .ops = &rockchip_sound_dmic_ops, | |
346 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
347 | SND_SOC_DAIFMT_CBS_CFS, | |
348 | }, | |
c6eac8a3 XZ |
349 | [DAILINK_MAX98357A] = { |
350 | .name = "MAX98357A", | |
351 | .stream_name = "MAX98357A PCM", | |
352 | .codec_dai_name = "HiFi", | |
353 | .ops = &rockchip_sound_max98357a_ops, | |
354 | /* set max98357a as slave */ | |
355 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
356 | SND_SOC_DAIFMT_CBS_CFS, | |
357 | }, | |
358 | [DAILINK_RT5514] = { | |
359 | .name = "RT5514", | |
360 | .stream_name = "RT5514 PCM", | |
361 | .codec_dai_name = "rt5514-aif1", | |
362 | .ops = &rockchip_sound_rt5514_ops, | |
363 | /* set rt5514 as slave */ | |
364 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
365 | SND_SOC_DAIFMT_CBS_CFS, | |
366 | }, | |
e5abe959 XZ |
367 | /* RT5514 DSP for voice wakeup via spi bus */ |
368 | [DAILINK_RT5514_DSP] = { | |
369 | .name = "RT5514 DSP", | |
370 | .stream_name = "Wake on Voice", | |
72cfb0f2 | 371 | .codec_dai_name = "rt5514-dsp-cpu-dai", |
e5abe959 | 372 | }, |
c6eac8a3 XZ |
373 | }; |
374 | ||
d9f9c167 JC |
375 | static const struct snd_soc_dapm_route rockchip_sound_cdndp_routes[] = { |
376 | /* Output */ | |
377 | {"HDMI", NULL, "TX"}, | |
378 | }; | |
379 | ||
380 | static const struct snd_soc_dapm_route rockchip_sound_da7219_routes[] = { | |
381 | /* Output */ | |
382 | {"Headphones", NULL, "HPL"}, | |
383 | {"Headphones", NULL, "HPR"}, | |
384 | ||
385 | /* Input */ | |
386 | {"MIC", NULL, "Headset Mic"}, | |
387 | }; | |
388 | ||
389 | static const struct snd_soc_dapm_route rockchip_sound_dmic_routes[] = { | |
390 | /* Input */ | |
391 | {"DMic", NULL, "Int Mic"}, | |
392 | }; | |
393 | ||
394 | static const struct snd_soc_dapm_route rockchip_sound_max98357a_routes[] = { | |
395 | /* Output */ | |
396 | {"Speakers", NULL, "Speaker"}, | |
397 | }; | |
398 | ||
399 | static const struct snd_soc_dapm_route rockchip_sound_rt5514_routes[] = { | |
400 | /* Input */ | |
401 | {"DMIC1L", NULL, "Int Mic"}, | |
402 | {"DMIC1R", NULL, "Int Mic"}, | |
403 | }; | |
404 | ||
405 | struct rockchip_sound_route { | |
406 | const struct snd_soc_dapm_route *routes; | |
407 | int num_routes; | |
408 | }; | |
409 | ||
410 | static const struct rockchip_sound_route rockchip_routes[] = { | |
411 | [DAILINK_CDNDP] = { | |
412 | .routes = rockchip_sound_cdndp_routes, | |
413 | .num_routes = ARRAY_SIZE(rockchip_sound_cdndp_routes), | |
414 | }, | |
415 | [DAILINK_DA7219] = { | |
416 | .routes = rockchip_sound_da7219_routes, | |
417 | .num_routes = ARRAY_SIZE(rockchip_sound_da7219_routes), | |
418 | }, | |
419 | [DAILINK_DMIC] = { | |
420 | .routes = rockchip_sound_dmic_routes, | |
421 | .num_routes = ARRAY_SIZE(rockchip_sound_dmic_routes), | |
422 | }, | |
423 | [DAILINK_MAX98357A] = { | |
424 | .routes = rockchip_sound_max98357a_routes, | |
425 | .num_routes = ARRAY_SIZE(rockchip_sound_max98357a_routes), | |
426 | }, | |
427 | [DAILINK_RT5514] = { | |
428 | .routes = rockchip_sound_rt5514_routes, | |
429 | .num_routes = ARRAY_SIZE(rockchip_sound_rt5514_routes), | |
430 | }, | |
431 | [DAILINK_RT5514_DSP] = {}, | |
432 | }; | |
433 | ||
7e0dc9ae JC |
434 | struct dailink_match_data { |
435 | const char *compatible; | |
436 | struct bus_type *bus_type; | |
437 | }; | |
438 | ||
439 | static const struct dailink_match_data dailink_match[] = { | |
440 | [DAILINK_CDNDP] = { | |
441 | .compatible = "rockchip,rk3399-cdn-dp", | |
442 | }, | |
443 | [DAILINK_DA7219] = { | |
444 | .compatible = "dlg,da7219", | |
445 | }, | |
446 | [DAILINK_DMIC] = { | |
447 | .compatible = "dmic-codec", | |
448 | }, | |
449 | [DAILINK_MAX98357A] = { | |
450 | .compatible = "maxim,max98357a", | |
451 | }, | |
452 | [DAILINK_RT5514] = { | |
453 | .compatible = "realtek,rt5514", | |
454 | .bus_type = &i2c_bus_type, | |
455 | }, | |
456 | [DAILINK_RT5514_DSP] = { | |
457 | .compatible = "realtek,rt5514", | |
458 | .bus_type = &spi_bus_type, | |
459 | }, | |
460 | }; | |
461 | ||
462 | static int of_dev_node_match(struct device *dev, void *data) | |
463 | { | |
464 | return dev->of_node == data; | |
465 | } | |
466 | ||
0d52954f | 467 | static int rockchip_sound_codec_node_match(struct device_node *np_codec) |
e5abe959 | 468 | { |
7e0dc9ae | 469 | struct device *dev; |
0d52954f | 470 | int i; |
e5abe959 | 471 | |
7e0dc9ae JC |
472 | for (i = 0; i < ARRAY_SIZE(dailink_match); i++) { |
473 | if (!of_device_is_compatible(np_codec, | |
474 | dailink_match[i].compatible)) | |
475 | continue; | |
476 | ||
477 | if (dailink_match[i].bus_type) { | |
478 | dev = bus_find_device(dailink_match[i].bus_type, NULL, | |
479 | np_codec, of_dev_node_match); | |
480 | if (!dev) | |
481 | continue; | |
482 | put_device(dev); | |
483 | } | |
484 | ||
485 | return i; | |
c6eac8a3 | 486 | } |
0d52954f JC |
487 | return -1; |
488 | } | |
c6eac8a3 | 489 | |
0d52954f JC |
490 | static int rockchip_sound_of_parse_dais(struct device *dev, |
491 | struct snd_soc_card *card) | |
492 | { | |
3313faf1 | 493 | struct device_node *np_cpu, *np_cpu0, *np_cpu1; |
0d52954f JC |
494 | struct device_node *np_codec; |
495 | struct snd_soc_dai_link *dai; | |
d9f9c167 | 496 | struct snd_soc_dapm_route *routes; |
0d52954f | 497 | int i, index; |
8eae6c25 | 498 | int num_routes; |
0d52954f JC |
499 | |
500 | card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais), | |
501 | GFP_KERNEL); | |
502 | if (!card->dai_link) | |
503 | return -ENOMEM; | |
504 | ||
8eae6c25 DA |
505 | num_routes = 0; |
506 | for (i = 0; i < ARRAY_SIZE(rockchip_routes); i++) | |
507 | num_routes += rockchip_routes[i].num_routes; | |
508 | routes = devm_kzalloc(dev, num_routes * sizeof(*routes), | |
d9f9c167 JC |
509 | GFP_KERNEL); |
510 | if (!routes) | |
511 | return -ENOMEM; | |
512 | card->dapm_routes = routes; | |
513 | ||
3313faf1 JC |
514 | np_cpu0 = of_parse_phandle(dev->of_node, "rockchip,cpu", 0); |
515 | np_cpu1 = of_parse_phandle(dev->of_node, "rockchip,cpu", 1); | |
0d52954f | 516 | |
d9f9c167 | 517 | card->num_dapm_routes = 0; |
0d52954f JC |
518 | card->num_links = 0; |
519 | for (i = 0; i < ARRAY_SIZE(rockchip_dais); i++) { | |
520 | np_codec = of_parse_phandle(dev->of_node, | |
521 | "rockchip,codec", i); | |
522 | if (!np_codec) | |
523 | break; | |
524 | ||
525 | if (!of_device_is_available(np_codec)) | |
526 | continue; | |
527 | ||
528 | index = rockchip_sound_codec_node_match(np_codec); | |
529 | if (index < 0) | |
530 | continue; | |
531 | ||
3313faf1 | 532 | np_cpu = (index == DAILINK_CDNDP) ? np_cpu1 : np_cpu0; |
0d52954f JC |
533 | if (!np_cpu) { |
534 | dev_err(dev, "Missing 'rockchip,cpu' for %s\n", | |
535 | rockchip_dais[index].name); | |
c6eac8a3 XZ |
536 | return -EINVAL; |
537 | } | |
c6eac8a3 | 538 | |
0d52954f JC |
539 | dai = &card->dai_link[card->num_links++]; |
540 | *dai = rockchip_dais[index]; | |
541 | ||
542 | dai->codec_of_node = np_codec; | |
543 | dai->platform_of_node = np_cpu; | |
544 | dai->cpu_of_node = np_cpu; | |
d9f9c167 | 545 | |
8eae6c25 DA |
546 | if (card->num_dapm_routes + rockchip_routes[index].num_routes > |
547 | num_routes) { | |
548 | dev_err(dev, "Too many routes\n"); | |
549 | return -EINVAL; | |
550 | } | |
551 | ||
d9f9c167 JC |
552 | memcpy(routes + card->num_dapm_routes, |
553 | rockchip_routes[index].routes, | |
554 | rockchip_routes[index].num_routes * sizeof(*routes)); | |
555 | card->num_dapm_routes += rockchip_routes[index].num_routes; | |
e5abe959 XZ |
556 | } |
557 | ||
0d52954f JC |
558 | return 0; |
559 | } | |
560 | ||
561 | static int rockchip_sound_probe(struct platform_device *pdev) | |
562 | { | |
563 | struct snd_soc_card *card = &rockchip_sound_card; | |
564 | int ret; | |
565 | ||
566 | ret = rockchip_sound_of_parse_dais(&pdev->dev, card); | |
567 | if (ret < 0) { | |
568 | dev_err(&pdev->dev, "Failed to parse dais: %d\n", ret); | |
569 | return ret; | |
e5abe959 XZ |
570 | } |
571 | ||
f628c4ed JC |
572 | /* Set DMIC wakeup delay */ |
573 | ret = device_property_read_u32(&pdev->dev, "dmic-wakeup-delay-ms", | |
574 | &dmic_wakeup_delay); | |
3a6f9dce | 575 | if (ret) { |
f628c4ed | 576 | dmic_wakeup_delay = 0; |
3a6f9dce | 577 | dev_dbg(&pdev->dev, |
f628c4ed | 578 | "no optional property 'dmic-wakeup-delay-ms' found, default: no delay\n"); |
3a6f9dce WL |
579 | } |
580 | ||
c6eac8a3 | 581 | card->dev = &pdev->dev; |
0d52954f | 582 | return devm_snd_soc_register_card(&pdev->dev, card); |
c6eac8a3 XZ |
583 | } |
584 | ||
585 | static const struct of_device_id rockchip_sound_of_match[] = { | |
586 | { .compatible = "rockchip,rk3399-gru-sound", }, | |
587 | {}, | |
588 | }; | |
589 | ||
590 | static struct platform_driver rockchip_sound_driver = { | |
591 | .probe = rockchip_sound_probe, | |
592 | .driver = { | |
593 | .name = DRV_NAME, | |
594 | .of_match_table = rockchip_sound_of_match, | |
595 | #ifdef CONFIG_PM | |
596 | .pm = &snd_soc_pm_ops, | |
597 | #endif | |
598 | }, | |
599 | }; | |
600 | ||
601 | module_platform_driver(rockchip_sound_driver); | |
602 | ||
603 | MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>"); | |
604 | MODULE_DESCRIPTION("Rockchip ASoC Machine Driver"); | |
605 | MODULE_LICENSE("GPL v2"); | |
606 | MODULE_ALIAS("platform:" DRV_NAME); | |
607 | MODULE_DEVICE_TABLE(of, rockchip_sound_of_match); |