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