Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2dac9217 MB |
2 | /* |
3 | * zylonite.c -- SoC audio for Zylonite | |
4 | * | |
5 | * Copyright 2008 Wolfson Microelectronics PLC. | |
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | |
2dac9217 MB |
7 | */ |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/moduleparam.h> | |
11 | #include <linux/device.h> | |
2c782f59 | 12 | #include <linux/clk.h> |
2dac9217 MB |
13 | #include <linux/i2c.h> |
14 | #include <sound/core.h> | |
15 | #include <sound/pcm.h> | |
16 | #include <sound/pcm_params.h> | |
17 | #include <sound/soc.h> | |
2dac9217 MB |
18 | |
19 | #include "../codecs/wm9713.h" | |
2dac9217 MB |
20 | #include "pxa-ssp.h" |
21 | ||
2c782f59 MB |
22 | /* |
23 | * There is a physical switch SW15 on the board which changes the MCLK | |
24 | * for the WM9713 between the standard AC97 master clock and the | |
25 | * output of the CLK_POUT signal from the PXA. | |
26 | */ | |
27 | static int clk_pout; | |
28 | module_param(clk_pout, int, 0); | |
29 | MODULE_PARM_DESC(clk_pout, "Use CLK_POUT as WM9713 MCLK (SW15 on board)."); | |
30 | ||
31 | static struct clk *pout; | |
32 | ||
2dac9217 MB |
33 | static struct snd_soc_card zylonite; |
34 | ||
35 | static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = { | |
36 | SND_SOC_DAPM_HP("Headphone", NULL), | |
37 | SND_SOC_DAPM_MIC("Headset Microphone", NULL), | |
38 | SND_SOC_DAPM_MIC("Handset Microphone", NULL), | |
39 | SND_SOC_DAPM_SPK("Multiactor", NULL), | |
40 | SND_SOC_DAPM_SPK("Headset Earpiece", NULL), | |
41 | }; | |
42 | ||
43 | /* Currently supported audio map */ | |
44 | static const struct snd_soc_dapm_route audio_map[] = { | |
45 | ||
46 | /* Headphone output connected to HPL/HPR */ | |
47 | { "Headphone", NULL, "HPL" }, | |
48 | { "Headphone", NULL, "HPR" }, | |
49 | ||
50 | /* On-board earpiece */ | |
51 | { "Headset Earpiece", NULL, "OUT3" }, | |
52 | ||
53 | /* Headphone mic */ | |
54 | { "MIC2A", NULL, "Mic Bias" }, | |
55 | { "Mic Bias", NULL, "Headset Microphone" }, | |
56 | ||
57 | /* On-board mic */ | |
58 | { "MIC1", NULL, "Mic Bias" }, | |
59 | { "Mic Bias", NULL, "Handset Microphone" }, | |
60 | ||
61 | /* Multiactor differentially connected over SPKL/SPKR */ | |
62 | { "Multiactor", NULL, "SPKL" }, | |
63 | { "Multiactor", NULL, "SPKR" }, | |
64 | }; | |
65 | ||
f0fba2ad | 66 | static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd) |
2dac9217 | 67 | { |
2c782f59 | 68 | if (clk_pout) |
f0fba2ad | 69 | snd_soc_dai_set_pll(rtd->codec_dai, 0, 0, |
85488037 | 70 | clk_get_rate(pout), 0); |
2dac9217 | 71 | |
2dac9217 MB |
72 | return 0; |
73 | } | |
74 | ||
75 | static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, | |
76 | struct snd_pcm_hw_params *params) | |
77 | { | |
78 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f0fba2ad LG |
79 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
80 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
2dac9217 MB |
81 | unsigned int wm9713_div = 0; |
82 | int ret = 0; | |
85fab780 | 83 | int rate = params_rate(params); |
85fab780 MB |
84 | |
85 | /* Only support ratios that we can generate neatly from the AC97 | |
86 | * based master clock - in particular, this excludes 44.1kHz. | |
87 | * In most applications the voice DAC will be used for telephony | |
88 | * data so multiples of 8kHz will be the common case. | |
89 | */ | |
90 | switch (rate) { | |
2dac9217 MB |
91 | case 8000: |
92 | wm9713_div = 12; | |
2dac9217 MB |
93 | break; |
94 | case 16000: | |
95 | wm9713_div = 6; | |
2dac9217 MB |
96 | break; |
97 | case 48000: | |
2dac9217 | 98 | wm9713_div = 2; |
2dac9217 | 99 | break; |
85fab780 MB |
100 | default: |
101 | /* Don't support OSS emulation */ | |
102 | return -EINVAL; | |
2dac9217 MB |
103 | } |
104 | ||
85fab780 | 105 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); |
2dac9217 MB |
106 | if (ret < 0) |
107 | return ret; | |
108 | ||
2c782f59 MB |
109 | if (clk_pout) |
110 | ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV, | |
111 | WM9713_PCMDIV(wm9713_div)); | |
112 | else | |
113 | ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV, | |
114 | WM9713_PCMDIV(wm9713_div)); | |
2dac9217 MB |
115 | if (ret < 0) |
116 | return ret; | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
943b7311 | 121 | static const struct snd_soc_ops zylonite_voice_ops = { |
2dac9217 MB |
122 | .hw_params = zylonite_voice_hw_params, |
123 | }; | |
124 | ||
1129efb0 KM |
125 | SND_SOC_DAILINK_DEFS(ac97, |
126 | DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")), | |
127 | DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-hifi")), | |
128 | DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); | |
129 | ||
130 | SND_SOC_DAILINK_DEFS(ac97_aux, | |
131 | DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")), | |
132 | DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-aux")), | |
133 | DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); | |
134 | ||
135 | SND_SOC_DAILINK_DEFS(voice, | |
136 | DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.2")), | |
137 | DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-voice")), | |
138 | DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); | |
139 | ||
2dac9217 MB |
140 | static struct snd_soc_dai_link zylonite_dai[] = { |
141 | { | |
142 | .name = "AC97", | |
143 | .stream_name = "AC97 HiFi", | |
2dac9217 | 144 | .init = zylonite_wm9713_init, |
1129efb0 | 145 | SND_SOC_DAILINK_REG(ac97), |
2dac9217 MB |
146 | }, |
147 | { | |
148 | .name = "AC97 Aux", | |
149 | .stream_name = "AC97 Aux", | |
1129efb0 | 150 | SND_SOC_DAILINK_REG(ac97_aux), |
2dac9217 MB |
151 | }, |
152 | { | |
153 | .name = "WM9713 Voice", | |
154 | .stream_name = "WM9713 Voice", | |
132c3096 LPC |
155 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
156 | SND_SOC_DAIFMT_CBS_CFS, | |
2dac9217 | 157 | .ops = &zylonite_voice_ops, |
1129efb0 | 158 | SND_SOC_DAILINK_REG(voice), |
2dac9217 MB |
159 | }, |
160 | }; | |
161 | ||
e7361ec4 | 162 | static int zylonite_probe(struct snd_soc_card *card) |
2c782f59 MB |
163 | { |
164 | int ret; | |
165 | ||
166 | if (clk_pout) { | |
167 | pout = clk_get(NULL, "CLK_POUT"); | |
168 | if (IS_ERR(pout)) { | |
eff919ac | 169 | dev_err(card->dev, "Unable to obtain CLK_POUT: %ld\n", |
2c782f59 MB |
170 | PTR_ERR(pout)); |
171 | return PTR_ERR(pout); | |
172 | } | |
173 | ||
174 | ret = clk_enable(pout); | |
175 | if (ret != 0) { | |
eff919ac | 176 | dev_err(card->dev, "Unable to enable CLK_POUT: %d\n", |
2c782f59 MB |
177 | ret); |
178 | clk_put(pout); | |
179 | return ret; | |
180 | } | |
181 | ||
eff919ac | 182 | dev_dbg(card->dev, "MCLK enabled at %luHz\n", |
2c782f59 MB |
183 | clk_get_rate(pout)); |
184 | } | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
e7361ec4 | 189 | static int zylonite_remove(struct snd_soc_card *card) |
2c782f59 MB |
190 | { |
191 | if (clk_pout) { | |
192 | clk_disable(pout); | |
193 | clk_put(pout); | |
194 | } | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
70b2ac12 | 199 | static int zylonite_suspend_post(struct snd_soc_card *card) |
2c782f59 MB |
200 | { |
201 | if (clk_pout) | |
202 | clk_disable(pout); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
70b2ac12 | 207 | static int zylonite_resume_pre(struct snd_soc_card *card) |
2c782f59 MB |
208 | { |
209 | int ret = 0; | |
210 | ||
211 | if (clk_pout) { | |
212 | ret = clk_enable(pout); | |
213 | if (ret != 0) | |
eff919ac | 214 | dev_err(card->dev, "Unable to enable CLK_POUT: %d\n", |
2c782f59 MB |
215 | ret); |
216 | } | |
217 | ||
218 | return ret; | |
219 | } | |
220 | ||
2dac9217 MB |
221 | static struct snd_soc_card zylonite = { |
222 | .name = "Zylonite", | |
561c6a17 | 223 | .owner = THIS_MODULE, |
2c782f59 MB |
224 | .probe = &zylonite_probe, |
225 | .remove = &zylonite_remove, | |
226 | .suspend_post = &zylonite_suspend_post, | |
227 | .resume_pre = &zylonite_resume_pre, | |
2dac9217 MB |
228 | .dai_link = zylonite_dai, |
229 | .num_links = ARRAY_SIZE(zylonite_dai), | |
a9576cbb LPC |
230 | |
231 | .dapm_widgets = zylonite_dapm_widgets, | |
232 | .num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets), | |
233 | .dapm_routes = audio_map, | |
234 | .num_dapm_routes = ARRAY_SIZE(audio_map), | |
2dac9217 MB |
235 | }; |
236 | ||
237 | static struct platform_device *zylonite_snd_ac97_device; | |
238 | ||
239 | static int __init zylonite_init(void) | |
240 | { | |
241 | int ret; | |
242 | ||
243 | zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1); | |
244 | if (!zylonite_snd_ac97_device) | |
245 | return -ENOMEM; | |
246 | ||
f0fba2ad | 247 | platform_set_drvdata(zylonite_snd_ac97_device, &zylonite); |
2dac9217 MB |
248 | |
249 | ret = platform_device_add(zylonite_snd_ac97_device); | |
250 | if (ret != 0) | |
251 | platform_device_put(zylonite_snd_ac97_device); | |
252 | ||
253 | return ret; | |
254 | } | |
255 | ||
256 | static void __exit zylonite_exit(void) | |
257 | { | |
258 | platform_device_unregister(zylonite_snd_ac97_device); | |
259 | } | |
260 | ||
261 | module_init(zylonite_init); | |
262 | module_exit(zylonite_exit); | |
263 | ||
264 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | |
265 | MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite"); | |
266 | MODULE_LICENSE("GPL"); |