GNU Linux-libre 4.14.290-gnu1
[releases.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 /* 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 static int bells_probe(struct platform_device *pdev)
450 {
451         int ret;
452
453         bells_cards[pdev->id].dev = &pdev->dev;
454
455         ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]);
456         if (ret)
457                 dev_err(&pdev->dev,
458                         "snd_soc_register_card(%s) failed: %d\n",
459                         bells_cards[pdev->id].name, ret);
460
461         return ret;
462 }
463
464 static struct platform_driver bells_driver = {
465         .driver = {
466                 .name = "bells",
467                 .pm = &snd_soc_pm_ops,
468         },
469         .probe = bells_probe,
470 };
471
472 module_platform_driver(bells_driver);
473
474 MODULE_DESCRIPTION("Bells audio support");
475 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
476 MODULE_LICENSE("GPL");
477 MODULE_ALIAS("platform:bells");