1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // Bells audio support
4 //
5 // Copyright 2012 Wolfson Microelectronics
6
7 #include <sound/soc.h>
8 #include <sound/soc-dapm.h>
9 #include <sound/jack.h>
10 #include <linux/module.h>
11
12 #include "../codecs/wm5102.h"
13 #include "../codecs/wm9081.h"
14
15 /* BCLK2 is fixed at this currently */
16 #define BCLK2_RATE (64 * 8000)
17
18 /*
19 * Expect a 24.576MHz crystal if one is fitted (the driver will function
20 * if this is not fitted).
21 */
22 #define MCLK_RATE 24576000
23
24 #define SYS_AUDIO_RATE 44100
25 #define SYS_MCLK_RATE (SYS_AUDIO_RATE * 512)
26
27 #define DAI_AP_DSP 0
28 #define DAI_DSP_CODEC 1
29 #define DAI_CODEC_CP 2
30 #define DAI_CODEC_SUB 3
31
32 struct bells_drvdata {
33 int sysclk_rate;
34 int asyncclk_rate;
35 };
36
37 static struct bells_drvdata wm2200_drvdata = {
38 .sysclk_rate = 22579200,
39 };
40
41 static struct bells_drvdata wm5102_drvdata = {
42 .sysclk_rate = 45158400,
43 .asyncclk_rate = 49152000,
44 };
45
46 static struct bells_drvdata wm5110_drvdata = {
47 .sysclk_rate = 135475200,
48 .asyncclk_rate = 147456000,
49 };
50
bells_set_bias_level(struct snd_soc_card * card,struct snd_soc_dapm_context * dapm,enum snd_soc_bias_level level)51 static int bells_set_bias_level(struct snd_soc_card *card,
52 struct snd_soc_dapm_context *dapm,
53 enum snd_soc_bias_level level)
54 {
55 struct snd_soc_pcm_runtime *rtd;
56 struct snd_soc_dai *codec_dai;
57 struct snd_soc_component *component;
58 struct bells_drvdata *bells = card->drvdata;
59 int ret;
60
61 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
62 codec_dai = snd_soc_rtd_to_codec(rtd, 0);
63 component = codec_dai->component;
64
65 if (dapm->dev != codec_dai->dev)
66 return 0;
67
68 switch (level) {
69 case SND_SOC_BIAS_PREPARE:
70 if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
71 break;
72
73 ret = snd_soc_component_set_pll(component, WM5102_FLL1,
74 ARIZONA_FLL_SRC_MCLK1,
75 MCLK_RATE,
76 bells->sysclk_rate);
77 if (ret < 0)
78 pr_err("Failed to start FLL: %d\n", ret);
79
80 if (bells->asyncclk_rate) {
81 ret = snd_soc_component_set_pll(component, WM5102_FLL2,
82 ARIZONA_FLL_SRC_AIF2BCLK,
83 BCLK2_RATE,
84 bells->asyncclk_rate);
85 if (ret < 0)
86 pr_err("Failed to start FLL: %d\n", ret);
87 }
88 break;
89
90 default:
91 break;
92 }
93
94 return 0;
95 }
96
bells_set_bias_level_post(struct snd_soc_card * card,struct snd_soc_dapm_context * dapm,enum snd_soc_bias_level level)97 static int bells_set_bias_level_post(struct snd_soc_card *card,
98 struct snd_soc_dapm_context *dapm,
99 enum snd_soc_bias_level level)
100 {
101 struct snd_soc_pcm_runtime *rtd;
102 struct snd_soc_dai *codec_dai;
103 struct snd_soc_component *component;
104 struct bells_drvdata *bells = card->drvdata;
105 int ret;
106
107 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
108 codec_dai = snd_soc_rtd_to_codec(rtd, 0);
109 component = codec_dai->component;
110
111 if (dapm->dev != codec_dai->dev)
112 return 0;
113
114 switch (level) {
115 case SND_SOC_BIAS_STANDBY:
116 ret = snd_soc_component_set_pll(component, WM5102_FLL1, 0, 0, 0);
117 if (ret < 0) {
118 pr_err("Failed to stop FLL: %d\n", ret);
119 return ret;
120 }
121
122 if (bells->asyncclk_rate) {
123 ret = snd_soc_component_set_pll(component, WM5102_FLL2,
124 0, 0, 0);
125 if (ret < 0) {
126 pr_err("Failed to stop FLL: %d\n", ret);
127 return ret;
128 }
129 }
130 break;
131
132 default:
133 break;
134 }
135
136 return 0;
137 }
138
bells_late_probe(struct snd_soc_card * card)139 static int bells_late_probe(struct snd_soc_card *card)
140 {
141 struct bells_drvdata *bells = card->drvdata;
142 struct snd_soc_pcm_runtime *rtd;
143 struct snd_soc_component *wm0010;
144 struct snd_soc_component *component;
145 struct snd_soc_dai *aif1_dai;
146 struct snd_soc_dai *aif2_dai;
147 struct snd_soc_dai *aif3_dai;
148 struct snd_soc_dai *wm9081_dai;
149 int ret;
150
151 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]);
152 wm0010 = snd_soc_rtd_to_codec(rtd, 0)->component;
153
154 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
155 component = snd_soc_rtd_to_codec(rtd, 0)->component;
156 aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
157
158 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,
159 ARIZONA_CLK_SRC_FLL1,
160 bells->sysclk_rate,
161 SND_SOC_CLOCK_IN);
162 if (ret != 0) {
163 dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);
164 return ret;
165 }
166
167 ret = snd_soc_component_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
168 if (ret != 0) {
169 dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
170 return ret;
171 }
172
173 ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
174 if (ret != 0)
175 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
176
177 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_OPCLK, 0,
178 SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
179 if (ret != 0)
180 dev_err(component->dev, "Failed to set OPCLK: %d\n", ret);
181
182 if (card->num_rtd == DAI_CODEC_CP)
183 return 0;
184
185 ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK,
186 ARIZONA_CLK_SRC_FLL2,
187 bells->asyncclk_rate,
188 SND_SOC_CLOCK_IN);
189 if (ret != 0) {
190 dev_err(component->dev, "Failed to set ASYNCCLK: %d\n", ret);
191 return ret;
192 }
193
194 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]);
195 aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);
196
197 ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
198 if (ret != 0) {
199 dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
200 return ret;
201 }
202
203 if (card->num_rtd == DAI_CODEC_SUB)
204 return 0;
205
206 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]);
207 aif3_dai = snd_soc_rtd_to_cpu(rtd, 0);
208 wm9081_dai = snd_soc_rtd_to_codec(rtd, 0);
209
210 ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
211 if (ret != 0) {
212 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
213 return ret;
214 }
215
216 ret = snd_soc_component_set_sysclk(wm9081_dai->component, WM9081_SYSCLK_MCLK,
217 0, SYS_MCLK_RATE, 0);
218 if (ret != 0) {
219 dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
220 return ret;
221 }
222
223 return 0;
224 }
225
226 static const struct snd_soc_pcm_stream baseband_params = {
227 .formats = SNDRV_PCM_FMTBIT_S32_LE,
228 .rate_min = 8000,
229 .rate_max = 8000,
230 .channels_min = 2,
231 .channels_max = 2,
232 };
233
234 static const struct snd_soc_pcm_stream sub_params = {
235 .formats = SNDRV_PCM_FMTBIT_S32_LE,
236 .rate_min = SYS_AUDIO_RATE,
237 .rate_max = SYS_AUDIO_RATE,
238 .channels_min = 2,
239 .channels_max = 2,
240 };
241
242 SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp,
243 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
244 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
245 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
246
247 SND_SOC_DAILINK_DEFS(wm2200_dsp_codec,
248 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
249 DAILINK_COMP_ARRAY(COMP_CODEC("wm2200.1-003a", "wm2200")));
250
251 static struct snd_soc_dai_link bells_dai_wm2200[] = {
252 {
253 .name = "CPU-DSP",
254 .stream_name = "CPU-DSP",
255 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
256 | SND_SOC_DAIFMT_CBP_CFP,
257 SND_SOC_DAILINK_REG(wm2200_cpu_dsp),
258 },
259 {
260 .name = "DSP-CODEC",
261 .stream_name = "DSP-CODEC",
262 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
263 | SND_SOC_DAIFMT_CBP_CFP,
264 .c2c_params = &sub_params,
265 .num_c2c_params = 1,
266 .ignore_suspend = 1,
267 SND_SOC_DAILINK_REG(wm2200_dsp_codec),
268 },
269 };
270
271 SND_SOC_DAILINK_DEFS(wm5102_cpu_dsp,
272 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
273 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
274 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
275
276 SND_SOC_DAILINK_DEFS(wm5102_dsp_codec,
277 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
278 DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1")));
279
280 SND_SOC_DAILINK_DEFS(wm5102_baseband,
281 DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")),
282 DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
283
284 SND_SOC_DAILINK_DEFS(wm5102_sub,
285 DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif3")),
286 DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
287
288 static struct snd_soc_dai_link bells_dai_wm5102[] = {
289 {
290 .name = "CPU-DSP",
291 .stream_name = "CPU-DSP",
292 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
293 | SND_SOC_DAIFMT_CBP_CFP,
294 SND_SOC_DAILINK_REG(wm5102_cpu_dsp),
295 },
296 {
297 .name = "DSP-CODEC",
298 .stream_name = "DSP-CODEC",
299 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
300 | SND_SOC_DAIFMT_CBP_CFP,
301 .c2c_params = &sub_params,
302 .num_c2c_params = 1,
303 .ignore_suspend = 1,
304 SND_SOC_DAILINK_REG(wm5102_dsp_codec),
305 },
306 {
307 .name = "Baseband",
308 .stream_name = "Baseband",
309 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
310 | SND_SOC_DAIFMT_CBP_CFP,
311 .ignore_suspend = 1,
312 .c2c_params = &baseband_params,
313 .num_c2c_params = 1,
314 SND_SOC_DAILINK_REG(wm5102_baseband),
315 },
316 {
317 .name = "Sub",
318 .stream_name = "Sub",
319 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
320 | SND_SOC_DAIFMT_CBC_CFC,
321 .ignore_suspend = 1,
322 .c2c_params = &sub_params,
323 .num_c2c_params = 1,
324 SND_SOC_DAILINK_REG(wm5102_sub),
325 },
326 };
327
328 SND_SOC_DAILINK_DEFS(wm5110_cpu_dsp,
329 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
330 DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
331 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
332
333 SND_SOC_DAILINK_DEFS(wm5110_dsp_codec,
334 DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
335 DAILINK_COMP_ARRAY(COMP_CODEC("wm5110-codec", "wm5110-aif1")));
336
337 SND_SOC_DAILINK_DEFS(wm5110_baseband,
338 DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif2")),
339 DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
340
341
342 SND_SOC_DAILINK_DEFS(wm5110_sub,
343 DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif3")),
344 DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
345
346 static struct snd_soc_dai_link bells_dai_wm5110[] = {
347 {
348 .name = "CPU-DSP",
349 .stream_name = "CPU-DSP",
350 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
351 | SND_SOC_DAIFMT_CBP_CFP,
352 SND_SOC_DAILINK_REG(wm5110_cpu_dsp),
353 },
354 {
355 .name = "DSP-CODEC",
356 .stream_name = "DSP-CODEC",
357 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
358 | SND_SOC_DAIFMT_CBP_CFP,
359 .c2c_params = &sub_params,
360 .num_c2c_params = 1,
361 .ignore_suspend = 1,
362 SND_SOC_DAILINK_REG(wm5110_dsp_codec),
363 },
364 {
365 .name = "Baseband",
366 .stream_name = "Baseband",
367 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
368 | SND_SOC_DAIFMT_CBP_CFP,
369 .ignore_suspend = 1,
370 .c2c_params = &baseband_params,
371 .num_c2c_params = 1,
372 SND_SOC_DAILINK_REG(wm5110_baseband),
373 },
374 {
375 .name = "Sub",
376 .stream_name = "Sub",
377 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
378 | SND_SOC_DAIFMT_CBC_CFC,
379 .ignore_suspend = 1,
380 .c2c_params = &sub_params,
381 .num_c2c_params = 1,
382 SND_SOC_DAILINK_REG(wm5110_sub),
383 },
384 };
385
386 static struct snd_soc_codec_conf bells_codec_conf[] = {
387 {
388 .dlc = COMP_CODEC_CONF("wm9081.1-006c"),
389 .name_prefix = "Sub",
390 },
391 };
392
393 static const struct snd_soc_dapm_widget bells_widgets[] = {
394 SND_SOC_DAPM_MIC("DMIC", NULL),
395 };
396
397 static const struct snd_soc_dapm_route bells_routes[] = {
398 { "Sub CLK_SYS", NULL, "OPCLK" },
399 { "CLKIN", NULL, "OPCLK" },
400
401 { "DMIC", NULL, "MICBIAS2" },
402 { "IN2L", NULL, "DMIC" },
403 { "IN2R", NULL, "DMIC" },
404 };
405
406 static struct snd_soc_card bells_cards[] = {
407 {
408 .name = "Bells WM2200",
409 .owner = THIS_MODULE,
410 .dai_link = bells_dai_wm2200,
411 .num_links = ARRAY_SIZE(bells_dai_wm2200),
412 .codec_conf = bells_codec_conf,
413 .num_configs = ARRAY_SIZE(bells_codec_conf),
414
415 .late_probe = bells_late_probe,
416
417 .dapm_widgets = bells_widgets,
418 .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
419 .dapm_routes = bells_routes,
420 .num_dapm_routes = ARRAY_SIZE(bells_routes),
421
422 .set_bias_level = bells_set_bias_level,
423 .set_bias_level_post = bells_set_bias_level_post,
424
425 .drvdata = &wm2200_drvdata,
426 },
427 {
428 .name = "Bells WM5102",
429 .owner = THIS_MODULE,
430 .dai_link = bells_dai_wm5102,
431 .num_links = ARRAY_SIZE(bells_dai_wm5102),
432 .codec_conf = bells_codec_conf,
433 .num_configs = ARRAY_SIZE(bells_codec_conf),
434
435 .late_probe = bells_late_probe,
436
437 .dapm_widgets = bells_widgets,
438 .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
439 .dapm_routes = bells_routes,
440 .num_dapm_routes = ARRAY_SIZE(bells_routes),
441
442 .set_bias_level = bells_set_bias_level,
443 .set_bias_level_post = bells_set_bias_level_post,
444
445 .drvdata = &wm5102_drvdata,
446 },
447 {
448 .name = "Bells WM5110",
449 .owner = THIS_MODULE,
450 .dai_link = bells_dai_wm5110,
451 .num_links = ARRAY_SIZE(bells_dai_wm5110),
452 .codec_conf = bells_codec_conf,
453 .num_configs = ARRAY_SIZE(bells_codec_conf),
454
455 .late_probe = bells_late_probe,
456
457 .dapm_widgets = bells_widgets,
458 .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
459 .dapm_routes = bells_routes,
460 .num_dapm_routes = ARRAY_SIZE(bells_routes),
461
462 .set_bias_level = bells_set_bias_level,
463 .set_bias_level_post = bells_set_bias_level_post,
464
465 .drvdata = &wm5110_drvdata,
466 },
467 };
468
bells_probe(struct platform_device * pdev)469 static int bells_probe(struct platform_device *pdev)
470 {
471 int ret;
472
473 bells_cards[pdev->id].dev = &pdev->dev;
474
475 ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]);
476 if (ret)
477 dev_err(&pdev->dev,
478 "snd_soc_register_card(%s) failed: %d\n",
479 bells_cards[pdev->id].name, ret);
480
481 return ret;
482 }
483
484 static struct platform_driver bells_driver = {
485 .driver = {
486 .name = "bells",
487 .pm = &snd_soc_pm_ops,
488 },
489 .probe = bells_probe,
490 };
491
492 module_platform_driver(bells_driver);
493
494 MODULE_DESCRIPTION("Bells audio support");
495 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
496 MODULE_LICENSE("GPL");
497 MODULE_ALIAS("platform:bells");
498