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