xref: /linux/sound/soc/samsung/bells.c (revision 177bf8620cf4ed290ee170a6c5966adc0924b336)
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