337855a3f2fe1473d32f8e9c723769cb581c7ddf
[firefly-linux-kernel-4.4.55.git] / sound / soc / samsung / speyside.c
1 /*
2  * Speyside audio support
3  *
4  * Copyright 2011 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
17 #include "../codecs/wm8915.h"
18 #include "../codecs/wm9081.h"
19
20 #define WM8915_HPSEL_GPIO 214
21
22 static int speyside_set_bias_level(struct snd_soc_card *card,
23                                    enum snd_soc_bias_level level)
24 {
25         struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
26         int ret;
27
28         switch (level) {
29         case SND_SOC_BIAS_STANDBY:
30                 ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
31                                              32768, SND_SOC_CLOCK_IN);
32                 if (ret < 0)
33                         return ret;
34
35                 ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
36                                           0, 0, 0);
37                 if (ret < 0) {
38                         pr_err("Failed to stop FLL\n");
39                         return ret;
40                 }
41
42         default:
43                 break;
44         }
45
46         return 0;
47 }
48
49 static int speyside_hw_params(struct snd_pcm_substream *substream,
50                               struct snd_pcm_hw_params *params)
51 {
52         struct snd_soc_pcm_runtime *rtd = substream->private_data;
53         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
54         struct snd_soc_dai *codec_dai = rtd->codec_dai;
55         int ret;
56
57         ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
58                                          | SND_SOC_DAIFMT_NB_NF
59                                          | SND_SOC_DAIFMT_CBM_CFM);
60         if (ret < 0)
61                 return ret;
62
63         ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
64                                          | SND_SOC_DAIFMT_NB_NF
65                                          | SND_SOC_DAIFMT_CBM_CFM);
66         if (ret < 0)
67                 return ret;
68
69         ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1,
70                                   32768, 256 * 48000);
71         if (ret < 0)
72                 return ret;
73
74         ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL,
75                                      256 * 48000, SND_SOC_CLOCK_IN);
76         if (ret < 0)
77                 return ret;
78
79         return 0;
80 }
81
82 static struct snd_soc_ops speyside_ops = {
83         .hw_params = speyside_hw_params,
84 };
85
86 static struct snd_soc_jack speyside_headset;
87
88 /* Headset jack detection DAPM pins */
89 static struct snd_soc_jack_pin speyside_headset_pins[] = {
90         {
91                 .pin = "Headset Mic",
92                 .mask = SND_JACK_MICROPHONE,
93         },
94         {
95                 .pin = "Headphone",
96                 .mask = SND_JACK_HEADPHONE,
97         },
98 };
99
100 /* Default the headphone selection to active high */
101 static int speyside_jack_polarity;
102
103 static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
104                                 struct snd_soc_dapm_widget *sink)
105 {
106         if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
107                 return 1;
108         if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
109                 return 1;
110
111         return 0;
112 }
113
114 static void speyside_set_polarity(struct snd_soc_codec *codec,
115                                   int polarity)
116 {
117         speyside_jack_polarity = !polarity;
118         gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
119
120         /* Re-run DAPM to make sure we're using the correct mic bias */
121         snd_soc_dapm_sync(&codec->dapm);
122 }
123
124 static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
125 {
126         struct snd_soc_dai *dai = rtd->codec_dai;
127         struct snd_soc_codec *codec = rtd->codec;
128         int ret;
129
130         ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
131         if (ret < 0)
132                 return ret;
133
134         ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL");
135         if (ret != 0)
136                 pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
137         gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
138
139         ret = snd_soc_jack_new(codec, "Headset",
140                                SND_JACK_HEADSET | SND_JACK_BTN_0,
141                                &speyside_headset);
142         if (ret)
143                 return ret;
144
145         ret = snd_soc_jack_add_pins(&speyside_headset,
146                                     ARRAY_SIZE(speyside_headset_pins),
147                                     speyside_headset_pins);
148         if (ret)
149                 return ret;
150
151         wm8915_detect(codec, &speyside_headset, speyside_set_polarity);
152
153         return 0;
154 }
155
156 static int speyside_late_probe(struct snd_soc_card *card)
157 {
158         snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
159         snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
160         snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
161         snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
162         snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
163         snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
164         snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
165
166         return 0;
167 }
168
169 static struct snd_soc_dai_link speyside_dai[] = {
170         {
171                 .name = "CPU",
172                 .stream_name = "CPU",
173                 .cpu_dai_name = "samsung-i2s.0",
174                 .codec_dai_name = "wm8915-aif1",
175                 .platform_name = "samsung-audio",
176                 .codec_name = "wm8915.1-001a",
177                 .init = speyside_wm8915_init,
178                 .ops = &speyside_ops,
179         },
180         {
181                 .name = "Baseband",
182                 .stream_name = "Baseband",
183                 .cpu_dai_name = "wm8915-aif2",
184                 .codec_dai_name = "wm1250-ev1",
185                 .codec_name = "wm1250-ev1.1-0027",
186                 .platform_name = "samsung-audio",
187                 .ops = &speyside_ops,
188                 .ignore_suspend = 1,
189         },
190 };
191
192 static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
193 {
194         snd_soc_dapm_nc_pin(dapm, "LINEOUT");
195
196         /* At any time the WM9081 is active it will have this clock */
197         return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK,
198                                         48000 * 256, 0);
199 }
200
201 static struct snd_soc_aux_dev speyside_aux_dev[] = {
202         {
203                 .name = "wm9081",
204                 .codec_name = "wm9081.1-006c",
205                 .init = speyside_wm9081_init,
206         },
207 };
208
209 static struct snd_soc_codec_conf speyside_codec_conf[] = {
210         {
211                 .dev_name = "wm9081.1-006c",
212                 .name_prefix = "Sub",
213         },
214 };
215
216 static const struct snd_kcontrol_new controls[] = {
217         SOC_DAPM_PIN_SWITCH("Main Speaker"),
218         SOC_DAPM_PIN_SWITCH("Main DMIC"),
219         SOC_DAPM_PIN_SWITCH("Main AMIC"),
220         SOC_DAPM_PIN_SWITCH("WM1250 Input"),
221         SOC_DAPM_PIN_SWITCH("WM1250 Output"),
222 };
223
224 static struct snd_soc_dapm_widget widgets[] = {
225         SND_SOC_DAPM_HP("Headphone", NULL),
226         SND_SOC_DAPM_MIC("Headset Mic", NULL),
227
228         SND_SOC_DAPM_SPK("Main Speaker", NULL),
229
230         SND_SOC_DAPM_MIC("Main AMIC", NULL),
231         SND_SOC_DAPM_MIC("Main DMIC", NULL),
232 };
233
234 static struct snd_soc_dapm_route audio_paths[] = {
235         { "IN1RN", NULL, "MICB1" },
236         { "IN1RP", NULL, "MICB1" },
237         { "IN1RN", NULL, "MICB2" },
238         { "IN1RP", NULL, "MICB2" },
239         { "MICB1", NULL, "Headset Mic", speyside_get_micbias },
240         { "MICB2", NULL, "Headset Mic", speyside_get_micbias },
241
242         { "IN1LP", NULL, "MICB2" },
243         { "IN1RN", NULL, "MICB1" },
244         { "MICB2", NULL, "Main AMIC" },
245
246         { "DMIC1DAT", NULL, "MICB1" },
247         { "DMIC2DAT", NULL, "MICB1" },
248         { "MICB1", NULL, "Main DMIC" },
249
250         { "Headphone", NULL, "HPOUT1L" },
251         { "Headphone", NULL, "HPOUT1R" },
252
253         { "Sub IN1", NULL, "HPOUT2L" },
254         { "Sub IN2", NULL, "HPOUT2R" },
255
256         { "Main Speaker", NULL, "Sub SPKN" },
257         { "Main Speaker", NULL, "Sub SPKP" },
258         { "Main Speaker", NULL, "SPKDAT" },
259 };
260
261 static struct snd_soc_card speyside = {
262         .name = "Speyside",
263         .dai_link = speyside_dai,
264         .num_links = ARRAY_SIZE(speyside_dai),
265         .aux_dev = speyside_aux_dev,
266         .num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
267         .codec_conf = speyside_codec_conf,
268         .num_configs = ARRAY_SIZE(speyside_codec_conf),
269
270         .set_bias_level = speyside_set_bias_level,
271
272         .controls = controls,
273         .num_controls = ARRAY_SIZE(controls),
274         .dapm_widgets = widgets,
275         .num_dapm_widgets = ARRAY_SIZE(widgets),
276         .dapm_routes = audio_paths,
277         .num_dapm_routes = ARRAY_SIZE(audio_paths),
278
279         .late_probe = speyside_late_probe,
280 };
281
282 static __devinit int speyside_probe(struct platform_device *pdev)
283 {
284         struct snd_soc_card *card = &speyside;
285         int ret;
286
287         card->dev = &pdev->dev;
288
289         ret = snd_soc_register_card(card);
290         if (ret) {
291                 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
292                         ret);
293                 return ret;
294         }
295
296         return 0;
297 }
298
299 static int __devexit speyside_remove(struct platform_device *pdev)
300 {
301         struct snd_soc_card *card = platform_get_drvdata(pdev);
302
303         snd_soc_unregister_card(card);
304
305         return 0;
306 }
307
308 static struct platform_driver speyside_driver = {
309         .driver = {
310                 .name = "speyside",
311                 .owner = THIS_MODULE,
312                 .pm = &snd_soc_pm_ops,
313         },
314         .probe = speyside_probe,
315         .remove = __devexit_p(speyside_remove),
316 };
317
318 static int __init speyside_audio_init(void)
319 {
320         return platform_driver_register(&speyside_driver);
321 }
322 module_init(speyside_audio_init);
323
324 static void __exit speyside_audio_exit(void)
325 {
326         platform_driver_unregister(&speyside_driver);
327 }
328 module_exit(speyside_audio_exit);
329
330 MODULE_DESCRIPTION("Speyside audio support");
331 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
332 MODULE_LICENSE("GPL");
333 MODULE_ALIAS("platform:speyside");