e171a321889fbf6d6454544fb7278a8b6621d56e
[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 struct snd_soc_dai_link speyside_dai[] = {
157         {
158                 .name = "CPU",
159                 .stream_name = "CPU",
160                 .cpu_dai_name = "samsung-i2s.0",
161                 .codec_dai_name = "wm8915-aif1",
162                 .platform_name = "samsung-audio",
163                 .codec_name = "wm8915.1-001a",
164                 .init = speyside_wm8915_init,
165                 .ops = &speyside_ops,
166         },
167 };
168
169 static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
170 {
171         snd_soc_dapm_nc_pin(dapm, "LINEOUT");
172
173         /* At any time the WM9081 is active it will have this clock */
174         return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK,
175                                         48000 * 256, 0);
176 }
177
178 static struct snd_soc_aux_dev speyside_aux_dev[] = {
179         {
180                 .name = "wm9081",
181                 .codec_name = "wm9081.1-006c",
182                 .init = speyside_wm9081_init,
183         },
184 };
185
186 static struct snd_soc_codec_conf speyside_codec_conf[] = {
187         {
188                 .dev_name = "wm9081.1-006c",
189                 .name_prefix = "Sub",
190         },
191 };
192
193 static const struct snd_kcontrol_new controls[] = {
194         SOC_DAPM_PIN_SWITCH("Main Speaker"),
195         SOC_DAPM_PIN_SWITCH("Main DMIC"),
196         SOC_DAPM_PIN_SWITCH("Main AMIC"),
197 };
198
199 static struct snd_soc_dapm_widget widgets[] = {
200         SND_SOC_DAPM_HP("Headphone", NULL),
201         SND_SOC_DAPM_MIC("Headset Mic", NULL),
202
203         SND_SOC_DAPM_SPK("Main Speaker", NULL),
204
205         SND_SOC_DAPM_MIC("Main AMIC", NULL),
206         SND_SOC_DAPM_MIC("Main DMIC", NULL),
207 };
208
209 static struct snd_soc_dapm_route audio_paths[] = {
210         { "IN1RN", NULL, "MICB1" },
211         { "IN1RP", NULL, "MICB1" },
212         { "IN1RN", NULL, "MICB2" },
213         { "IN1RP", NULL, "MICB2" },
214         { "MICB1", NULL, "Headset Mic", speyside_get_micbias },
215         { "MICB2", NULL, "Headset Mic", speyside_get_micbias },
216
217         { "IN1LP", NULL, "MICB2" },
218         { "IN1RN", NULL, "MICB1" },
219         { "MICB2", NULL, "Main AMIC" },
220
221         { "DMIC1DAT", NULL, "MICB1" },
222         { "DMIC2DAT", NULL, "MICB1" },
223         { "MICB1", NULL, "Main DMIC" },
224
225         { "Headphone", NULL, "HPOUT1L" },
226         { "Headphone", NULL, "HPOUT1R" },
227
228         { "Sub IN1", NULL, "HPOUT2L" },
229         { "Sub IN2", NULL, "HPOUT2R" },
230
231         { "Main Speaker", NULL, "Sub SPKN" },
232         { "Main Speaker", NULL, "Sub SPKP" },
233         { "Main Speaker", NULL, "SPKDAT" },
234 };
235
236 static struct snd_soc_card speyside = {
237         .name = "Speyside",
238         .dai_link = speyside_dai,
239         .num_links = ARRAY_SIZE(speyside_dai),
240         .aux_dev = speyside_aux_dev,
241         .num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
242         .codec_conf = speyside_codec_conf,
243         .num_configs = ARRAY_SIZE(speyside_codec_conf),
244
245         .set_bias_level = speyside_set_bias_level,
246
247         .controls = controls,
248         .num_controls = ARRAY_SIZE(controls),
249         .dapm_widgets = widgets,
250         .num_dapm_widgets = ARRAY_SIZE(widgets),
251         .dapm_routes = audio_paths,
252         .num_dapm_routes = ARRAY_SIZE(audio_paths),
253 };
254
255 static __devinit int speyside_probe(struct platform_device *pdev)
256 {
257         struct snd_soc_card *card = &speyside;
258         int ret;
259
260         card->dev = &pdev->dev;
261
262         ret = snd_soc_register_card(card);
263         if (ret) {
264                 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
265                         ret);
266                 return ret;
267         }
268
269         return 0;
270 }
271
272 static int __devexit speyside_remove(struct platform_device *pdev)
273 {
274         struct snd_soc_card *card = platform_get_drvdata(pdev);
275
276         snd_soc_unregister_card(card);
277
278         return 0;
279 }
280
281 static struct platform_driver speyside_driver = {
282         .driver = {
283                 .name = "speyside",
284                 .owner = THIS_MODULE,
285                 .pm = &snd_soc_pm_ops,
286         },
287         .probe = speyside_probe,
288         .remove = __devexit_p(speyside_remove),
289 };
290
291 static int __init speyside_audio_init(void)
292 {
293         return platform_driver_register(&speyside_driver);
294 }
295 module_init(speyside_audio_init);
296
297 static void __exit speyside_audio_exit(void)
298 {
299         platform_driver_unregister(&speyside_driver);
300 }
301 module_exit(speyside_audio_exit);
302
303 MODULE_DESCRIPTION("Speyside audio support");
304 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
305 MODULE_LICENSE("GPL");
306 MODULE_ALIAS("platform:speyside");