Commit | Line | Data |
---|---|---|
f51582fd CC |
1 | /* |
2 | * goni_wm8994.c | |
3 | * | |
4 | * Copyright (C) 2010 Samsung Electronics Co.Ltd | |
5 | * Author: Chanwoo Choi <cw00.choi@samsung.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/moduleparam.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <sound/soc.h> | |
f51582fd CC |
19 | #include <sound/jack.h> |
20 | #include <asm/mach-types.h> | |
21 | #include <mach/gpio.h> | |
22 | #include <mach/regs-clock.h> | |
23 | ||
24 | #include <linux/mfd/wm8994/core.h> | |
25 | #include <linux/mfd/wm8994/registers.h> | |
26 | #include "../codecs/wm8994.h" | |
4b640cf3 | 27 | #include "dma.h" |
fcd8c742 | 28 | #include "i2s.h" |
f51582fd | 29 | |
3a56d0ca JB |
30 | #define MACHINE_NAME 0 |
31 | #define CPU_VOICE_DAI 1 | |
32 | ||
33 | static const char *aquila_str[] = { | |
34 | [MACHINE_NAME] = "aquila", | |
35 | [CPU_VOICE_DAI] = "aquila-voice-dai", | |
36 | }; | |
37 | ||
f51582fd CC |
38 | static struct snd_soc_card goni; |
39 | static struct platform_device *goni_snd_device; | |
40 | ||
41 | /* 3.5 pie jack */ | |
42 | static struct snd_soc_jack jack; | |
43 | ||
44 | /* 3.5 pie jack detection DAPM pins */ | |
45 | static struct snd_soc_jack_pin jack_pins[] = { | |
46 | { | |
47 | .pin = "Headset Mic", | |
48 | .mask = SND_JACK_MICROPHONE, | |
49 | }, { | |
50 | .pin = "Headset Stereophone", | |
51 | .mask = SND_JACK_HEADPHONE | SND_JACK_MECHANICAL | | |
52 | SND_JACK_AVOUT, | |
53 | }, | |
54 | }; | |
55 | ||
56 | /* 3.5 pie jack detection gpios */ | |
57 | static struct snd_soc_jack_gpio jack_gpios[] = { | |
58 | { | |
59 | .gpio = S5PV210_GPH0(6), | |
60 | .name = "DET_3.5", | |
61 | .report = SND_JACK_HEADSET | SND_JACK_MECHANICAL | | |
62 | SND_JACK_AVOUT, | |
63 | .debounce_time = 200, | |
64 | }, | |
65 | }; | |
66 | ||
67 | static const struct snd_soc_dapm_widget goni_dapm_widgets[] = { | |
68 | SND_SOC_DAPM_SPK("Ext Left Spk", NULL), | |
69 | SND_SOC_DAPM_SPK("Ext Right Spk", NULL), | |
70 | SND_SOC_DAPM_SPK("Ext Rcv", NULL), | |
71 | SND_SOC_DAPM_HP("Headset Stereophone", NULL), | |
72 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
73 | SND_SOC_DAPM_MIC("Main Mic", NULL), | |
74 | SND_SOC_DAPM_MIC("2nd Mic", NULL), | |
75 | SND_SOC_DAPM_LINE("Radio In", NULL), | |
76 | }; | |
77 | ||
78 | static const struct snd_soc_dapm_route goni_dapm_routes[] = { | |
79 | {"Ext Left Spk", NULL, "SPKOUTLP"}, | |
80 | {"Ext Left Spk", NULL, "SPKOUTLN"}, | |
81 | ||
82 | {"Ext Right Spk", NULL, "SPKOUTRP"}, | |
83 | {"Ext Right Spk", NULL, "SPKOUTRN"}, | |
84 | ||
85 | {"Ext Rcv", NULL, "HPOUT2N"}, | |
86 | {"Ext Rcv", NULL, "HPOUT2P"}, | |
87 | ||
88 | {"Headset Stereophone", NULL, "HPOUT1L"}, | |
89 | {"Headset Stereophone", NULL, "HPOUT1R"}, | |
90 | ||
91 | {"IN1RN", NULL, "Headset Mic"}, | |
92 | {"IN1RP", NULL, "Headset Mic"}, | |
93 | ||
94 | {"IN1RN", NULL, "2nd Mic"}, | |
95 | {"IN1RP", NULL, "2nd Mic"}, | |
96 | ||
97 | {"IN1LN", NULL, "Main Mic"}, | |
98 | {"IN1LP", NULL, "Main Mic"}, | |
99 | ||
100 | {"IN2LN", NULL, "Radio In"}, | |
101 | {"IN2RN", NULL, "Radio In"}, | |
102 | }; | |
103 | ||
104 | static int goni_wm8994_init(struct snd_soc_pcm_runtime *rtd) | |
105 | { | |
106 | struct snd_soc_codec *codec = rtd->codec; | |
ce6120cc | 107 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
f51582fd CC |
108 | int ret; |
109 | ||
110 | /* add goni specific widgets */ | |
ce6120cc | 111 | snd_soc_dapm_new_controls(dapm, goni_dapm_widgets, |
f51582fd CC |
112 | ARRAY_SIZE(goni_dapm_widgets)); |
113 | ||
114 | /* set up goni specific audio routes */ | |
ce6120cc | 115 | snd_soc_dapm_add_routes(dapm, goni_dapm_routes, |
f51582fd CC |
116 | ARRAY_SIZE(goni_dapm_routes)); |
117 | ||
118 | /* set endpoints to not connected */ | |
ce6120cc LG |
119 | snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN"); |
120 | snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP"); | |
121 | snd_soc_dapm_nc_pin(dapm, "LINEOUT1N"); | |
122 | snd_soc_dapm_nc_pin(dapm, "LINEOUT1P"); | |
123 | snd_soc_dapm_nc_pin(dapm, "LINEOUT2N"); | |
124 | snd_soc_dapm_nc_pin(dapm, "LINEOUT2P"); | |
125 | ||
3a56d0ca JB |
126 | if (machine_is_aquila()) { |
127 | snd_soc_dapm_nc_pin(dapm, "SPKOUTRN"); | |
128 | snd_soc_dapm_nc_pin(dapm, "SPKOUTRP"); | |
129 | } | |
130 | ||
ce6120cc | 131 | snd_soc_dapm_sync(dapm); |
f51582fd CC |
132 | |
133 | /* Headset jack detection */ | |
134 | ret = snd_soc_jack_new(&goni, "Headset Jack", | |
135 | SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT, | |
136 | &jack); | |
137 | if (ret) | |
138 | return ret; | |
139 | ||
140 | ret = snd_soc_jack_add_pins(&jack, ARRAY_SIZE(jack_pins), jack_pins); | |
141 | if (ret) | |
142 | return ret; | |
143 | ||
144 | ret = snd_soc_jack_add_gpios(&jack, ARRAY_SIZE(jack_gpios), jack_gpios); | |
145 | if (ret) | |
146 | return ret; | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | static int goni_hifi_hw_params(struct snd_pcm_substream *substream, | |
152 | struct snd_pcm_hw_params *params) | |
153 | { | |
154 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
155 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
156 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
157 | unsigned int pll_out = 24000000; | |
158 | int ret = 0; | |
159 | ||
160 | /* set the cpu DAI configuration */ | |
161 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | |
162 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | |
163 | if (ret < 0) | |
164 | return ret; | |
165 | ||
f51582fd CC |
166 | /* set codec DAI configuration */ |
167 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | |
168 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | |
169 | if (ret < 0) | |
170 | return ret; | |
171 | ||
172 | /* set the codec FLL */ | |
173 | ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, 0, pll_out, | |
174 | params_rate(params) * 256); | |
175 | if (ret < 0) | |
176 | return ret; | |
177 | ||
178 | /* set the codec system clock */ | |
179 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, | |
180 | params_rate(params) * 256, SND_SOC_CLOCK_IN); | |
181 | if (ret < 0) | |
182 | return ret; | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
187 | static struct snd_soc_ops goni_hifi_ops = { | |
188 | .hw_params = goni_hifi_hw_params, | |
189 | }; | |
190 | ||
191 | static int goni_voice_hw_params(struct snd_pcm_substream *substream, | |
192 | struct snd_pcm_hw_params *params) | |
193 | { | |
194 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
195 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
196 | unsigned int pll_out = 24000000; | |
197 | int ret = 0; | |
198 | ||
199 | if (params_rate(params) != 8000) | |
200 | return -EINVAL; | |
201 | ||
202 | /* set codec DAI configuration */ | |
203 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | | |
204 | SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); | |
205 | if (ret < 0) | |
206 | return ret; | |
207 | ||
208 | /* set the codec FLL */ | |
209 | ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, 0, pll_out, | |
210 | params_rate(params) * 256); | |
211 | if (ret < 0) | |
212 | return ret; | |
213 | ||
214 | /* set the codec system clock */ | |
215 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2, | |
216 | params_rate(params) * 256, SND_SOC_CLOCK_IN); | |
217 | if (ret < 0) | |
218 | return ret; | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static struct snd_soc_dai_driver voice_dai = { | |
224 | .name = "goni-voice-dai", | |
225 | .id = 0, | |
226 | .playback = { | |
227 | .channels_min = 1, | |
228 | .channels_max = 2, | |
229 | .rates = SNDRV_PCM_RATE_8000, | |
230 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
231 | .capture = { | |
232 | .channels_min = 1, | |
233 | .channels_max = 2, | |
234 | .rates = SNDRV_PCM_RATE_8000, | |
235 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
236 | }; | |
237 | ||
238 | static struct snd_soc_ops goni_voice_ops = { | |
239 | .hw_params = goni_voice_hw_params, | |
240 | }; | |
241 | ||
242 | static struct snd_soc_dai_link goni_dai[] = { | |
243 | { | |
244 | .name = "WM8994", | |
245 | .stream_name = "WM8994 HiFi", | |
fcd8c742 | 246 | .cpu_dai_name = "samsung-i2s.0", |
f51582fd | 247 | .codec_dai_name = "wm8994-hifi", |
58bb4072 | 248 | .platform_name = "samsung-audio", |
f51582fd CC |
249 | .codec_name = "wm8994-codec.0-0x1a", |
250 | .init = goni_wm8994_init, | |
251 | .ops = &goni_hifi_ops, | |
252 | }, { | |
253 | .name = "WM8994 Voice", | |
254 | .stream_name = "Voice", | |
255 | .cpu_dai_name = "goni-voice-dai", | |
256 | .codec_dai_name = "wm8994-voice", | |
58bb4072 | 257 | .platform_name = "samsung-audio", |
f51582fd CC |
258 | .codec_name = "wm8994-codec.0-0x1a", |
259 | .ops = &goni_voice_ops, | |
260 | }, | |
261 | }; | |
262 | ||
263 | static struct snd_soc_card goni = { | |
264 | .name = "goni", | |
265 | .dai_link = goni_dai, | |
266 | .num_links = ARRAY_SIZE(goni_dai), | |
267 | }; | |
268 | ||
269 | static int __init goni_init(void) | |
270 | { | |
271 | int ret; | |
272 | ||
3a56d0ca JB |
273 | if (machine_is_aquila()) { |
274 | voice_dai.name = aquila_str[CPU_VOICE_DAI]; | |
275 | goni_dai[1].cpu_dai_name = aquila_str[CPU_VOICE_DAI]; | |
276 | goni.name = aquila_str[MACHINE_NAME]; | |
277 | } else if (!machine_is_goni()) | |
f51582fd CC |
278 | return -ENODEV; |
279 | ||
280 | goni_snd_device = platform_device_alloc("soc-audio", -1); | |
281 | if (!goni_snd_device) | |
282 | return -ENOMEM; | |
283 | ||
284 | /* register voice DAI here */ | |
285 | ret = snd_soc_register_dai(&goni_snd_device->dev, &voice_dai); | |
286 | if (ret) | |
287 | return ret; | |
288 | ||
289 | platform_set_drvdata(goni_snd_device, &goni); | |
290 | ret = platform_device_add(goni_snd_device); | |
291 | ||
292 | if (ret) | |
293 | platform_device_put(goni_snd_device); | |
294 | ||
295 | return ret; | |
296 | } | |
297 | ||
298 | static void __exit goni_exit(void) | |
299 | { | |
300 | platform_device_unregister(goni_snd_device); | |
301 | } | |
302 | ||
303 | module_init(goni_init); | |
304 | module_exit(goni_exit); | |
305 | ||
306 | /* Module information */ | |
307 | MODULE_DESCRIPTION("ALSA SoC WM8994 GONI(S5PV210)"); | |
308 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | |
309 | MODULE_LICENSE("GPL"); |