]> rtime.felk.cvut.cz Git - can-eth-gw-linux.git/blob - sound/soc/samsung/bells.c
ARM: mxs_defconfig: Improve USB related support
[can-eth-gw-linux.git] / sound / soc / samsung / bells.c
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 = "wm5102-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 = "wm5102-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");