xref: /linux/sound/soc/samsung/bells.c (revision f49f4ab95c301dbccad0efe85296d908b8ae7ad4)
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 /*
22  * 44.1kHz based clocks for the SYSCLK domain, use a very high clock
23  * to allow all the DSP functionality to be enabled if desired.
24  */
25 #define SYSCLK_RATE (44100 * 1024)
26 
27 /* 48kHz based clocks for the ASYNC domain */
28 #define ASYNCCLK_RATE (48000 * 512)
29 
30 /* BCLK2 is fixed at this currently */
31 #define BCLK2_RATE (64 * 8000)
32 
33 /*
34  * Expect a 24.576MHz crystal if one is fitted (the driver will function
35  * if this is not fitted).
36  */
37 #define MCLK_RATE 24576000
38 
39 #define WM9081_AUDIO_RATE 44100
40 #define WM9081_MCLK_RATE  (WM9081_AUDIO_RATE * 256)
41 
42 static int bells_set_bias_level(struct snd_soc_card *card,
43 				struct snd_soc_dapm_context *dapm,
44 				enum snd_soc_bias_level level)
45 {
46 	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
47 	struct snd_soc_codec *codec = codec_dai->codec;
48 	int ret;
49 
50 	if (dapm->dev != codec_dai->dev)
51 		return 0;
52 
53 	switch (level) {
54 	case SND_SOC_BIAS_PREPARE:
55 		if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
56 			ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
57 						    ARIZONA_FLL_SRC_MCLK1,
58 						    MCLK_RATE,
59 						    SYSCLK_RATE);
60 			if (ret < 0)
61 				pr_err("Failed to start FLL: %d\n", ret);
62 
63 			ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
64 						    ARIZONA_FLL_SRC_AIF2BCLK,
65 						    BCLK2_RATE,
66 						    ASYNCCLK_RATE);
67 			if (ret < 0)
68 				pr_err("Failed to start FLL: %d\n", ret);
69 		}
70 		break;
71 
72 	default:
73 		break;
74 	}
75 
76 	return 0;
77 }
78 
79 static int bells_set_bias_level_post(struct snd_soc_card *card,
80 				     struct snd_soc_dapm_context *dapm,
81 				     enum snd_soc_bias_level level)
82 {
83 	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
84 	struct snd_soc_codec *codec = codec_dai->codec;
85 	int ret;
86 
87 	if (dapm->dev != codec_dai->dev)
88 		return 0;
89 
90 	switch (level) {
91 	case SND_SOC_BIAS_STANDBY:
92 		ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
93 		if (ret < 0) {
94 			pr_err("Failed to stop FLL: %d\n", ret);
95 			return ret;
96 		}
97 
98 		ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 0, 0, 0);
99 		if (ret < 0) {
100 			pr_err("Failed to stop FLL: %d\n", ret);
101 			return ret;
102 		}
103 		break;
104 
105 	default:
106 		break;
107 	}
108 
109 	dapm->bias_level = level;
110 
111 	return 0;
112 }
113 
114 static int bells_late_probe(struct snd_soc_card *card)
115 {
116 	struct snd_soc_codec *codec = card->rtd[0].codec;
117 	struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
118 	struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
119 	struct snd_soc_dai *aif3_dai = card->rtd[2].cpu_dai;
120 	struct snd_soc_dai *wm9081_dai = card->rtd[2].codec_dai;
121 	int ret;
122 
123 	ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
124 	if (ret != 0) {
125 		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
126 		return ret;
127 	}
128 
129 	ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
130 	if (ret != 0) {
131 		dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
132 		return ret;
133 	}
134 
135 	ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
136 	if (ret != 0) {
137 		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
138 		return ret;
139 	}
140 
141 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
142 				       ARIZONA_CLK_SRC_FLL1, SYSCLK_RATE,
143 				       SND_SOC_CLOCK_IN);
144 	if (ret != 0) {
145 		dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
146 		return ret;
147 	}
148 
149 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
150 				       WM9081_MCLK_RATE, SND_SOC_CLOCK_OUT);
151 	if (ret != 0) {
152 		dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
153 		return ret;
154 	}
155 
156 	ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
157 				       ARIZONA_CLK_SRC_FLL2, ASYNCCLK_RATE,
158 				       SND_SOC_CLOCK_IN);
159 	if (ret != 0) {
160 		dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
161 		return ret;
162 	}
163 
164 	ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
165 				       0, WM9081_MCLK_RATE, 0);
166 	if (ret != 0) {
167 		dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
168 		return ret;
169 	}
170 
171 	return 0;
172 }
173 
174 static const struct snd_soc_pcm_stream baseband_params = {
175 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
176 	.rate_min = 8000,
177 	.rate_max = 8000,
178 	.channels_min = 2,
179 	.channels_max = 2,
180 };
181 
182 static const struct snd_soc_pcm_stream sub_params = {
183 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
184 	.rate_min = WM9081_AUDIO_RATE,
185 	.rate_max = WM9081_AUDIO_RATE,
186 	.channels_min = 2,
187 	.channels_max = 2,
188 };
189 
190 static struct snd_soc_dai_link bells_dai_wm5102[] = {
191 	{
192 		.name = "CPU",
193 		.stream_name = "CPU",
194 		.cpu_dai_name = "samsung-i2s.0",
195 		.codec_dai_name = "wm5102-aif1",
196 		.platform_name = "samsung-audio",
197 		.codec_name = "wm5102-codec",
198 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
199 				| SND_SOC_DAIFMT_CBM_CFM,
200 	},
201 	{
202 		.name = "Baseband",
203 		.stream_name = "Baseband",
204 		.cpu_dai_name = "wm5102-aif2",
205 		.codec_dai_name = "wm1250-ev1",
206 		.codec_name = "wm1250-ev1.1-0027",
207 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
208 				| SND_SOC_DAIFMT_CBM_CFM,
209 		.ignore_suspend = 1,
210 		.params = &baseband_params,
211 	},
212 	{
213 		.name = "Sub",
214 		.stream_name = "Sub",
215 		.cpu_dai_name = "wm5110-aif3",
216 		.codec_dai_name = "wm9081-hifi",
217 		.codec_name = "wm9081.1-006c",
218 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
219 				| SND_SOC_DAIFMT_CBS_CFS,
220 		.ignore_suspend = 1,
221 		.params = &sub_params,
222 	},
223 };
224 
225 static struct snd_soc_dai_link bells_dai_wm5110[] = {
226 	{
227 		.name = "CPU",
228 		.stream_name = "CPU",
229 		.cpu_dai_name = "samsung-i2s.0",
230 		.codec_dai_name = "wm5110-aif1",
231 		.platform_name = "samsung-audio",
232 		.codec_name = "wm5110-codec",
233 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
234 				| SND_SOC_DAIFMT_CBM_CFM,
235 	},
236 	{
237 		.name = "Baseband",
238 		.stream_name = "Baseband",
239 		.cpu_dai_name = "wm5110-aif2",
240 		.codec_dai_name = "wm1250-ev1",
241 		.codec_name = "wm1250-ev1.1-0027",
242 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
243 				| SND_SOC_DAIFMT_CBM_CFM,
244 		.ignore_suspend = 1,
245 		.params = &baseband_params,
246 	},
247 	{
248 		.name = "Sub",
249 		.stream_name = "Sub",
250 		.cpu_dai_name = "wm5110-aif3",
251 		.codec_dai_name = "wm9081-hifi",
252 		.codec_name = "wm9081.1-006c",
253 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
254 				| SND_SOC_DAIFMT_CBS_CFS,
255 		.ignore_suspend = 1,
256 		.params = &sub_params,
257 	},
258 };
259 
260 static struct snd_soc_codec_conf bells_codec_conf[] = {
261 	{
262 		.dev_name = "wm9081.1-006c",
263 		.name_prefix = "Sub",
264 	},
265 };
266 
267 static struct snd_soc_dapm_route bells_routes[] = {
268 	{ "Sub CLK_SYS", NULL, "OPCLK" },
269 };
270 
271 static struct snd_soc_card bells_cards[] = {
272 	{
273 		.name = "Bells WM5102",
274 		.owner = THIS_MODULE,
275 		.dai_link = bells_dai_wm5102,
276 		.num_links = ARRAY_SIZE(bells_dai_wm5102),
277 		.codec_conf = bells_codec_conf,
278 		.num_configs = ARRAY_SIZE(bells_codec_conf),
279 
280 		.late_probe = bells_late_probe,
281 
282 		.dapm_routes = bells_routes,
283 		.num_dapm_routes = ARRAY_SIZE(bells_routes),
284 
285 		.set_bias_level = bells_set_bias_level,
286 		.set_bias_level_post = bells_set_bias_level_post,
287 	},
288 	{
289 		.name = "Bells WM5110",
290 		.owner = THIS_MODULE,
291 		.dai_link = bells_dai_wm5110,
292 		.num_links = ARRAY_SIZE(bells_dai_wm5110),
293 		.codec_conf = bells_codec_conf,
294 		.num_configs = ARRAY_SIZE(bells_codec_conf),
295 
296 		.late_probe = bells_late_probe,
297 
298 		.dapm_routes = bells_routes,
299 		.num_dapm_routes = ARRAY_SIZE(bells_routes),
300 
301 		.set_bias_level = bells_set_bias_level,
302 		.set_bias_level_post = bells_set_bias_level_post,
303 	},
304 };
305 
306 
307 static __devinit int bells_probe(struct platform_device *pdev)
308 {
309 	int ret;
310 
311 	bells_cards[pdev->id].dev = &pdev->dev;
312 
313 	ret = snd_soc_register_card(&bells_cards[pdev->id]);
314 	if (ret) {
315 		dev_err(&pdev->dev,
316 			"snd_soc_register_card(%s) failed: %d\n",
317 			bells_cards[pdev->id].name, ret);
318 		return ret;
319 	}
320 
321 	return 0;
322 }
323 
324 static int __devexit bells_remove(struct platform_device *pdev)
325 {
326 	snd_soc_unregister_card(&bells_cards[pdev->id]);
327 
328 	return 0;
329 }
330 
331 static struct platform_driver bells_driver = {
332 	.driver = {
333 		.name = "bells",
334 		.owner = THIS_MODULE,
335 		.pm = &snd_soc_pm_ops,
336 	},
337 	.probe = bells_probe,
338 	.remove = __devexit_p(bells_remove),
339 };
340 
341 module_platform_driver(bells_driver);
342 
343 MODULE_DESCRIPTION("Bells audio support");
344 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
345 MODULE_LICENSE("GPL");
346 MODULE_ALIAS("platform:bells");
347