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