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