aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/imx
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-02-23 06:15:34 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-03-03 12:08:11 -0500
commiteeec124685164cb8f9bbf274e1d4c9b9c2f8d6c9 (patch)
tree54e91596aac9e8dd2b32a5ce0b2d05ac4ac9e350 /sound/soc/imx
parent14dc5734bdac2629ed4228f3d30662bb440a3982 (diff)
ASoC: Wolfson Microelectronics 1133-EV1 audio support
Initial support for audio using the 1133-EV1 audio and PMIC module for the i.MX31ADS. Currently only playback is supported, and the FIQ DMA driver has performance problems at higher sample rates which cause audible dropouts. This driver is based heavily on an out of tree one written by Liam Girdwood. Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/imx')
-rw-r--r--sound/soc/imx/Kconfig8
-rw-r--r--sound/soc/imx/Makefile3
-rw-r--r--sound/soc/imx/wm1133-ev1.c291
3 files changed, 302 insertions, 0 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index c7d0fd9b7de..c045da8ff61 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -11,3 +11,11 @@ config SND_IMX_SOC
11config SND_MXC_SOC_SSI 11config SND_MXC_SOC_SSI
12 tristate 12 tristate
13 13
14config SND_MXC_SOC_WM1133_EV1
15 tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted"
16 depends on SND_IMX_SOC && EXPERIMENTAL
17 select SND_SOC_WM8350
18 select SND_MXC_SOC_SSI
19 help
20 Enable support for audio on the i.MX31ADS with the WM1133-EV1
21 PMIC board with WM8835x fitted.
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index 9f8bb92ddfc..2d203635ac1 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -9,4 +9,7 @@ obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o
9 9
10# i.MX Machine Support 10# i.MX Machine Support
11snd-soc-phycore-ac97-objs := phycore-ac97.o 11snd-soc-phycore-ac97-objs := phycore-ac97.o
12snd-soc-wm1133-ev1-objs := wm1133-ev1.o
13
12obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o 14obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
15obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/imx/wm1133-ev1.c
new file mode 100644
index 00000000000..b75fcde85e8
--- /dev/null
+++ b/sound/soc/imx/wm1133-ev1.c
@@ -0,0 +1,291 @@
1/*
2 * wm1133-ev1.c - Audio for WM1133-EV1 on i.MX31ADS
3 *
4 * Copyright (c) 2010 Wolfson Microelectronics plc
5 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
6 *
7 * Based on an earlier driver for the same hardware by Liam Girdwood.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 */
14
15#include <linux/platform_device.h>
16#include <linux/clk.h>
17#include <sound/core.h>
18#include <sound/jack.h>
19#include <sound/pcm.h>
20#include <sound/pcm_params.h>
21#include <sound/soc.h>
22#include <sound/soc-dapm.h>
23
24#include <mach/audmux.h>
25
26#include "imx-ssi.h"
27#include "../codecs/wm8350.h"
28
29/* There is a silicon mic on the board optionally connected via a solder pad
30 * SP1. Define this to enable it.
31 */
32#undef USE_SIMIC
33
34struct _wm8350_audio {
35 unsigned int channels;
36 snd_pcm_format_t format;
37 unsigned int rate;
38 unsigned int sysclk;
39 unsigned int bclkdiv;
40 unsigned int clkdiv;
41 unsigned int lr_rate;
42};
43
44/* in order of power consumption per rate (lowest first) */
45static const struct _wm8350_audio wm8350_audio[] = {
46 /* 16bit mono modes */
47 {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000 >> 1,
48 WM8350_BCLK_DIV_48, WM8350_DACDIV_3, 16,},
49
50 /* 16 bit stereo modes */
51 {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
52 WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
53 {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
54 WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,},
55 {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
56 WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,},
57 {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
58 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
59 {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
60 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
61 {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
62 WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
63 {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
64 WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,},
65 {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
66 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
67 {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
68 WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
69
70 /* 24bit stereo modes */
71 {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000,
72 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
73 {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000,
74 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
75 {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600,
76 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
77 {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200,
78 WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
79};
80
81static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream,
82 struct snd_pcm_hw_params *params)
83{
84 struct snd_soc_pcm_runtime *rtd = substream->private_data;
85 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
86 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
87 int i, found = 0;
88 snd_pcm_format_t format = params_format(params);
89 unsigned int rate = params_rate(params);
90 unsigned int channels = params_channels(params);
91 u32 dai_format;
92
93 /* find the correct audio parameters */
94 for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) {
95 if (rate == wm8350_audio[i].rate &&
96 format == wm8350_audio[i].format &&
97 channels == wm8350_audio[i].channels) {
98 found = 1;
99 break;
100 }
101 }
102 if (!found)
103 return -EINVAL;
104
105 /* codec FLL input is 14.75 MHz from MCLK */
106 snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk);
107
108 dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
109 SND_SOC_DAIFMT_CBM_CFM;
110
111 /* set codec DAI configuration */
112 snd_soc_dai_set_fmt(codec_dai, dai_format);
113
114 /* set cpu DAI configuration */
115 snd_soc_dai_set_fmt(cpu_dai, dai_format);
116
117 /* TODO: The SSI driver should figure this out for us */
118 switch (channels) {
119 case 2:
120 snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
121 break;
122 case 1:
123 snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffe, 0xffffffe, 1, 0);
124 break;
125 default:
126 return -EINVAL;
127 }
128
129 /* set MCLK as the codec system clock for DAC and ADC */
130 snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_MCLK,
131 wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
132
133 /* set codec BCLK division for sample rate */
134 snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV,
135 wm8350_audio[i].bclkdiv);
136
137 /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */
138 snd_soc_dai_set_clkdiv(codec_dai,
139 WM8350_DACLR_CLKDIV, wm8350_audio[i].lr_rate);
140 snd_soc_dai_set_clkdiv(codec_dai,
141 WM8350_ADCLR_CLKDIV, wm8350_audio[i].lr_rate);
142
143 /* now configure DAC and ADC clocks */
144 snd_soc_dai_set_clkdiv(codec_dai,
145 WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv);
146
147 snd_soc_dai_set_clkdiv(codec_dai,
148 WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv);
149
150 return 0;
151}
152
153static struct snd_soc_ops wm1133_ev1_ops = {
154 .hw_params = wm1133_ev1_hw_params,
155};
156
157static const struct snd_soc_dapm_widget wm1133_ev1_widgets[] = {
158#ifdef USE_SIMIC
159 SND_SOC_DAPM_MIC("SiMIC", NULL),
160#endif
161 SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
162 SND_SOC_DAPM_MIC("Mic2 Jack", NULL),
163 SND_SOC_DAPM_LINE("Line In Jack", NULL),
164 SND_SOC_DAPM_LINE("Line Out Jack", NULL),
165 SND_SOC_DAPM_HP("Headphone Jack", NULL),
166};
167
168/* imx32ads soc_card audio map */
169static const struct snd_soc_dapm_route wm1133_ev1_map[] = {
170
171#ifdef USE_SIMIC
172 /* SiMIC --> IN1LN (with automatic bias) via SP1 */
173 { "IN1LN", NULL, "Mic Bias" },
174 { "Mic Bias", NULL, "SiMIC" },
175#endif
176
177 /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */
178 { "IN1LN", NULL, "Mic Bias" },
179 { "IN1LP", NULL, "Mic1 Jack" },
180 { "Mic Bias", NULL, "Mic1 Jack" },
181
182 /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */
183 { "IN1RN", NULL, "Mic Bias" },
184 { "IN1RP", NULL, "Mic1 Jack" },
185 { "Mic Bias", NULL, "Mic1 Jack" },
186
187 /* Line in Jack --> AUX (L+R) */
188 { "IN3R", NULL, "Line In Jack" },
189 { "IN3L", NULL, "Line In Jack" },
190
191 /* Out1 --> Headphone Jack */
192 { "Headphone Jack", NULL, "OUT1R" },
193 { "Headphone Jack", NULL, "OUT1L" },
194
195 /* Out1 --> Line Out Jack */
196 { "Line Out Jack", NULL, "OUT2R" },
197 { "Line Out Jack", NULL, "OUT2L" },
198};
199
200static struct snd_soc_jack hp_jack;
201
202static struct snd_soc_jack_pin hp_jack_pins[] = {
203 { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE },
204};
205
206static int wm1133_ev1_init(struct snd_soc_codec *codec)
207{
208 struct snd_soc_card *card = codec->socdev->card;
209
210 snd_soc_dapm_new_controls(codec, wm1133_ev1_widgets,
211 ARRAY_SIZE(wm1133_ev1_widgets));
212
213 snd_soc_dapm_add_routes(codec, wm1133_ev1_map,
214 ARRAY_SIZE(wm1133_ev1_map));
215
216 /* Headphone jack detection */
217 snd_soc_jack_new(card, "Headphone", SND_JACK_HEADPHONE, &hp_jack);
218 snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
219 hp_jack_pins);
220 wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
221
222 return 0;
223}
224
225
226static struct snd_soc_dai_link wm1133_ev1_dai = {
227 .name = "WM1133-EV1",
228 .stream_name = "Audio",
229 .cpu_dai = &imx_ssi_pcm_dai[0],
230 .codec_dai = &wm8350_dai,
231 .init = wm1133_ev1_init,
232 .ops = &wm1133_ev1_ops,
233 .symmetric_rates = 1,
234};
235
236static struct snd_soc_card wm1133_ev1 = {
237 .name = "WM1133-EV1",
238 .platform = &imx_soc_platform,
239 .dai_link = &wm1133_ev1_dai,
240 .num_links = 1,
241};
242
243static struct snd_soc_device wm1133_ev1_snd_devdata = {
244 .card = &wm1133_ev1,
245 .codec_dev = &soc_codec_dev_wm8350,
246};
247
248static struct platform_device *wm1133_ev1_snd_device;
249
250static int __init wm1133_ev1_audio_init(void)
251{
252 int ret;
253 unsigned int ptcr, pdcr;
254
255 /* SSI0 mastered by port 5 */
256 ptcr = MXC_AUDMUX_V2_PTCR_SYN |
257 MXC_AUDMUX_V2_PTCR_TFSDIR |
258 MXC_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) |
259 MXC_AUDMUX_V2_PTCR_TCLKDIR |
260 MXC_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
261 pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
262 mxc_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr);
263
264 ptcr = MXC_AUDMUX_V2_PTCR_SYN;
265 pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0);
266 mxc_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr);
267
268 wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1);
269 if (!wm1133_ev1_snd_device)
270 return -ENOMEM;
271
272 platform_set_drvdata(wm1133_ev1_snd_device, &wm1133_ev1_snd_devdata);
273 wm1133_ev1_snd_devdata.dev = &wm1133_ev1_snd_device->dev;
274 ret = platform_device_add(wm1133_ev1_snd_device);
275
276 if (ret)
277 platform_device_put(wm1133_ev1_snd_device);
278
279 return ret;
280}
281module_init(wm1133_ev1_audio_init);
282
283static void __exit wm1133_ev1_audio_exit(void)
284{
285 platform_device_unregister(wm1133_ev1_snd_device);
286}
287module_exit(wm1133_ev1_audio_exit);
288
289MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
290MODULE_DESCRIPTION("Audio for WM1133-EV1 on i.MX31ADS");
291MODULE_LICENSE("GPL");