blob: 052729c6540dfcf6609289f43b0b4551eb66dc1a [file] [log] [blame]
Chanwoo Choif51582f2010-07-22 15:16:10 +09001/*
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
31static struct snd_soc_card goni;
32static struct platform_device *goni_snd_device;
33
34/* 3.5 pie jack */
35static struct snd_soc_jack jack;
36
37/* 3.5 pie jack detection DAPM pins */
38static 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 */
50static 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
60static 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
71static 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
97static int goni_wm8994_init(struct snd_soc_pcm_runtime *rtd)
98{
99 struct snd_soc_codec *codec = rtd->codec;
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200100 struct snd_soc_dapm_context *dapm = &codec->dapm;
Chanwoo Choif51582f2010-07-22 15:16:10 +0900101 int ret;
102
103 /* add goni specific widgets */
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200104 snd_soc_dapm_new_controls(dapm, goni_dapm_widgets,
Chanwoo Choif51582f2010-07-22 15:16:10 +0900105 ARRAY_SIZE(goni_dapm_widgets));
106
107 /* set up goni specific audio routes */
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200108 snd_soc_dapm_add_routes(dapm, goni_dapm_routes,
Chanwoo Choif51582f2010-07-22 15:16:10 +0900109 ARRAY_SIZE(goni_dapm_routes));
110
111 /* set endpoints to not connected */
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200112 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");
Chanwoo Choif51582f2010-07-22 15:16:10 +0900118
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200119 snd_soc_dapm_sync(dapm);
Chanwoo Choif51582f2010-07-22 15:16:10 +0900120
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
139static 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
181static struct snd_soc_ops goni_hifi_ops = {
182 .hw_params = goni_hifi_hw_params,
183};
184
185static 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
217static 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
232static struct snd_soc_ops goni_voice_ops = {
233 .hw_params = goni_voice_hw_params,
234};
235
236static 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
257static struct snd_soc_card goni = {
258 .name = "goni",
259 .dai_link = goni_dai,
260 .num_links = ARRAY_SIZE(goni_dai),
261};
262
263static 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
288static void __exit goni_exit(void)
289{
290 platform_device_unregister(goni_snd_device);
291}
292
293module_init(goni_init);
294module_exit(goni_exit);
295
296/* Module information */
297MODULE_DESCRIPTION("ALSA SoC WM8994 GONI(S5PV210)");
298MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
299MODULE_LICENSE("GPL");