xref: /openbmc/linux/sound/soc/samsung/speyside.c (revision 7fe2f639)
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");
333