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