diff options
Diffstat (limited to 'sound/soc/pxa')
-rw-r--r-- | sound/soc/pxa/Kconfig | 60 | ||||
-rw-r--r-- | sound/soc/pxa/Makefile | 20 | ||||
-rw-r--r-- | sound/soc/pxa/corgi.c | 383 | ||||
-rw-r--r-- | sound/soc/pxa/poodle.c | 352 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-ac97.c | 431 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-ac97.h | 22 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-i2s.c | 318 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-i2s.h | 20 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-pcm.c | 372 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-pcm.h | 34 | ||||
-rw-r--r-- | sound/soc/pxa/spitz.c | 394 | ||||
-rw-r--r-- | sound/soc/pxa/tosa.c | 289 |
12 files changed, 2695 insertions, 0 deletions
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig new file mode 100644 index 000000000000..579e1c8d2b28 --- /dev/null +++ b/sound/soc/pxa/Kconfig | |||
@@ -0,0 +1,60 @@ | |||
1 | menu "SoC Audio for the Intel PXA2xx" | ||
2 | |||
3 | config SND_PXA2XX_SOC | ||
4 | tristate "SoC Audio for the Intel PXA2xx chip" | ||
5 | depends on ARCH_PXA && SND | ||
6 | select SND_PCM | ||
7 | help | ||
8 | Say Y or M if you want to add support for codecs attached to | ||
9 | the PXA2xx AC97, I2S or SSP interface. You will also need | ||
10 | to select the audio interfaces to support below. | ||
11 | |||
12 | config SND_PXA2XX_AC97 | ||
13 | tristate | ||
14 | select SND_AC97_CODEC | ||
15 | |||
16 | config SND_PXA2XX_SOC_AC97 | ||
17 | tristate | ||
18 | select AC97_BUS | ||
19 | select SND_SOC_AC97_BUS | ||
20 | |||
21 | config SND_PXA2XX_SOC_I2S | ||
22 | tristate | ||
23 | |||
24 | config SND_PXA2XX_SOC_CORGI | ||
25 | tristate "SoC Audio support for Sharp Zaurus SL-C7x0" | ||
26 | depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx | ||
27 | select SND_PXA2XX_SOC_I2S | ||
28 | select SND_SOC_WM8731 | ||
29 | help | ||
30 | Say Y if you want to add support for SoC audio on Sharp | ||
31 | Zaurus SL-C7x0 models (Corgi, Shepherd, Husky). | ||
32 | |||
33 | config SND_PXA2XX_SOC_SPITZ | ||
34 | tristate "SoC Audio support for Sharp Zaurus SL-Cxx00" | ||
35 | depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00 | ||
36 | select SND_PXA2XX_SOC_I2S | ||
37 | select SND_SOC_WM8750 | ||
38 | help | ||
39 | Say Y if you want to add support for SoC audio on Sharp | ||
40 | Zaurus SL-Cxx00 models (Spitz, Borzoi and Akita). | ||
41 | |||
42 | config SND_PXA2XX_SOC_POODLE | ||
43 | tristate "SoC Audio support for Poodle" | ||
44 | depends on SND_PXA2XX_SOC && MACH_POODLE | ||
45 | select SND_PXA2XX_SOC_I2S | ||
46 | select SND_SOC_WM8731 | ||
47 | help | ||
48 | Say Y if you want to add support for SoC audio on Sharp | ||
49 | Zaurus SL-5600 model (Poodle). | ||
50 | |||
51 | config SND_PXA2XX_SOC_TOSA | ||
52 | tristate "SoC AC97 Audio support for Tosa" | ||
53 | depends on SND_PXA2XX_SOC && MACH_TOSA | ||
54 | select SND_PXA2XX_SOC_AC97 | ||
55 | select SND_SOC_WM9712 | ||
56 | help | ||
57 | Say Y if you want to add support for SoC audio on Sharp | ||
58 | Zaurus SL-C6000x models (Tosa). | ||
59 | |||
60 | endmenu | ||
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile new file mode 100644 index 000000000000..78e0d6b07d1d --- /dev/null +++ b/sound/soc/pxa/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | # PXA Platform Support | ||
2 | snd-soc-pxa2xx-objs := pxa2xx-pcm.o | ||
3 | snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o | ||
4 | snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o | ||
5 | |||
6 | obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o | ||
7 | obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o | ||
8 | obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o | ||
9 | |||
10 | # PXA Machine Support | ||
11 | snd-soc-corgi-objs := corgi.o | ||
12 | snd-soc-poodle-objs := poodle.o | ||
13 | snd-soc-tosa-objs := tosa.o | ||
14 | snd-soc-spitz-objs := spitz.o | ||
15 | |||
16 | obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o | ||
17 | obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o | ||
18 | obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o | ||
19 | obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o | ||
20 | |||
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c new file mode 100644 index 000000000000..5ee51a994ac3 --- /dev/null +++ b/sound/soc/pxa/corgi.c | |||
@@ -0,0 +1,383 @@ | |||
1 | /* | ||
2 | * corgi.c -- SoC audio for Corgi | ||
3 | * | ||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | ||
5 | * Copyright 2005 Openedhand Ltd. | ||
6 | * | ||
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | ||
8 | * Richard Purdie <richard@openedhand.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * Revision history | ||
16 | * 30th Nov 2005 Initial version. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/timer.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <sound/driver.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/soc.h> | ||
29 | #include <sound/soc-dapm.h> | ||
30 | |||
31 | #include <asm/mach-types.h> | ||
32 | #include <asm/hardware/scoop.h> | ||
33 | #include <asm/arch/pxa-regs.h> | ||
34 | #include <asm/arch/hardware.h> | ||
35 | #include <asm/arch/corgi.h> | ||
36 | #include <asm/arch/audio.h> | ||
37 | |||
38 | #include "../codecs/wm8731.h" | ||
39 | #include "pxa2xx-pcm.h" | ||
40 | #include "pxa2xx-i2s.h" | ||
41 | |||
42 | #define CORGI_HP 0 | ||
43 | #define CORGI_MIC 1 | ||
44 | #define CORGI_LINE 2 | ||
45 | #define CORGI_HEADSET 3 | ||
46 | #define CORGI_HP_OFF 4 | ||
47 | #define CORGI_SPK_ON 0 | ||
48 | #define CORGI_SPK_OFF 1 | ||
49 | |||
50 | /* audio clock in Hz - rounded from 12.235MHz */ | ||
51 | #define CORGI_AUDIO_CLOCK 12288000 | ||
52 | |||
53 | static int corgi_jack_func; | ||
54 | static int corgi_spk_func; | ||
55 | |||
56 | static void corgi_ext_control(struct snd_soc_codec *codec) | ||
57 | { | ||
58 | int spk = 0, mic = 0, line = 0, hp = 0, hs = 0; | ||
59 | |||
60 | /* set up jack connection */ | ||
61 | switch (corgi_jack_func) { | ||
62 | case CORGI_HP: | ||
63 | hp = 1; | ||
64 | /* set = unmute headphone */ | ||
65 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | ||
66 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | ||
67 | break; | ||
68 | case CORGI_MIC: | ||
69 | mic = 1; | ||
70 | /* reset = mute headphone */ | ||
71 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | ||
72 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | ||
73 | break; | ||
74 | case CORGI_LINE: | ||
75 | line = 1; | ||
76 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | ||
77 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | ||
78 | break; | ||
79 | case CORGI_HEADSET: | ||
80 | hs = 1; | ||
81 | mic = 1; | ||
82 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | ||
83 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | ||
84 | break; | ||
85 | } | ||
86 | |||
87 | if (corgi_spk_func == CORGI_SPK_ON) | ||
88 | spk = 1; | ||
89 | |||
90 | /* set the enpoints to their new connetion states */ | ||
91 | snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk); | ||
92 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic); | ||
93 | snd_soc_dapm_set_endpoint(codec, "Line Jack", line); | ||
94 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp); | ||
95 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs); | ||
96 | |||
97 | /* signal a DAPM event */ | ||
98 | snd_soc_dapm_sync_endpoints(codec); | ||
99 | } | ||
100 | |||
101 | static int corgi_startup(struct snd_pcm_substream *substream) | ||
102 | { | ||
103 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
104 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
105 | |||
106 | /* check the jack status at stream startup */ | ||
107 | corgi_ext_control(codec); | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | /* we need to unmute the HP at shutdown as the mute burns power on corgi */ | ||
112 | static int corgi_shutdown(struct snd_pcm_substream *substream) | ||
113 | { | ||
114 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
115 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
116 | |||
117 | /* set = unmute headphone */ | ||
118 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | ||
119 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int corgi_hw_params(struct snd_pcm_substream *substream, | ||
124 | struct snd_pcm_hw_params *params) | ||
125 | { | ||
126 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
127 | struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
128 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
129 | unsigned int clk = 0; | ||
130 | int ret = 0; | ||
131 | |||
132 | switch (params_rate(params)) { | ||
133 | case 8000: | ||
134 | case 16000: | ||
135 | case 48000: | ||
136 | case 96000: | ||
137 | clk = 12288000; | ||
138 | break; | ||
139 | case 11025: | ||
140 | case 22050: | ||
141 | case 44100: | ||
142 | clk = 11289600; | ||
143 | break; | ||
144 | } | ||
145 | |||
146 | /* set codec DAI configuration */ | ||
147 | ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
148 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
149 | if (ret < 0) | ||
150 | return ret; | ||
151 | |||
152 | /* set cpu DAI configuration */ | ||
153 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
154 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
155 | if (ret < 0) | ||
156 | return ret; | ||
157 | |||
158 | /* set the codec system clock for DAC and ADC */ | ||
159 | ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk, | ||
160 | SND_SOC_CLOCK_IN); | ||
161 | if (ret < 0) | ||
162 | return ret; | ||
163 | |||
164 | /* set the I2S system clock as input (unused) */ | ||
165 | ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, | ||
166 | SND_SOC_CLOCK_IN); | ||
167 | if (ret < 0) | ||
168 | return ret; | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static struct snd_soc_ops corgi_ops = { | ||
174 | .startup = corgi_startup, | ||
175 | .hw_params = corgi_hw_params, | ||
176 | .shutdown = corgi_shutdown, | ||
177 | }; | ||
178 | |||
179 | static int corgi_get_jack(struct snd_kcontrol *kcontrol, | ||
180 | struct snd_ctl_elem_value *ucontrol) | ||
181 | { | ||
182 | ucontrol->value.integer.value[0] = corgi_jack_func; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int corgi_set_jack(struct snd_kcontrol *kcontrol, | ||
187 | struct snd_ctl_elem_value *ucontrol) | ||
188 | { | ||
189 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
190 | |||
191 | if (corgi_jack_func == ucontrol->value.integer.value[0]) | ||
192 | return 0; | ||
193 | |||
194 | corgi_jack_func = ucontrol->value.integer.value[0]; | ||
195 | corgi_ext_control(codec); | ||
196 | return 1; | ||
197 | } | ||
198 | |||
199 | static int corgi_get_spk(struct snd_kcontrol *kcontrol, | ||
200 | struct snd_ctl_elem_value *ucontrol) | ||
201 | { | ||
202 | ucontrol->value.integer.value[0] = corgi_spk_func; | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static int corgi_set_spk(struct snd_kcontrol *kcontrol, | ||
207 | struct snd_ctl_elem_value *ucontrol) | ||
208 | { | ||
209 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
210 | |||
211 | if (corgi_spk_func == ucontrol->value.integer.value[0]) | ||
212 | return 0; | ||
213 | |||
214 | corgi_spk_func = ucontrol->value.integer.value[0]; | ||
215 | corgi_ext_control(codec); | ||
216 | return 1; | ||
217 | } | ||
218 | |||
219 | static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event) | ||
220 | { | ||
221 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
222 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); | ||
223 | else | ||
224 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int corgi_mic_event(struct snd_soc_dapm_widget *w, int event) | ||
230 | { | ||
231 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
232 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); | ||
233 | else | ||
234 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | /* corgi machine dapm widgets */ | ||
240 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { | ||
241 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
242 | SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event), | ||
243 | SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event), | ||
244 | SND_SOC_DAPM_LINE("Line Jack", NULL), | ||
245 | SND_SOC_DAPM_HP("Headset Jack", NULL), | ||
246 | }; | ||
247 | |||
248 | /* Corgi machine audio map (connections to the codec pins) */ | ||
249 | static const char *audio_map[][3] = { | ||
250 | |||
251 | /* headset Jack - in = micin, out = LHPOUT*/ | ||
252 | {"Headset Jack", NULL, "LHPOUT"}, | ||
253 | |||
254 | /* headphone connected to LHPOUT1, RHPOUT1 */ | ||
255 | {"Headphone Jack", NULL, "LHPOUT"}, | ||
256 | {"Headphone Jack", NULL, "RHPOUT"}, | ||
257 | |||
258 | /* speaker connected to LOUT, ROUT */ | ||
259 | {"Ext Spk", NULL, "ROUT"}, | ||
260 | {"Ext Spk", NULL, "LOUT"}, | ||
261 | |||
262 | /* mic is connected to MICIN (via right channel of headphone jack) */ | ||
263 | {"MICIN", NULL, "Mic Jack"}, | ||
264 | |||
265 | /* Same as the above but no mic bias for line signals */ | ||
266 | {"MICIN", NULL, "Line Jack"}, | ||
267 | |||
268 | {NULL, NULL, NULL}, | ||
269 | }; | ||
270 | |||
271 | static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", | ||
272 | "Off"}; | ||
273 | static const char *spk_function[] = {"On", "Off"}; | ||
274 | static const struct soc_enum corgi_enum[] = { | ||
275 | SOC_ENUM_SINGLE_EXT(5, jack_function), | ||
276 | SOC_ENUM_SINGLE_EXT(2, spk_function), | ||
277 | }; | ||
278 | |||
279 | static const struct snd_kcontrol_new wm8731_corgi_controls[] = { | ||
280 | SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack, | ||
281 | corgi_set_jack), | ||
282 | SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk, | ||
283 | corgi_set_spk), | ||
284 | }; | ||
285 | |||
286 | /* | ||
287 | * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device | ||
288 | */ | ||
289 | static int corgi_wm8731_init(struct snd_soc_codec *codec) | ||
290 | { | ||
291 | int i, err; | ||
292 | |||
293 | snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); | ||
294 | snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); | ||
295 | |||
296 | /* Add corgi specific controls */ | ||
297 | for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { | ||
298 | err = snd_ctl_add(codec->card, | ||
299 | snd_soc_cnew(&wm8731_corgi_controls[i],codec, NULL)); | ||
300 | if (err < 0) | ||
301 | return err; | ||
302 | } | ||
303 | |||
304 | /* Add corgi specific widgets */ | ||
305 | for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { | ||
306 | snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); | ||
307 | } | ||
308 | |||
309 | /* Set up corgi specific audio path audio_map */ | ||
310 | for(i = 0; audio_map[i][0] != NULL; i++) { | ||
311 | snd_soc_dapm_connect_input(codec, audio_map[i][0], | ||
312 | audio_map[i][1], audio_map[i][2]); | ||
313 | } | ||
314 | |||
315 | snd_soc_dapm_sync_endpoints(codec); | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | /* corgi digital audio interface glue - connects codec <--> CPU */ | ||
320 | static struct snd_soc_dai_link corgi_dai = { | ||
321 | .name = "WM8731", | ||
322 | .stream_name = "WM8731", | ||
323 | .cpu_dai = &pxa_i2s_dai, | ||
324 | .codec_dai = &wm8731_dai, | ||
325 | .init = corgi_wm8731_init, | ||
326 | .ops = &corgi_ops, | ||
327 | }; | ||
328 | |||
329 | /* corgi audio machine driver */ | ||
330 | static struct snd_soc_machine snd_soc_machine_corgi = { | ||
331 | .name = "Corgi", | ||
332 | .dai_link = &corgi_dai, | ||
333 | .num_links = 1, | ||
334 | }; | ||
335 | |||
336 | /* corgi audio private data */ | ||
337 | static struct wm8731_setup_data corgi_wm8731_setup = { | ||
338 | .i2c_address = 0x1b, | ||
339 | }; | ||
340 | |||
341 | /* corgi audio subsystem */ | ||
342 | static struct snd_soc_device corgi_snd_devdata = { | ||
343 | .machine = &snd_soc_machine_corgi, | ||
344 | .platform = &pxa2xx_soc_platform, | ||
345 | .codec_dev = &soc_codec_dev_wm8731, | ||
346 | .codec_data = &corgi_wm8731_setup, | ||
347 | }; | ||
348 | |||
349 | static struct platform_device *corgi_snd_device; | ||
350 | |||
351 | static int __init corgi_init(void) | ||
352 | { | ||
353 | int ret; | ||
354 | |||
355 | if (!(machine_is_corgi() || machine_is_shepherd() || machine_is_husky())) | ||
356 | return -ENODEV; | ||
357 | |||
358 | corgi_snd_device = platform_device_alloc("soc-audio", -1); | ||
359 | if (!corgi_snd_device) | ||
360 | return -ENOMEM; | ||
361 | |||
362 | platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata); | ||
363 | corgi_snd_devdata.dev = &corgi_snd_device->dev; | ||
364 | ret = platform_device_add(corgi_snd_device); | ||
365 | |||
366 | if (ret) | ||
367 | platform_device_put(corgi_snd_device); | ||
368 | |||
369 | return ret; | ||
370 | } | ||
371 | |||
372 | static void __exit corgi_exit(void) | ||
373 | { | ||
374 | platform_device_unregister(corgi_snd_device); | ||
375 | } | ||
376 | |||
377 | module_init(corgi_init); | ||
378 | module_exit(corgi_exit); | ||
379 | |||
380 | /* Module information */ | ||
381 | MODULE_AUTHOR("Richard Purdie"); | ||
382 | MODULE_DESCRIPTION("ALSA SoC Corgi"); | ||
383 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c new file mode 100644 index 000000000000..0915cf740421 --- /dev/null +++ b/sound/soc/pxa/poodle.c | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | * poodle.c -- SoC audio for Poodle | ||
3 | * | ||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | ||
5 | * Copyright 2005 Openedhand Ltd. | ||
6 | * | ||
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | ||
8 | * Richard Purdie <richard@openedhand.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/moduleparam.h> | ||
19 | #include <linux/timer.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/pcm.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <sound/soc-dapm.h> | ||
27 | |||
28 | #include <asm/mach-types.h> | ||
29 | #include <asm/hardware/locomo.h> | ||
30 | #include <asm/arch/pxa-regs.h> | ||
31 | #include <asm/arch/hardware.h> | ||
32 | #include <asm/arch/poodle.h> | ||
33 | #include <asm/arch/audio.h> | ||
34 | |||
35 | #include "../codecs/wm8731.h" | ||
36 | #include "pxa2xx-pcm.h" | ||
37 | #include "pxa2xx-i2s.h" | ||
38 | |||
39 | #define POODLE_HP 1 | ||
40 | #define POODLE_HP_OFF 0 | ||
41 | #define POODLE_SPK_ON 1 | ||
42 | #define POODLE_SPK_OFF 0 | ||
43 | |||
44 | /* audio clock in Hz - rounded from 12.235MHz */ | ||
45 | #define POODLE_AUDIO_CLOCK 12288000 | ||
46 | |||
47 | static int poodle_jack_func; | ||
48 | static int poodle_spk_func; | ||
49 | |||
50 | static void poodle_ext_control(struct snd_soc_codec *codec) | ||
51 | { | ||
52 | int spk = 0; | ||
53 | |||
54 | /* set up jack connection */ | ||
55 | if (poodle_jack_func == POODLE_HP) { | ||
56 | /* set = unmute headphone */ | ||
57 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
58 | POODLE_LOCOMO_GPIO_MUTE_L, 1); | ||
59 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
60 | POODLE_LOCOMO_GPIO_MUTE_R, 1); | ||
61 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); | ||
62 | } else { | ||
63 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
64 | POODLE_LOCOMO_GPIO_MUTE_L, 0); | ||
65 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
66 | POODLE_LOCOMO_GPIO_MUTE_R, 0); | ||
67 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
68 | } | ||
69 | |||
70 | if (poodle_spk_func == POODLE_SPK_ON) | ||
71 | spk = 1; | ||
72 | |||
73 | /* set the enpoints to their new connetion states */ | ||
74 | snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk); | ||
75 | |||
76 | /* signal a DAPM event */ | ||
77 | snd_soc_dapm_sync_endpoints(codec); | ||
78 | } | ||
79 | |||
80 | static int poodle_startup(struct snd_pcm_substream *substream) | ||
81 | { | ||
82 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
83 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
84 | |||
85 | /* check the jack status at stream startup */ | ||
86 | poodle_ext_control(codec); | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | /* we need to unmute the HP at shutdown as the mute burns power on poodle */ | ||
91 | static int poodle_shutdown(struct snd_pcm_substream *substream) | ||
92 | { | ||
93 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
94 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
95 | |||
96 | /* set = unmute headphone */ | ||
97 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
98 | POODLE_LOCOMO_GPIO_MUTE_L, 1); | ||
99 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
100 | POODLE_LOCOMO_GPIO_MUTE_R, 1); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int poodle_hw_params(struct snd_pcm_substream *substream, | ||
105 | struct snd_pcm_hw_params *params) | ||
106 | { | ||
107 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
108 | struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
109 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
110 | unsigned int clk = 0; | ||
111 | int ret = 0; | ||
112 | |||
113 | switch (params_rate(params)) { | ||
114 | case 8000: | ||
115 | case 16000: | ||
116 | case 48000: | ||
117 | case 96000: | ||
118 | clk = 12288000; | ||
119 | break; | ||
120 | case 11025: | ||
121 | case 22050: | ||
122 | case 44100: | ||
123 | clk = 11289600; | ||
124 | break; | ||
125 | } | ||
126 | |||
127 | /* set codec DAI configuration */ | ||
128 | ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
129 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
130 | if (ret < 0) | ||
131 | return ret; | ||
132 | |||
133 | /* set cpu DAI configuration */ | ||
134 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
135 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
136 | if (ret < 0) | ||
137 | return ret; | ||
138 | |||
139 | /* set the codec system clock for DAC and ADC */ | ||
140 | ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk, | ||
141 | SND_SOC_CLOCK_IN); | ||
142 | if (ret < 0) | ||
143 | return ret; | ||
144 | |||
145 | /* set the I2S system clock as input (unused) */ | ||
146 | ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, | ||
147 | SND_SOC_CLOCK_IN); | ||
148 | if (ret < 0) | ||
149 | return ret; | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static struct snd_soc_ops poodle_ops = { | ||
155 | .startup = poodle_startup, | ||
156 | .hw_params = poodle_hw_params, | ||
157 | .shutdown = poodle_shutdown, | ||
158 | }; | ||
159 | |||
160 | static int poodle_get_jack(struct snd_kcontrol *kcontrol, | ||
161 | struct snd_ctl_elem_value *ucontrol) | ||
162 | { | ||
163 | ucontrol->value.integer.value[0] = poodle_jack_func; | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int poodle_set_jack(struct snd_kcontrol *kcontrol, | ||
168 | struct snd_ctl_elem_value *ucontrol) | ||
169 | { | ||
170 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
171 | |||
172 | if (poodle_jack_func == ucontrol->value.integer.value[0]) | ||
173 | return 0; | ||
174 | |||
175 | poodle_jack_func = ucontrol->value.integer.value[0]; | ||
176 | poodle_ext_control(codec); | ||
177 | return 1; | ||
178 | } | ||
179 | |||
180 | static int poodle_get_spk(struct snd_kcontrol *kcontrol, | ||
181 | struct snd_ctl_elem_value *ucontrol) | ||
182 | { | ||
183 | ucontrol->value.integer.value[0] = poodle_spk_func; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int poodle_set_spk(struct snd_kcontrol *kcontrol, | ||
188 | struct snd_ctl_elem_value *ucontrol) | ||
189 | { | ||
190 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
191 | |||
192 | if (poodle_spk_func == ucontrol->value.integer.value[0]) | ||
193 | return 0; | ||
194 | |||
195 | poodle_spk_func = ucontrol->value.integer.value[0]; | ||
196 | poodle_ext_control(codec); | ||
197 | return 1; | ||
198 | } | ||
199 | |||
200 | static int poodle_amp_event(struct snd_soc_dapm_widget *w, int event) | ||
201 | { | ||
202 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
203 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
204 | POODLE_LOCOMO_GPIO_AMP_ON, 0); | ||
205 | else | ||
206 | locomo_gpio_write(&poodle_locomo_device.dev, | ||
207 | POODLE_LOCOMO_GPIO_AMP_ON, 1); | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | /* poodle machine dapm widgets */ | ||
213 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { | ||
214 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
215 | SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event), | ||
216 | }; | ||
217 | |||
218 | /* Corgi machine audio_mapnections to the codec pins */ | ||
219 | static const char *audio_map[][3] = { | ||
220 | |||
221 | /* headphone connected to LHPOUT1, RHPOUT1 */ | ||
222 | {"Headphone Jack", NULL, "LHPOUT"}, | ||
223 | {"Headphone Jack", NULL, "RHPOUT"}, | ||
224 | |||
225 | /* speaker connected to LOUT, ROUT */ | ||
226 | {"Ext Spk", NULL, "ROUT"}, | ||
227 | {"Ext Spk", NULL, "LOUT"}, | ||
228 | |||
229 | {NULL, NULL, NULL}, | ||
230 | }; | ||
231 | |||
232 | static const char *jack_function[] = {"Off", "Headphone"}; | ||
233 | static const char *spk_function[] = {"Off", "On"}; | ||
234 | static const struct soc_enum poodle_enum[] = { | ||
235 | SOC_ENUM_SINGLE_EXT(2, jack_function), | ||
236 | SOC_ENUM_SINGLE_EXT(2, spk_function), | ||
237 | }; | ||
238 | |||
239 | static const snd_kcontrol_new_t wm8731_poodle_controls[] = { | ||
240 | SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack, | ||
241 | poodle_set_jack), | ||
242 | SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk, | ||
243 | poodle_set_spk), | ||
244 | }; | ||
245 | |||
246 | /* | ||
247 | * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device | ||
248 | */ | ||
249 | static int poodle_wm8731_init(struct snd_soc_codec *codec) | ||
250 | { | ||
251 | int i, err; | ||
252 | |||
253 | snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); | ||
254 | snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); | ||
255 | snd_soc_dapm_set_endpoint(codec, "MICIN", 1); | ||
256 | |||
257 | /* Add poodle specific controls */ | ||
258 | for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) { | ||
259 | err = snd_ctl_add(codec->card, | ||
260 | snd_soc_cnew(&wm8731_poodle_controls[i],codec, NULL)); | ||
261 | if (err < 0) | ||
262 | return err; | ||
263 | } | ||
264 | |||
265 | /* Add poodle specific widgets */ | ||
266 | for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { | ||
267 | snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); | ||
268 | } | ||
269 | |||
270 | /* Set up poodle specific audio path audio_map */ | ||
271 | for (i = 0; audio_map[i][0] != NULL; i++) { | ||
272 | snd_soc_dapm_connect_input(codec, audio_map[i][0], | ||
273 | audio_map[i][1], audio_map[i][2]); | ||
274 | } | ||
275 | |||
276 | snd_soc_dapm_sync_endpoints(codec); | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | /* poodle digital audio interface glue - connects codec <--> CPU */ | ||
281 | static struct snd_soc_dai_link poodle_dai = { | ||
282 | .name = "WM8731", | ||
283 | .stream_name = "WM8731", | ||
284 | .cpu_dai = &pxa_i2s_dai, | ||
285 | .codec_dai = &wm8731_dai, | ||
286 | .init = poodle_wm8731_init, | ||
287 | .ops = &poodle_ops, | ||
288 | }; | ||
289 | |||
290 | /* poodle audio machine driver */ | ||
291 | static struct snd_soc_machine snd_soc_machine_poodle = { | ||
292 | .name = "Poodle", | ||
293 | .dai_link = &poodle_dai, | ||
294 | .num_links = 1, | ||
295 | }; | ||
296 | |||
297 | /* poodle audio private data */ | ||
298 | static struct wm8731_setup_data poodle_wm8731_setup = { | ||
299 | .i2c_address = 0x1b, | ||
300 | }; | ||
301 | |||
302 | /* poodle audio subsystem */ | ||
303 | static struct snd_soc_device poodle_snd_devdata = { | ||
304 | .machine = &snd_soc_machine_poodle, | ||
305 | .platform = &pxa2xx_soc_platform, | ||
306 | .codec_dev = &soc_codec_dev_wm8731, | ||
307 | .codec_data = &poodle_wm8731_setup, | ||
308 | }; | ||
309 | |||
310 | static struct platform_device *poodle_snd_device; | ||
311 | |||
312 | static int __init poodle_init(void) | ||
313 | { | ||
314 | int ret; | ||
315 | |||
316 | if (!machine_is_poodle()) | ||
317 | return -ENODEV; | ||
318 | |||
319 | locomo_gpio_set_dir(&poodle_locomo_device.dev, | ||
320 | POODLE_LOCOMO_GPIO_AMP_ON, 0); | ||
321 | /* should we mute HP at startup - burning power ?*/ | ||
322 | locomo_gpio_set_dir(&poodle_locomo_device.dev, | ||
323 | POODLE_LOCOMO_GPIO_MUTE_L, 0); | ||
324 | locomo_gpio_set_dir(&poodle_locomo_device.dev, | ||
325 | POODLE_LOCOMO_GPIO_MUTE_R, 0); | ||
326 | |||
327 | poodle_snd_device = platform_device_alloc("soc-audio", -1); | ||
328 | if (!poodle_snd_device) | ||
329 | return -ENOMEM; | ||
330 | |||
331 | platform_set_drvdata(poodle_snd_device, &poodle_snd_devdata); | ||
332 | poodle_snd_devdata.dev = &poodle_snd_device->dev; | ||
333 | ret = platform_device_add(poodle_snd_device); | ||
334 | |||
335 | if (ret) | ||
336 | platform_device_put(poodle_snd_device); | ||
337 | |||
338 | return ret; | ||
339 | } | ||
340 | |||
341 | static void __exit poodle_exit(void) | ||
342 | { | ||
343 | platform_device_unregister(poodle_snd_device); | ||
344 | } | ||
345 | |||
346 | module_init(poodle_init); | ||
347 | module_exit(poodle_exit); | ||
348 | |||
349 | /* Module information */ | ||
350 | MODULE_AUTHOR("Richard Purdie"); | ||
351 | MODULE_DESCRIPTION("ALSA SoC Poodle"); | ||
352 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c new file mode 100644 index 000000000000..1bbbeff84ef0 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-ac97.c | |||
@@ -0,0 +1,431 @@ | |||
1 | /* | ||
2 | * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. | ||
3 | * | ||
4 | * Author: Nicolas Pitre | ||
5 | * Created: Dec 02, 2004 | ||
6 | * Copyright: MontaVista Software Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/delay.h> | ||
19 | |||
20 | #include <sound/driver.h> | ||
21 | #include <sound/core.h> | ||
22 | #include <sound/pcm.h> | ||
23 | #include <sound/ac97_codec.h> | ||
24 | #include <sound/initval.h> | ||
25 | #include <sound/soc.h> | ||
26 | |||
27 | #include <asm/irq.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <asm/hardware.h> | ||
30 | #include <asm/arch/pxa-regs.h> | ||
31 | #include <asm/arch/audio.h> | ||
32 | |||
33 | #include "pxa2xx-pcm.h" | ||
34 | #include "pxa2xx-ac97.h" | ||
35 | |||
36 | static DEFINE_MUTEX(car_mutex); | ||
37 | static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); | ||
38 | static volatile long gsr_bits; | ||
39 | |||
40 | /* | ||
41 | * Beware PXA27x bugs: | ||
42 | * | ||
43 | * o Slot 12 read from modem space will hang controller. | ||
44 | * o CDONE, SDONE interrupt fails after any slot 12 IO. | ||
45 | * | ||
46 | * We therefore have an hybrid approach for waiting on SDONE (interrupt or | ||
47 | * 1 jiffy timeout if interrupt never comes). | ||
48 | */ | ||
49 | |||
50 | static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, | ||
51 | unsigned short reg) | ||
52 | { | ||
53 | unsigned short val = -1; | ||
54 | volatile u32 *reg_addr; | ||
55 | |||
56 | mutex_lock(&car_mutex); | ||
57 | |||
58 | /* set up primary or secondary codec/modem space */ | ||
59 | #ifdef CONFIG_PXA27x | ||
60 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
61 | #else | ||
62 | if (reg == AC97_GPIO_STATUS) | ||
63 | reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; | ||
64 | else | ||
65 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
66 | #endif | ||
67 | reg_addr += (reg >> 1); | ||
68 | |||
69 | #ifndef CONFIG_PXA27x | ||
70 | if (reg == AC97_GPIO_STATUS) { | ||
71 | /* read from controller cache */ | ||
72 | val = *reg_addr; | ||
73 | goto out; | ||
74 | } | ||
75 | #endif | ||
76 | |||
77 | /* start read access across the ac97 link */ | ||
78 | GSR = GSR_CDONE | GSR_SDONE; | ||
79 | gsr_bits = 0; | ||
80 | val = *reg_addr; | ||
81 | |||
82 | wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); | ||
83 | if (!((GSR | gsr_bits) & GSR_SDONE)) { | ||
84 | printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n", | ||
85 | __FUNCTION__, reg, GSR | gsr_bits); | ||
86 | val = -1; | ||
87 | goto out; | ||
88 | } | ||
89 | |||
90 | /* valid data now */ | ||
91 | GSR = GSR_CDONE | GSR_SDONE; | ||
92 | gsr_bits = 0; | ||
93 | val = *reg_addr; | ||
94 | /* but we've just started another cycle... */ | ||
95 | wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); | ||
96 | |||
97 | out: mutex_unlock(&car_mutex); | ||
98 | return val; | ||
99 | } | ||
100 | |||
101 | static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | ||
102 | unsigned short val) | ||
103 | { | ||
104 | volatile u32 *reg_addr; | ||
105 | |||
106 | mutex_lock(&car_mutex); | ||
107 | |||
108 | /* set up primary or secondary codec/modem space */ | ||
109 | #ifdef CONFIG_PXA27x | ||
110 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
111 | #else | ||
112 | if (reg == AC97_GPIO_STATUS) | ||
113 | reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; | ||
114 | else | ||
115 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
116 | #endif | ||
117 | reg_addr += (reg >> 1); | ||
118 | |||
119 | GSR = GSR_CDONE | GSR_SDONE; | ||
120 | gsr_bits = 0; | ||
121 | *reg_addr = val; | ||
122 | wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1); | ||
123 | if (!((GSR | gsr_bits) & GSR_CDONE)) | ||
124 | printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n", | ||
125 | __FUNCTION__, reg, GSR | gsr_bits); | ||
126 | |||
127 | mutex_unlock(&car_mutex); | ||
128 | } | ||
129 | |||
130 | static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) | ||
131 | { | ||
132 | gsr_bits = 0; | ||
133 | |||
134 | #ifdef CONFIG_PXA27x | ||
135 | /* warm reset broken on Bulverde, | ||
136 | so manually keep AC97 reset high */ | ||
137 | pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); | ||
138 | udelay(10); | ||
139 | GCR |= GCR_WARM_RST; | ||
140 | pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); | ||
141 | udelay(500); | ||
142 | #else | ||
143 | GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; | ||
144 | wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); | ||
145 | #endif | ||
146 | |||
147 | if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) | ||
148 | printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", | ||
149 | __FUNCTION__, gsr_bits); | ||
150 | |||
151 | GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); | ||
152 | GCR |= GCR_SDONE_IE|GCR_CDONE_IE; | ||
153 | } | ||
154 | |||
155 | static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) | ||
156 | { | ||
157 | GCR &= GCR_COLD_RST; /* clear everything but nCRST */ | ||
158 | GCR &= ~GCR_COLD_RST; /* then assert nCRST */ | ||
159 | |||
160 | gsr_bits = 0; | ||
161 | #ifdef CONFIG_PXA27x | ||
162 | /* PXA27x Developers Manual section 13.5.2.2.1 */ | ||
163 | pxa_set_cken(1 << 31, 1); | ||
164 | udelay(5); | ||
165 | pxa_set_cken(1 << 31, 0); | ||
166 | GCR = GCR_COLD_RST; | ||
167 | udelay(50); | ||
168 | #else | ||
169 | GCR = GCR_COLD_RST; | ||
170 | GCR |= GCR_CDONE_IE|GCR_SDONE_IE; | ||
171 | wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); | ||
172 | #endif | ||
173 | |||
174 | if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) | ||
175 | printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", | ||
176 | __FUNCTION__, gsr_bits); | ||
177 | |||
178 | GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); | ||
179 | GCR |= GCR_SDONE_IE|GCR_CDONE_IE; | ||
180 | } | ||
181 | |||
182 | static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) | ||
183 | { | ||
184 | long status; | ||
185 | |||
186 | status = GSR; | ||
187 | if (status) { | ||
188 | GSR = status; | ||
189 | gsr_bits |= status; | ||
190 | wake_up(&gsr_wq); | ||
191 | |||
192 | #ifdef CONFIG_PXA27x | ||
193 | /* Although we don't use those we still need to clear them | ||
194 | since they tend to spuriously trigger when MMC is used | ||
195 | (hardware bug? go figure)... */ | ||
196 | MISR = MISR_EOC; | ||
197 | PISR = PISR_EOC; | ||
198 | MCSR = MCSR_EOC; | ||
199 | #endif | ||
200 | |||
201 | return IRQ_HANDLED; | ||
202 | } | ||
203 | |||
204 | return IRQ_NONE; | ||
205 | } | ||
206 | |||
207 | struct snd_ac97_bus_ops soc_ac97_ops = { | ||
208 | .read = pxa2xx_ac97_read, | ||
209 | .write = pxa2xx_ac97_write, | ||
210 | .warm_reset = pxa2xx_ac97_warm_reset, | ||
211 | .reset = pxa2xx_ac97_cold_reset, | ||
212 | }; | ||
213 | |||
214 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { | ||
215 | .name = "AC97 PCM Stereo out", | ||
216 | .dev_addr = __PREG(PCDR), | ||
217 | .drcmr = &DRCMRTXPCDR, | ||
218 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
219 | DCMD_BURST32 | DCMD_WIDTH4, | ||
220 | }; | ||
221 | |||
222 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { | ||
223 | .name = "AC97 PCM Stereo in", | ||
224 | .dev_addr = __PREG(PCDR), | ||
225 | .drcmr = &DRCMRRXPCDR, | ||
226 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
227 | DCMD_BURST32 | DCMD_WIDTH4, | ||
228 | }; | ||
229 | |||
230 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { | ||
231 | .name = "AC97 Aux PCM (Slot 5) Mono out", | ||
232 | .dev_addr = __PREG(MODR), | ||
233 | .drcmr = &DRCMRTXMODR, | ||
234 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
235 | DCMD_BURST16 | DCMD_WIDTH2, | ||
236 | }; | ||
237 | |||
238 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { | ||
239 | .name = "AC97 Aux PCM (Slot 5) Mono in", | ||
240 | .dev_addr = __PREG(MODR), | ||
241 | .drcmr = &DRCMRRXMODR, | ||
242 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
243 | DCMD_BURST16 | DCMD_WIDTH2, | ||
244 | }; | ||
245 | |||
246 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { | ||
247 | .name = "AC97 Mic PCM (Slot 6) Mono in", | ||
248 | .dev_addr = __PREG(MCDR), | ||
249 | .drcmr = &DRCMRRXMCDR, | ||
250 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
251 | DCMD_BURST16 | DCMD_WIDTH2, | ||
252 | }; | ||
253 | |||
254 | #ifdef CONFIG_PM | ||
255 | static int pxa2xx_ac97_suspend(struct platform_device *pdev, | ||
256 | struct snd_soc_cpu_dai *dai) | ||
257 | { | ||
258 | GCR |= GCR_ACLINK_OFF; | ||
259 | pxa_set_cken(CKEN2_AC97, 0); | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static int pxa2xx_ac97_resume(struct platform_device *pdev, | ||
264 | struct snd_soc_cpu_dai *dai) | ||
265 | { | ||
266 | pxa_gpio_mode(GPIO31_SYNC_AC97_MD); | ||
267 | pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); | ||
268 | pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); | ||
269 | pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); | ||
270 | #ifdef CONFIG_PXA27x | ||
271 | /* Use GPIO 113 as AC97 Reset on Bulverde */ | ||
272 | pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); | ||
273 | #endif | ||
274 | pxa_set_cken(CKEN2_AC97, 1); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | #else | ||
279 | #define pxa2xx_ac97_suspend NULL | ||
280 | #define pxa2xx_ac97_resume NULL | ||
281 | #endif | ||
282 | |||
283 | static int pxa2xx_ac97_probe(struct platform_device *pdev) | ||
284 | { | ||
285 | int ret; | ||
286 | |||
287 | ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); | ||
288 | if (ret < 0) | ||
289 | goto err; | ||
290 | |||
291 | pxa_gpio_mode(GPIO31_SYNC_AC97_MD); | ||
292 | pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); | ||
293 | pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); | ||
294 | pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); | ||
295 | #ifdef CONFIG_PXA27x | ||
296 | /* Use GPIO 113 as AC97 Reset on Bulverde */ | ||
297 | pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); | ||
298 | #endif | ||
299 | pxa_set_cken(CKEN2_AC97, 1); | ||
300 | return 0; | ||
301 | |||
302 | err: | ||
303 | if (CKEN & CKEN2_AC97) { | ||
304 | GCR |= GCR_ACLINK_OFF; | ||
305 | free_irq(IRQ_AC97, NULL); | ||
306 | pxa_set_cken(CKEN2_AC97, 0); | ||
307 | } | ||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | static void pxa2xx_ac97_remove(struct platform_device *pdev) | ||
312 | { | ||
313 | GCR |= GCR_ACLINK_OFF; | ||
314 | free_irq(IRQ_AC97, NULL); | ||
315 | pxa_set_cken(CKEN2_AC97, 0); | ||
316 | } | ||
317 | |||
318 | static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, | ||
319 | struct snd_pcm_hw_params *params) | ||
320 | { | ||
321 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
322 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
323 | |||
324 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
325 | cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out; | ||
326 | else | ||
327 | cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in; | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, | ||
333 | struct snd_pcm_hw_params *params) | ||
334 | { | ||
335 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
336 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
337 | |||
338 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
339 | cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out; | ||
340 | else | ||
341 | cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in; | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, | ||
347 | struct snd_pcm_hw_params *params) | ||
348 | { | ||
349 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
350 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
351 | |||
352 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
353 | return -ENODEV; | ||
354 | else | ||
355 | cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in; | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | #define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
361 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ | ||
362 | SNDRV_PCM_RATE_48000) | ||
363 | |||
364 | /* | ||
365 | * There is only 1 physical AC97 interface for pxa2xx, but it | ||
366 | * has extra fifo's that can be used for aux DACs and ADCs. | ||
367 | */ | ||
368 | struct snd_soc_cpu_dai pxa_ac97_dai[] = { | ||
369 | { | ||
370 | .name = "pxa2xx-ac97", | ||
371 | .id = 0, | ||
372 | .type = SND_SOC_DAI_AC97, | ||
373 | .probe = pxa2xx_ac97_probe, | ||
374 | .remove = pxa2xx_ac97_remove, | ||
375 | .suspend = pxa2xx_ac97_suspend, | ||
376 | .resume = pxa2xx_ac97_resume, | ||
377 | .playback = { | ||
378 | .stream_name = "AC97 Playback", | ||
379 | .channels_min = 2, | ||
380 | .channels_max = 2, | ||
381 | .rates = PXA2XX_AC97_RATES, | ||
382 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
383 | .capture = { | ||
384 | .stream_name = "AC97 Capture", | ||
385 | .channels_min = 2, | ||
386 | .channels_max = 2, | ||
387 | .rates = PXA2XX_AC97_RATES, | ||
388 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
389 | .ops = { | ||
390 | .hw_params = pxa2xx_ac97_hw_params,}, | ||
391 | }, | ||
392 | { | ||
393 | .name = "pxa2xx-ac97-aux", | ||
394 | .id = 1, | ||
395 | .type = SND_SOC_DAI_AC97, | ||
396 | .playback = { | ||
397 | .stream_name = "AC97 Aux Playback", | ||
398 | .channels_min = 1, | ||
399 | .channels_max = 1, | ||
400 | .rates = PXA2XX_AC97_RATES, | ||
401 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
402 | .capture = { | ||
403 | .stream_name = "AC97 Aux Capture", | ||
404 | .channels_min = 1, | ||
405 | .channels_max = 1, | ||
406 | .rates = PXA2XX_AC97_RATES, | ||
407 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
408 | .ops = { | ||
409 | .hw_params = pxa2xx_ac97_hw_aux_params,}, | ||
410 | }, | ||
411 | { | ||
412 | .name = "pxa2xx-ac97-mic", | ||
413 | .id = 2, | ||
414 | .type = SND_SOC_DAI_AC97, | ||
415 | .capture = { | ||
416 | .stream_name = "AC97 Mic Capture", | ||
417 | .channels_min = 1, | ||
418 | .channels_max = 1, | ||
419 | .rates = PXA2XX_AC97_RATES, | ||
420 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
421 | .ops = { | ||
422 | .hw_params = pxa2xx_ac97_hw_mic_params,}, | ||
423 | }, | ||
424 | }; | ||
425 | |||
426 | EXPORT_SYMBOL_GPL(pxa_ac97_dai); | ||
427 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | ||
428 | |||
429 | MODULE_AUTHOR("Nicolas Pitre"); | ||
430 | MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); | ||
431 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h new file mode 100644 index 000000000000..4c4b882316ac --- /dev/null +++ b/sound/soc/pxa/pxa2xx-ac97.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/pxa2xx-ac97.h | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _PXA2XX_AC97_H | ||
10 | #define _PXA2XX_AC97_H | ||
11 | |||
12 | /* pxa2xx DAI ID's */ | ||
13 | #define PXA2XX_DAI_AC97_HIFI 0 | ||
14 | #define PXA2XX_DAI_AC97_AUX 1 | ||
15 | #define PXA2XX_DAI_AC97_MIC 2 | ||
16 | |||
17 | extern struct snd_soc_cpu_dai pxa_ac97_dai[3]; | ||
18 | |||
19 | /* platform data */ | ||
20 | extern struct snd_ac97_bus_ops pxa2xx_ac97_ops; | ||
21 | |||
22 | #endif | ||
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c new file mode 100644 index 000000000000..575a6137c040 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-i2s.c | |||
@@ -0,0 +1,318 @@ | |||
1 | /* | ||
2 | * pxa2xx-i2s.c -- ALSA Soc Audio Layer | ||
3 | * | ||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | ||
5 | * Author: Liam Girdwood | ||
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | * Revision history | ||
14 | * 12th Aug 2005 Initial version. | ||
15 | */ | ||
16 | |||
17 | #include <linux/init.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <sound/driver.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/initval.h> | ||
25 | #include <sound/soc.h> | ||
26 | |||
27 | #include <asm/hardware.h> | ||
28 | #include <asm/arch/pxa-regs.h> | ||
29 | #include <asm/arch/audio.h> | ||
30 | |||
31 | #include "pxa2xx-pcm.h" | ||
32 | #include "pxa2xx-i2s.h" | ||
33 | |||
34 | struct pxa_i2s_port { | ||
35 | u32 sadiv; | ||
36 | u32 sacr0; | ||
37 | u32 sacr1; | ||
38 | u32 saimr; | ||
39 | int master; | ||
40 | u32 fmt; | ||
41 | }; | ||
42 | static struct pxa_i2s_port pxa_i2s; | ||
43 | |||
44 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { | ||
45 | .name = "I2S PCM Stereo out", | ||
46 | .dev_addr = __PREG(SADR), | ||
47 | .drcmr = &DRCMRTXSADR, | ||
48 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
49 | DCMD_BURST32 | DCMD_WIDTH4, | ||
50 | }; | ||
51 | |||
52 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { | ||
53 | .name = "I2S PCM Stereo in", | ||
54 | .dev_addr = __PREG(SADR), | ||
55 | .drcmr = &DRCMRRXSADR, | ||
56 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
57 | DCMD_BURST32 | DCMD_WIDTH4, | ||
58 | }; | ||
59 | |||
60 | static struct pxa2xx_gpio gpio_bus[] = { | ||
61 | { /* I2S SoC Slave */ | ||
62 | .rx = GPIO29_SDATA_IN_I2S_MD, | ||
63 | .tx = GPIO30_SDATA_OUT_I2S_MD, | ||
64 | .clk = GPIO28_BITCLK_IN_I2S_MD, | ||
65 | .frm = GPIO31_SYNC_I2S_MD, | ||
66 | }, | ||
67 | { /* I2S SoC Master */ | ||
68 | #ifdef CONFIG_PXA27x | ||
69 | .sys = GPIO113_I2S_SYSCLK_MD, | ||
70 | #else | ||
71 | .sys = GPIO32_SYSCLK_I2S_MD, | ||
72 | #endif | ||
73 | .rx = GPIO29_SDATA_IN_I2S_MD, | ||
74 | .tx = GPIO30_SDATA_OUT_I2S_MD, | ||
75 | .clk = GPIO28_BITCLK_OUT_I2S_MD, | ||
76 | .frm = GPIO31_SYNC_I2S_MD, | ||
77 | }, | ||
78 | }; | ||
79 | |||
80 | static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream) | ||
81 | { | ||
82 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
83 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
84 | |||
85 | if (!cpu_dai->active) { | ||
86 | SACR0 |= SACR0_RST; | ||
87 | SACR0 = 0; | ||
88 | } | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | /* wait for I2S controller to be ready */ | ||
94 | static int pxa_i2s_wait(void) | ||
95 | { | ||
96 | int i; | ||
97 | |||
98 | /* flush the Rx FIFO */ | ||
99 | for(i = 0; i < 16; i++) | ||
100 | SADR; | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, | ||
105 | unsigned int fmt) | ||
106 | { | ||
107 | /* interface format */ | ||
108 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
109 | case SND_SOC_DAIFMT_I2S: | ||
110 | pxa_i2s.fmt = 0; | ||
111 | break; | ||
112 | case SND_SOC_DAIFMT_LEFT_J: | ||
113 | pxa_i2s.fmt = SACR1_AMSL; | ||
114 | break; | ||
115 | } | ||
116 | |||
117 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
118 | case SND_SOC_DAIFMT_CBS_CFS: | ||
119 | pxa_i2s.master = 1; | ||
120 | break; | ||
121 | case SND_SOC_DAIFMT_CBM_CFS: | ||
122 | pxa_i2s.master = 0; | ||
123 | break; | ||
124 | default: | ||
125 | break; | ||
126 | } | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, | ||
131 | int clk_id, unsigned int freq, int dir) | ||
132 | { | ||
133 | if (clk_id != PXA2XX_I2S_SYSCLK) | ||
134 | return -ENODEV; | ||
135 | |||
136 | if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT) | ||
137 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, | ||
143 | struct snd_pcm_hw_params *params) | ||
144 | { | ||
145 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
146 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
147 | |||
148 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); | ||
149 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); | ||
150 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); | ||
151 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); | ||
152 | pxa_set_cken(CKEN8_I2S, 1); | ||
153 | pxa_i2s_wait(); | ||
154 | |||
155 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
156 | cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; | ||
157 | else | ||
158 | cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; | ||
159 | |||
160 | /* is port used by another stream */ | ||
161 | if (!(SACR0 & SACR0_ENB)) { | ||
162 | |||
163 | SACR0 = 0; | ||
164 | SACR1 = 0; | ||
165 | if (pxa_i2s.master) | ||
166 | SACR0 |= SACR0_BCKD; | ||
167 | |||
168 | SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1); | ||
169 | SACR1 |= pxa_i2s.fmt; | ||
170 | } | ||
171 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
172 | SAIMR |= SAIMR_TFS; | ||
173 | else | ||
174 | SAIMR |= SAIMR_RFS; | ||
175 | |||
176 | switch (params_rate(params)) { | ||
177 | case 8000: | ||
178 | SADIV = 0x48; | ||
179 | break; | ||
180 | case 11025: | ||
181 | SADIV = 0x34; | ||
182 | break; | ||
183 | case 16000: | ||
184 | SADIV = 0x24; | ||
185 | break; | ||
186 | case 22050: | ||
187 | SADIV = 0x1a; | ||
188 | break; | ||
189 | case 44100: | ||
190 | SADIV = 0xd; | ||
191 | break; | ||
192 | case 48000: | ||
193 | SADIV = 0xc; | ||
194 | break; | ||
195 | case 96000: /* not in manual and possibly slightly inaccurate */ | ||
196 | SADIV = 0x6; | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) | ||
204 | { | ||
205 | int ret = 0; | ||
206 | |||
207 | switch (cmd) { | ||
208 | case SNDRV_PCM_TRIGGER_START: | ||
209 | SACR0 |= SACR0_ENB; | ||
210 | break; | ||
211 | case SNDRV_PCM_TRIGGER_RESUME: | ||
212 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
213 | case SNDRV_PCM_TRIGGER_STOP: | ||
214 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
215 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
216 | break; | ||
217 | default: | ||
218 | ret = -EINVAL; | ||
219 | } | ||
220 | |||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream) | ||
225 | { | ||
226 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
227 | SACR1 |= SACR1_DRPL; | ||
228 | SAIMR &= ~SAIMR_TFS; | ||
229 | } else { | ||
230 | SACR1 |= SACR1_DREC; | ||
231 | SAIMR &= ~SAIMR_RFS; | ||
232 | } | ||
233 | |||
234 | if (SACR1 & (SACR1_DREC | SACR1_DRPL)) { | ||
235 | SACR0 &= ~SACR0_ENB; | ||
236 | pxa_i2s_wait(); | ||
237 | pxa_set_cken(CKEN8_I2S, 0); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | #ifdef CONFIG_PM | ||
242 | static int pxa2xx_i2s_suspend(struct platform_device *dev, | ||
243 | struct snd_soc_cpu_dai *dai) | ||
244 | { | ||
245 | if (!dai->active) | ||
246 | return 0; | ||
247 | |||
248 | /* store registers */ | ||
249 | pxa_i2s.sacr0 = SACR0; | ||
250 | pxa_i2s.sacr1 = SACR1; | ||
251 | pxa_i2s.saimr = SAIMR; | ||
252 | pxa_i2s.sadiv = SADIV; | ||
253 | |||
254 | /* deactivate link */ | ||
255 | SACR0 &= ~SACR0_ENB; | ||
256 | pxa_i2s_wait(); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int pxa2xx_i2s_resume(struct platform_device *pdev, | ||
261 | struct snd_soc_cpu_dai *dai) | ||
262 | { | ||
263 | if (!dai->active) | ||
264 | return 0; | ||
265 | |||
266 | pxa_i2s_wait(); | ||
267 | |||
268 | SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB; | ||
269 | SACR1 = pxa_i2s.sacr1; | ||
270 | SAIMR = pxa_i2s.saimr; | ||
271 | SADIV = pxa_i2s.sadiv; | ||
272 | SACR0 |= SACR0_ENB; | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | #else | ||
278 | #define pxa2xx_i2s_suspend NULL | ||
279 | #define pxa2xx_i2s_resume NULL | ||
280 | #endif | ||
281 | |||
282 | #define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
283 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ | ||
284 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) | ||
285 | |||
286 | struct snd_soc_cpu_dai pxa_i2s_dai = { | ||
287 | .name = "pxa2xx-i2s", | ||
288 | .id = 0, | ||
289 | .type = SND_SOC_DAI_I2S, | ||
290 | .suspend = pxa2xx_i2s_suspend, | ||
291 | .resume = pxa2xx_i2s_resume, | ||
292 | .playback = { | ||
293 | .channels_min = 2, | ||
294 | .channels_max = 2, | ||
295 | .rates = PXA2XX_I2S_RATES, | ||
296 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
297 | .capture = { | ||
298 | .channels_min = 2, | ||
299 | .channels_max = 2, | ||
300 | .rates = PXA2XX_I2S_RATES, | ||
301 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
302 | .ops = { | ||
303 | .startup = pxa2xx_i2s_startup, | ||
304 | .shutdown = pxa2xx_i2s_shutdown, | ||
305 | .trigger = pxa2xx_i2s_trigger, | ||
306 | .hw_params = pxa2xx_i2s_hw_params,}, | ||
307 | .dai_ops = { | ||
308 | .set_fmt = pxa2xx_i2s_set_dai_fmt, | ||
309 | .set_sysclk = pxa2xx_i2s_set_dai_sysclk, | ||
310 | }, | ||
311 | }; | ||
312 | |||
313 | EXPORT_SYMBOL_GPL(pxa_i2s_dai); | ||
314 | |||
315 | /* Module information */ | ||
316 | MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); | ||
317 | MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); | ||
318 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h new file mode 100644 index 000000000000..a2484f0881f1 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-i2s.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/pxa2xx-i2s.h | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _PXA2XX_I2S_H | ||
10 | #define _PXA2XX_I2S_H | ||
11 | |||
12 | /* pxa2xx DAI ID's */ | ||
13 | #define PXA2XX_DAI_I2S 0 | ||
14 | |||
15 | /* I2S clock */ | ||
16 | #define PXA2XX_I2S_SYSCLK 0 | ||
17 | |||
18 | extern struct snd_soc_cpu_dai pxa_i2s_dai; | ||
19 | |||
20 | #endif | ||
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c new file mode 100644 index 000000000000..35e8fa3a469c --- /dev/null +++ b/sound/soc/pxa/pxa2xx-pcm.c | |||
@@ -0,0 +1,372 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip | ||
3 | * | ||
4 | * Author: Nicolas Pitre | ||
5 | * Created: Nov 30, 2004 | ||
6 | * Copyright: (C) 2004 MontaVista Software, Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/dma-mapping.h> | ||
18 | |||
19 | #include <sound/driver.h> | ||
20 | #include <sound/core.h> | ||
21 | #include <sound/pcm.h> | ||
22 | #include <sound/pcm_params.h> | ||
23 | #include <sound/soc.h> | ||
24 | |||
25 | #include <asm/dma.h> | ||
26 | #include <asm/hardware.h> | ||
27 | #include <asm/arch/pxa-regs.h> | ||
28 | #include <asm/arch/audio.h> | ||
29 | |||
30 | #include "pxa2xx-pcm.h" | ||
31 | |||
32 | static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { | ||
33 | .info = SNDRV_PCM_INFO_MMAP | | ||
34 | SNDRV_PCM_INFO_MMAP_VALID | | ||
35 | SNDRV_PCM_INFO_INTERLEAVED | | ||
36 | SNDRV_PCM_INFO_PAUSE | | ||
37 | SNDRV_PCM_INFO_RESUME, | ||
38 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
39 | SNDRV_PCM_FMTBIT_S24_LE | | ||
40 | SNDRV_PCM_FMTBIT_S32_LE, | ||
41 | .period_bytes_min = 32, | ||
42 | .period_bytes_max = 8192 - 32, | ||
43 | .periods_min = 1, | ||
44 | .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), | ||
45 | .buffer_bytes_max = 128 * 1024, | ||
46 | .fifo_size = 32, | ||
47 | }; | ||
48 | |||
49 | struct pxa2xx_runtime_data { | ||
50 | int dma_ch; | ||
51 | struct pxa2xx_pcm_dma_params *params; | ||
52 | pxa_dma_desc *dma_desc_array; | ||
53 | dma_addr_t dma_desc_array_phys; | ||
54 | }; | ||
55 | |||
56 | static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) | ||
57 | { | ||
58 | struct snd_pcm_substream *substream = dev_id; | ||
59 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||
60 | int dcsr; | ||
61 | |||
62 | dcsr = DCSR(dma_ch); | ||
63 | DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; | ||
64 | |||
65 | if (dcsr & DCSR_ENDINTR) { | ||
66 | snd_pcm_period_elapsed(substream); | ||
67 | } else { | ||
68 | printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", | ||
69 | prtd->params->name, dma_ch, dcsr ); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, | ||
74 | struct snd_pcm_hw_params *params) | ||
75 | { | ||
76 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
77 | struct pxa2xx_runtime_data *prtd = runtime->private_data; | ||
78 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
79 | struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; | ||
80 | size_t totsize = params_buffer_bytes(params); | ||
81 | size_t period = params_period_bytes(params); | ||
82 | pxa_dma_desc *dma_desc; | ||
83 | dma_addr_t dma_buff_phys, next_desc_phys; | ||
84 | int ret; | ||
85 | |||
86 | /* return if this is a bufferless transfer e.g. | ||
87 | * codec <--> BT codec or GSM modem -- lg FIXME */ | ||
88 | if (!dma) | ||
89 | return 0; | ||
90 | |||
91 | /* this may get called several times by oss emulation | ||
92 | * with different params */ | ||
93 | if (prtd->params == NULL) { | ||
94 | prtd->params = dma; | ||
95 | ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, | ||
96 | pxa2xx_pcm_dma_irq, substream); | ||
97 | if (ret < 0) | ||
98 | return ret; | ||
99 | prtd->dma_ch = ret; | ||
100 | } else if (prtd->params != dma) { | ||
101 | pxa_free_dma(prtd->dma_ch); | ||
102 | prtd->params = dma; | ||
103 | ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, | ||
104 | pxa2xx_pcm_dma_irq, substream); | ||
105 | if (ret < 0) | ||
106 | return ret; | ||
107 | prtd->dma_ch = ret; | ||
108 | } | ||
109 | |||
110 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
111 | runtime->dma_bytes = totsize; | ||
112 | |||
113 | dma_desc = prtd->dma_desc_array; | ||
114 | next_desc_phys = prtd->dma_desc_array_phys; | ||
115 | dma_buff_phys = runtime->dma_addr; | ||
116 | do { | ||
117 | next_desc_phys += sizeof(pxa_dma_desc); | ||
118 | dma_desc->ddadr = next_desc_phys; | ||
119 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
120 | dma_desc->dsadr = dma_buff_phys; | ||
121 | dma_desc->dtadr = prtd->params->dev_addr; | ||
122 | } else { | ||
123 | dma_desc->dsadr = prtd->params->dev_addr; | ||
124 | dma_desc->dtadr = dma_buff_phys; | ||
125 | } | ||
126 | if (period > totsize) | ||
127 | period = totsize; | ||
128 | dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN; | ||
129 | dma_desc++; | ||
130 | dma_buff_phys += period; | ||
131 | } while (totsize -= period); | ||
132 | dma_desc[-1].ddadr = prtd->dma_desc_array_phys; | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) | ||
138 | { | ||
139 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||
140 | |||
141 | if (prtd && prtd->params) | ||
142 | *prtd->params->drcmr = 0; | ||
143 | |||
144 | if (prtd->dma_ch) { | ||
145 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
146 | pxa_free_dma(prtd->dma_ch); | ||
147 | prtd->dma_ch = 0; | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) | ||
154 | { | ||
155 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||
156 | |||
157 | DCSR(prtd->dma_ch) &= ~DCSR_RUN; | ||
158 | DCSR(prtd->dma_ch) = 0; | ||
159 | DCMD(prtd->dma_ch) = 0; | ||
160 | *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
166 | { | ||
167 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||
168 | int ret = 0; | ||
169 | |||
170 | switch (cmd) { | ||
171 | case SNDRV_PCM_TRIGGER_START: | ||
172 | DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; | ||
173 | DCSR(prtd->dma_ch) = DCSR_RUN; | ||
174 | break; | ||
175 | |||
176 | case SNDRV_PCM_TRIGGER_STOP: | ||
177 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
178 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
179 | DCSR(prtd->dma_ch) &= ~DCSR_RUN; | ||
180 | break; | ||
181 | |||
182 | case SNDRV_PCM_TRIGGER_RESUME: | ||
183 | DCSR(prtd->dma_ch) |= DCSR_RUN; | ||
184 | break; | ||
185 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
186 | DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; | ||
187 | DCSR(prtd->dma_ch) |= DCSR_RUN; | ||
188 | break; | ||
189 | |||
190 | default: | ||
191 | ret = -EINVAL; | ||
192 | } | ||
193 | |||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | static snd_pcm_uframes_t | ||
198 | pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) | ||
199 | { | ||
200 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
201 | struct pxa2xx_runtime_data *prtd = runtime->private_data; | ||
202 | |||
203 | dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
204 | DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); | ||
205 | snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); | ||
206 | |||
207 | if (x == runtime->buffer_size) | ||
208 | x = 0; | ||
209 | return x; | ||
210 | } | ||
211 | |||
212 | static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) | ||
213 | { | ||
214 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
215 | struct pxa2xx_runtime_data *prtd; | ||
216 | int ret; | ||
217 | |||
218 | snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware); | ||
219 | |||
220 | /* | ||
221 | * For mysterious reasons (and despite what the manual says) | ||
222 | * playback samples are lost if the DMA count is not a multiple | ||
223 | * of the DMA burst size. Let's add a rule to enforce that. | ||
224 | */ | ||
225 | ret = snd_pcm_hw_constraint_step(runtime, 0, | ||
226 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); | ||
227 | if (ret) | ||
228 | goto out; | ||
229 | |||
230 | ret = snd_pcm_hw_constraint_step(runtime, 0, | ||
231 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); | ||
232 | if (ret) | ||
233 | goto out; | ||
234 | |||
235 | ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | ||
236 | if (ret < 0) | ||
237 | goto out; | ||
238 | |||
239 | prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL); | ||
240 | if (prtd == NULL) { | ||
241 | ret = -ENOMEM; | ||
242 | goto out; | ||
243 | } | ||
244 | |||
245 | prtd->dma_desc_array = | ||
246 | dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, | ||
247 | &prtd->dma_desc_array_phys, GFP_KERNEL); | ||
248 | if (!prtd->dma_desc_array) { | ||
249 | ret = -ENOMEM; | ||
250 | goto err1; | ||
251 | } | ||
252 | |||
253 | runtime->private_data = prtd; | ||
254 | return 0; | ||
255 | |||
256 | err1: | ||
257 | kfree(prtd); | ||
258 | out: | ||
259 | return ret; | ||
260 | } | ||
261 | |||
262 | static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) | ||
263 | { | ||
264 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
265 | struct pxa2xx_runtime_data *prtd = runtime->private_data; | ||
266 | |||
267 | dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, | ||
268 | prtd->dma_desc_array, prtd->dma_desc_array_phys); | ||
269 | kfree(prtd); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, | ||
274 | struct vm_area_struct *vma) | ||
275 | { | ||
276 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
277 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
278 | runtime->dma_area, | ||
279 | runtime->dma_addr, | ||
280 | runtime->dma_bytes); | ||
281 | } | ||
282 | |||
283 | struct snd_pcm_ops pxa2xx_pcm_ops = { | ||
284 | .open = pxa2xx_pcm_open, | ||
285 | .close = pxa2xx_pcm_close, | ||
286 | .ioctl = snd_pcm_lib_ioctl, | ||
287 | .hw_params = pxa2xx_pcm_hw_params, | ||
288 | .hw_free = pxa2xx_pcm_hw_free, | ||
289 | .prepare = pxa2xx_pcm_prepare, | ||
290 | .trigger = pxa2xx_pcm_trigger, | ||
291 | .pointer = pxa2xx_pcm_pointer, | ||
292 | .mmap = pxa2xx_pcm_mmap, | ||
293 | }; | ||
294 | |||
295 | static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
296 | { | ||
297 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
298 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
299 | size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; | ||
300 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
301 | buf->dev.dev = pcm->card->dev; | ||
302 | buf->private_data = NULL; | ||
303 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
304 | &buf->addr, GFP_KERNEL); | ||
305 | if (!buf->area) | ||
306 | return -ENOMEM; | ||
307 | buf->bytes = size; | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
312 | { | ||
313 | struct snd_pcm_substream *substream; | ||
314 | struct snd_dma_buffer *buf; | ||
315 | int stream; | ||
316 | |||
317 | for (stream = 0; stream < 2; stream++) { | ||
318 | substream = pcm->streams[stream].substream; | ||
319 | if (!substream) | ||
320 | continue; | ||
321 | |||
322 | buf = &substream->dma_buffer; | ||
323 | if (!buf->area) | ||
324 | continue; | ||
325 | |||
326 | dma_free_writecombine(pcm->card->dev, buf->bytes, | ||
327 | buf->area, buf->addr); | ||
328 | buf->area = NULL; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK; | ||
333 | |||
334 | int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai, | ||
335 | struct snd_pcm *pcm) | ||
336 | { | ||
337 | int ret = 0; | ||
338 | |||
339 | if (!card->dev->dma_mask) | ||
340 | card->dev->dma_mask = &pxa2xx_pcm_dmamask; | ||
341 | if (!card->dev->coherent_dma_mask) | ||
342 | card->dev->coherent_dma_mask = DMA_32BIT_MASK; | ||
343 | |||
344 | if (dai->playback.channels_min) { | ||
345 | ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, | ||
346 | SNDRV_PCM_STREAM_PLAYBACK); | ||
347 | if (ret) | ||
348 | goto out; | ||
349 | } | ||
350 | |||
351 | if (dai->capture.channels_min) { | ||
352 | ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, | ||
353 | SNDRV_PCM_STREAM_CAPTURE); | ||
354 | if (ret) | ||
355 | goto out; | ||
356 | } | ||
357 | out: | ||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | struct snd_soc_platform pxa2xx_soc_platform = { | ||
362 | .name = "pxa2xx-audio", | ||
363 | .pcm_ops = &pxa2xx_pcm_ops, | ||
364 | .pcm_new = pxa2xx_pcm_new, | ||
365 | .pcm_free = pxa2xx_pcm_free_dma_buffers, | ||
366 | }; | ||
367 | |||
368 | EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); | ||
369 | |||
370 | MODULE_AUTHOR("Nicolas Pitre"); | ||
371 | MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); | ||
372 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h new file mode 100644 index 000000000000..54c9c755e508 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-pcm.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip | ||
3 | * | ||
4 | * Author: Nicolas Pitre | ||
5 | * Created: Nov 30, 2004 | ||
6 | * Copyright: MontaVista Software, Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef _PXA2XX_PCM_H | ||
14 | #define _PXA2XX_PCM_H | ||
15 | |||
16 | struct pxa2xx_pcm_dma_params { | ||
17 | char *name; /* stream identifier */ | ||
18 | u32 dcmd; /* DMA descriptor dcmd field */ | ||
19 | volatile u32 *drcmr; /* the DMA request channel to use */ | ||
20 | u32 dev_addr; /* device physical address for DMA */ | ||
21 | }; | ||
22 | |||
23 | struct pxa2xx_gpio { | ||
24 | u32 sys; | ||
25 | u32 rx; | ||
26 | u32 tx; | ||
27 | u32 clk; | ||
28 | u32 frm; | ||
29 | }; | ||
30 | |||
31 | /* platform data */ | ||
32 | extern struct snd_soc_platform pxa2xx_soc_platform; | ||
33 | |||
34 | #endif | ||
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c new file mode 100644 index 000000000000..80e82109fef7 --- /dev/null +++ b/sound/soc/pxa/spitz.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita | ||
3 | * | ||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | ||
5 | * Copyright 2005 Openedhand Ltd. | ||
6 | * | ||
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | ||
8 | * Richard Purdie <richard@openedhand.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * Revision history | ||
16 | * 30th Nov 2005 Initial version. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/timer.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <sound/driver.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/soc.h> | ||
29 | #include <sound/soc-dapm.h> | ||
30 | |||
31 | #include <asm/mach-types.h> | ||
32 | #include <asm/hardware/scoop.h> | ||
33 | #include <asm/arch/pxa-regs.h> | ||
34 | #include <asm/arch/hardware.h> | ||
35 | #include <asm/arch/akita.h> | ||
36 | #include <asm/arch/spitz.h> | ||
37 | #include <asm/mach-types.h> | ||
38 | #include "../codecs/wm8750.h" | ||
39 | #include "pxa2xx-pcm.h" | ||
40 | #include "pxa2xx-i2s.h" | ||
41 | |||
42 | #define SPITZ_HP 0 | ||
43 | #define SPITZ_MIC 1 | ||
44 | #define SPITZ_LINE 2 | ||
45 | #define SPITZ_HEADSET 3 | ||
46 | #define SPITZ_HP_OFF 4 | ||
47 | #define SPITZ_SPK_ON 0 | ||
48 | #define SPITZ_SPK_OFF 1 | ||
49 | |||
50 | /* audio clock in Hz - rounded from 12.235MHz */ | ||
51 | #define SPITZ_AUDIO_CLOCK 12288000 | ||
52 | |||
53 | static int spitz_jack_func; | ||
54 | static int spitz_spk_func; | ||
55 | |||
56 | static void spitz_ext_control(struct snd_soc_codec *codec) | ||
57 | { | ||
58 | if (spitz_spk_func == SPITZ_SPK_ON) | ||
59 | snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1); | ||
60 | else | ||
61 | snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0); | ||
62 | |||
63 | /* set up jack connection */ | ||
64 | switch (spitz_jack_func) { | ||
65 | case SPITZ_HP: | ||
66 | /* enable and unmute hp jack, disable mic bias */ | ||
67 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
68 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); | ||
69 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
70 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); | ||
71 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
72 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
73 | break; | ||
74 | case SPITZ_MIC: | ||
75 | /* enable mic jack and bias, mute hp */ | ||
76 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
77 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
78 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
79 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); | ||
80 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
81 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
82 | break; | ||
83 | case SPITZ_LINE: | ||
84 | /* enable line jack, disable mic bias and mute hp */ | ||
85 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
86 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
87 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); | ||
88 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 1); | ||
89 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
90 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
91 | break; | ||
92 | case SPITZ_HEADSET: | ||
93 | /* enable and unmute headset jack enable mic bias, mute L hp */ | ||
94 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
95 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); | ||
96 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
97 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1); | ||
98 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
99 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
100 | break; | ||
101 | case SPITZ_HP_OFF: | ||
102 | |||
103 | /* jack removed, everything off */ | ||
104 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
105 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
106 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); | ||
107 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
108 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
109 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
110 | break; | ||
111 | } | ||
112 | snd_soc_dapm_sync_endpoints(codec); | ||
113 | } | ||
114 | |||
115 | static int spitz_startup(struct snd_pcm_substream *substream) | ||
116 | { | ||
117 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
118 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
119 | |||
120 | /* check the jack status at stream startup */ | ||
121 | spitz_ext_control(codec); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int spitz_hw_params(struct snd_pcm_substream *substream, | ||
126 | struct snd_pcm_hw_params *params) | ||
127 | { | ||
128 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
129 | struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
130 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
131 | unsigned int clk = 0; | ||
132 | int ret = 0; | ||
133 | |||
134 | switch (params_rate(params)) { | ||
135 | case 8000: | ||
136 | case 16000: | ||
137 | case 48000: | ||
138 | case 96000: | ||
139 | clk = 12288000; | ||
140 | break; | ||
141 | case 11025: | ||
142 | case 22050: | ||
143 | case 44100: | ||
144 | clk = 11289600; | ||
145 | break; | ||
146 | } | ||
147 | |||
148 | /* set codec DAI configuration */ | ||
149 | ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
150 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
151 | if (ret < 0) | ||
152 | return ret; | ||
153 | |||
154 | /* set cpu DAI configuration */ | ||
155 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
156 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
157 | if (ret < 0) | ||
158 | return ret; | ||
159 | |||
160 | /* set the codec system clock for DAC and ADC */ | ||
161 | ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8750_SYSCLK, clk, | ||
162 | SND_SOC_CLOCK_IN); | ||
163 | if (ret < 0) | ||
164 | return ret; | ||
165 | |||
166 | /* set the I2S system clock as input (unused) */ | ||
167 | ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, | ||
168 | SND_SOC_CLOCK_IN); | ||
169 | if (ret < 0) | ||
170 | return ret; | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static struct snd_soc_ops spitz_ops = { | ||
176 | .startup = spitz_startup, | ||
177 | .hw_params = spitz_hw_params, | ||
178 | }; | ||
179 | |||
180 | static int spitz_get_jack(struct snd_kcontrol *kcontrol, | ||
181 | struct snd_ctl_elem_value *ucontrol) | ||
182 | { | ||
183 | ucontrol->value.integer.value[0] = spitz_jack_func; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int spitz_set_jack(struct snd_kcontrol *kcontrol, | ||
188 | struct snd_ctl_elem_value *ucontrol) | ||
189 | { | ||
190 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
191 | |||
192 | if (spitz_jack_func == ucontrol->value.integer.value[0]) | ||
193 | return 0; | ||
194 | |||
195 | spitz_jack_func = ucontrol->value.integer.value[0]; | ||
196 | spitz_ext_control(codec); | ||
197 | return 1; | ||
198 | } | ||
199 | |||
200 | static int spitz_get_spk(struct snd_kcontrol *kcontrol, | ||
201 | struct snd_ctl_elem_value *ucontrol) | ||
202 | { | ||
203 | ucontrol->value.integer.value[0] = spitz_spk_func; | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int spitz_set_spk(struct snd_kcontrol *kcontrol, | ||
208 | struct snd_ctl_elem_value *ucontrol) | ||
209 | { | ||
210 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
211 | |||
212 | if (spitz_spk_func == ucontrol->value.integer.value[0]) | ||
213 | return 0; | ||
214 | |||
215 | spitz_spk_func = ucontrol->value.integer.value[0]; | ||
216 | spitz_ext_control(codec); | ||
217 | return 1; | ||
218 | } | ||
219 | |||
220 | static int spitz_mic_bias(struct snd_soc_dapm_widget *w, int event) | ||
221 | { | ||
222 | if (machine_is_borzoi() || machine_is_spitz()) { | ||
223 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
224 | set_scoop_gpio(&spitzscoop2_device.dev, | ||
225 | SPITZ_SCP2_MIC_BIAS); | ||
226 | else | ||
227 | reset_scoop_gpio(&spitzscoop2_device.dev, | ||
228 | SPITZ_SCP2_MIC_BIAS); | ||
229 | } | ||
230 | |||
231 | if (machine_is_akita()) { | ||
232 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
233 | akita_set_ioexp(&akitaioexp_device.dev, | ||
234 | AKITA_IOEXP_MIC_BIAS); | ||
235 | else | ||
236 | akita_reset_ioexp(&akitaioexp_device.dev, | ||
237 | AKITA_IOEXP_MIC_BIAS); | ||
238 | } | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | /* spitz machine dapm widgets */ | ||
243 | static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { | ||
244 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
245 | SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), | ||
246 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | ||
247 | SND_SOC_DAPM_LINE("Line Jack", NULL), | ||
248 | |||
249 | /* headset is a mic and mono headphone */ | ||
250 | SND_SOC_DAPM_HP("Headset Jack", NULL), | ||
251 | }; | ||
252 | |||
253 | /* Spitz machine audio_map */ | ||
254 | static const char *audio_map[][3] = { | ||
255 | |||
256 | /* headphone connected to LOUT1, ROUT1 */ | ||
257 | {"Headphone Jack", NULL, "LOUT1"}, | ||
258 | {"Headphone Jack", NULL, "ROUT1"}, | ||
259 | |||
260 | /* headset connected to ROUT1 and LINPUT1 with bias (def below) */ | ||
261 | {"Headset Jack", NULL, "ROUT1"}, | ||
262 | |||
263 | /* ext speaker connected to LOUT2, ROUT2 */ | ||
264 | {"Ext Spk", NULL , "ROUT2"}, | ||
265 | {"Ext Spk", NULL , "LOUT2"}, | ||
266 | |||
267 | /* mic is connected to input 1 - with bias */ | ||
268 | {"LINPUT1", NULL, "Mic Bias"}, | ||
269 | {"Mic Bias", NULL, "Mic Jack"}, | ||
270 | |||
271 | /* line is connected to input 1 - no bias */ | ||
272 | {"LINPUT1", NULL, "Line Jack"}, | ||
273 | |||
274 | {NULL, NULL, NULL}, | ||
275 | }; | ||
276 | |||
277 | static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", | ||
278 | "Off"}; | ||
279 | static const char *spk_function[] = {"On", "Off"}; | ||
280 | static const struct soc_enum spitz_enum[] = { | ||
281 | SOC_ENUM_SINGLE_EXT(5, jack_function), | ||
282 | SOC_ENUM_SINGLE_EXT(2, spk_function), | ||
283 | }; | ||
284 | |||
285 | static const struct snd_kcontrol_new wm8750_spitz_controls[] = { | ||
286 | SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack, | ||
287 | spitz_set_jack), | ||
288 | SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk, | ||
289 | spitz_set_spk), | ||
290 | }; | ||
291 | |||
292 | /* | ||
293 | * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device | ||
294 | */ | ||
295 | static int spitz_wm8750_init(struct snd_soc_codec *codec) | ||
296 | { | ||
297 | int i, err; | ||
298 | |||
299 | /* NC codec pins */ | ||
300 | snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0); | ||
301 | snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0); | ||
302 | snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0); | ||
303 | snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0); | ||
304 | snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0); | ||
305 | snd_soc_dapm_set_endpoint(codec, "OUT3", 0); | ||
306 | snd_soc_dapm_set_endpoint(codec, "MONO", 0); | ||
307 | |||
308 | /* Add spitz specific controls */ | ||
309 | for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { | ||
310 | err = snd_ctl_add(codec->card, | ||
311 | snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL)); | ||
312 | if (err < 0) | ||
313 | return err; | ||
314 | } | ||
315 | |||
316 | /* Add spitz specific widgets */ | ||
317 | for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) { | ||
318 | snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]); | ||
319 | } | ||
320 | |||
321 | /* Set up spitz specific audio path audio_map */ | ||
322 | for (i = 0; audio_map[i][0] != NULL; i++) { | ||
323 | snd_soc_dapm_connect_input(codec, audio_map[i][0], | ||
324 | audio_map[i][1], audio_map[i][2]); | ||
325 | } | ||
326 | |||
327 | snd_soc_dapm_sync_endpoints(codec); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | /* spitz digital audio interface glue - connects codec <--> CPU */ | ||
332 | static struct snd_soc_dai_link spitz_dai = { | ||
333 | .name = "wm8750", | ||
334 | .stream_name = "WM8750", | ||
335 | .cpu_dai = &pxa_i2s_dai, | ||
336 | .codec_dai = &wm8750_dai, | ||
337 | .init = spitz_wm8750_init, | ||
338 | .ops = &spitz_ops, | ||
339 | }; | ||
340 | |||
341 | /* spitz audio machine driver */ | ||
342 | static struct snd_soc_machine snd_soc_machine_spitz = { | ||
343 | .name = "Spitz", | ||
344 | .dai_link = &spitz_dai, | ||
345 | .num_links = 1, | ||
346 | }; | ||
347 | |||
348 | /* spitz audio private data */ | ||
349 | static struct wm8750_setup_data spitz_wm8750_setup = { | ||
350 | .i2c_address = 0x1b, | ||
351 | }; | ||
352 | |||
353 | /* spitz audio subsystem */ | ||
354 | static struct snd_soc_device spitz_snd_devdata = { | ||
355 | .machine = &snd_soc_machine_spitz, | ||
356 | .platform = &pxa2xx_soc_platform, | ||
357 | .codec_dev = &soc_codec_dev_wm8750, | ||
358 | .codec_data = &spitz_wm8750_setup, | ||
359 | }; | ||
360 | |||
361 | static struct platform_device *spitz_snd_device; | ||
362 | |||
363 | static int __init spitz_init(void) | ||
364 | { | ||
365 | int ret; | ||
366 | |||
367 | if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) | ||
368 | return -ENODEV; | ||
369 | |||
370 | spitz_snd_device = platform_device_alloc("soc-audio", -1); | ||
371 | if (!spitz_snd_device) | ||
372 | return -ENOMEM; | ||
373 | |||
374 | platform_set_drvdata(spitz_snd_device, &spitz_snd_devdata); | ||
375 | spitz_snd_devdata.dev = &spitz_snd_device->dev; | ||
376 | ret = platform_device_add(spitz_snd_device); | ||
377 | |||
378 | if (ret) | ||
379 | platform_device_put(spitz_snd_device); | ||
380 | |||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | static void __exit spitz_exit(void) | ||
385 | { | ||
386 | platform_device_unregister(spitz_snd_device); | ||
387 | } | ||
388 | |||
389 | module_init(spitz_init); | ||
390 | module_exit(spitz_exit); | ||
391 | |||
392 | MODULE_AUTHOR("Richard Purdie"); | ||
393 | MODULE_DESCRIPTION("ALSA SoC Spitz"); | ||
394 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c new file mode 100644 index 000000000000..5504e30acf14 --- /dev/null +++ b/sound/soc/pxa/tosa.c | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * tosa.c -- SoC audio for Tosa | ||
3 | * | ||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | ||
5 | * Copyright 2005 Openedhand Ltd. | ||
6 | * | ||
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | ||
8 | * Richard Purdie <richard@openedhand.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * Revision history | ||
16 | * 30th Nov 2005 Initial version. | ||
17 | * | ||
18 | * GPIO's | ||
19 | * 1 - Jack Insertion | ||
20 | * 5 - Hookswitch (headset answer/hang up switch) | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/moduleparam.h> | ||
26 | #include <linux/device.h> | ||
27 | |||
28 | #include <sound/driver.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/soc.h> | ||
32 | #include <sound/soc-dapm.h> | ||
33 | |||
34 | #include <asm/mach-types.h> | ||
35 | #include <asm/hardware/tmio.h> | ||
36 | #include <asm/arch/pxa-regs.h> | ||
37 | #include <asm/arch/hardware.h> | ||
38 | #include <asm/arch/audio.h> | ||
39 | #include <asm/arch/tosa.h> | ||
40 | |||
41 | #include "../codecs/wm9712.h" | ||
42 | #include "pxa2xx-pcm.h" | ||
43 | #include "pxa2xx-ac97.h" | ||
44 | |||
45 | static struct snd_soc_machine tosa; | ||
46 | |||
47 | #define TOSA_HP 0 | ||
48 | #define TOSA_MIC_INT 1 | ||
49 | #define TOSA_HEADSET 2 | ||
50 | #define TOSA_HP_OFF 3 | ||
51 | #define TOSA_SPK_ON 0 | ||
52 | #define TOSA_SPK_OFF 1 | ||
53 | |||
54 | static int tosa_jack_func; | ||
55 | static int tosa_spk_func; | ||
56 | |||
57 | static void tosa_ext_control(struct snd_soc_codec *codec) | ||
58 | { | ||
59 | int spk = 0, mic_int = 0, hp = 0, hs = 0; | ||
60 | |||
61 | /* set up jack connection */ | ||
62 | switch (tosa_jack_func) { | ||
63 | case TOSA_HP: | ||
64 | hp = 1; | ||
65 | break; | ||
66 | case TOSA_MIC_INT: | ||
67 | mic_int = 1; | ||
68 | break; | ||
69 | case TOSA_HEADSET: | ||
70 | hs = 1; | ||
71 | break; | ||
72 | } | ||
73 | |||
74 | if (tosa_spk_func == TOSA_SPK_ON) | ||
75 | spk = 1; | ||
76 | |||
77 | snd_soc_dapm_set_endpoint(codec, "Speaker", spk); | ||
78 | snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int); | ||
79 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp); | ||
80 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs); | ||
81 | snd_soc_dapm_sync_endpoints(codec); | ||
82 | } | ||
83 | |||
84 | static int tosa_startup(struct snd_pcm_substream *substream) | ||
85 | { | ||
86 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
87 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
88 | |||
89 | /* check the jack status at stream startup */ | ||
90 | tosa_ext_control(codec); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static struct snd_soc_ops tosa_ops = { | ||
95 | .startup = tosa_startup, | ||
96 | }; | ||
97 | |||
98 | static int tosa_get_jack(struct snd_kcontrol *kcontrol, | ||
99 | struct snd_ctl_elem_value *ucontrol) | ||
100 | { | ||
101 | ucontrol->value.integer.value[0] = tosa_jack_func; | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int tosa_set_jack(struct snd_kcontrol *kcontrol, | ||
106 | struct snd_ctl_elem_value *ucontrol) | ||
107 | { | ||
108 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
109 | |||
110 | if (tosa_jack_func == ucontrol->value.integer.value[0]) | ||
111 | return 0; | ||
112 | |||
113 | tosa_jack_func = ucontrol->value.integer.value[0]; | ||
114 | tosa_ext_control(codec); | ||
115 | return 1; | ||
116 | } | ||
117 | |||
118 | static int tosa_get_spk(struct snd_kcontrol *kcontrol, | ||
119 | struct snd_ctl_elem_value *ucontrol) | ||
120 | { | ||
121 | ucontrol->value.integer.value[0] = tosa_spk_func; | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int tosa_set_spk(struct snd_kcontrol *kcontrol, | ||
126 | struct snd_ctl_elem_value *ucontrol) | ||
127 | { | ||
128 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
129 | |||
130 | if (tosa_spk_func == ucontrol->value.integer.value[0]) | ||
131 | return 0; | ||
132 | |||
133 | tosa_spk_func = ucontrol->value.integer.value[0]; | ||
134 | tosa_ext_control(codec); | ||
135 | return 1; | ||
136 | } | ||
137 | |||
138 | /* tosa dapm event handlers */ | ||
139 | static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event) | ||
140 | { | ||
141 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
142 | set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); | ||
143 | else | ||
144 | reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | /* tosa machine dapm widgets */ | ||
149 | static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = { | ||
150 | SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event), | ||
151 | SND_SOC_DAPM_HP("Headset Jack", NULL), | ||
152 | SND_SOC_DAPM_MIC("Mic (Internal)", NULL), | ||
153 | SND_SOC_DAPM_SPK("Speaker", NULL), | ||
154 | }; | ||
155 | |||
156 | /* tosa audio map */ | ||
157 | static const char *audio_map[][3] = { | ||
158 | |||
159 | /* headphone connected to HPOUTL, HPOUTR */ | ||
160 | {"Headphone Jack", NULL, "HPOUTL"}, | ||
161 | {"Headphone Jack", NULL, "HPOUTR"}, | ||
162 | |||
163 | /* ext speaker connected to LOUT2, ROUT2 */ | ||
164 | {"Speaker", NULL, "LOUT2"}, | ||
165 | {"Speaker", NULL, "ROUT2"}, | ||
166 | |||
167 | /* internal mic is connected to mic1, mic2 differential - with bias */ | ||
168 | {"MIC1", NULL, "Mic Bias"}, | ||
169 | {"MIC2", NULL, "Mic Bias"}, | ||
170 | {"Mic Bias", NULL, "Mic (Internal)"}, | ||
171 | |||
172 | /* headset is connected to HPOUTR, and LINEINR with bias */ | ||
173 | {"Headset Jack", NULL, "HPOUTR"}, | ||
174 | {"LINEINR", NULL, "Mic Bias"}, | ||
175 | {"Mic Bias", NULL, "Headset Jack"}, | ||
176 | |||
177 | {NULL, NULL, NULL}, | ||
178 | }; | ||
179 | |||
180 | static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", | ||
181 | "Off"}; | ||
182 | static const char *spk_function[] = {"On", "Off"}; | ||
183 | static const struct soc_enum tosa_enum[] = { | ||
184 | SOC_ENUM_SINGLE_EXT(5, jack_function), | ||
185 | SOC_ENUM_SINGLE_EXT(2, spk_function), | ||
186 | }; | ||
187 | |||
188 | static const struct snd_kcontrol_new tosa_controls[] = { | ||
189 | SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack, | ||
190 | tosa_set_jack), | ||
191 | SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk, | ||
192 | tosa_set_spk), | ||
193 | }; | ||
194 | |||
195 | static int tosa_ac97_init(struct snd_soc_codec *codec) | ||
196 | { | ||
197 | int i, err; | ||
198 | |||
199 | snd_soc_dapm_set_endpoint(codec, "OUT3", 0); | ||
200 | snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0); | ||
201 | |||
202 | /* add tosa specific controls */ | ||
203 | for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { | ||
204 | err = snd_ctl_add(codec->card, | ||
205 | snd_soc_cnew(&tosa_controls[i],codec, NULL)); | ||
206 | if (err < 0) | ||
207 | return err; | ||
208 | } | ||
209 | |||
210 | /* add tosa specific widgets */ | ||
211 | for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) { | ||
212 | snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]); | ||
213 | } | ||
214 | |||
215 | /* set up tosa specific audio path audio_map */ | ||
216 | for (i = 0; audio_map[i][0] != NULL; i++) { | ||
217 | snd_soc_dapm_connect_input(codec, audio_map[i][0], | ||
218 | audio_map[i][1], audio_map[i][2]); | ||
219 | } | ||
220 | |||
221 | snd_soc_dapm_sync_endpoints(codec); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static struct snd_soc_dai_link tosa_dai[] = { | ||
226 | { | ||
227 | .name = "AC97", | ||
228 | .stream_name = "AC97 HiFi", | ||
229 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], | ||
230 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], | ||
231 | .init = tosa_ac97_init, | ||
232 | .ops = &tosa_ops, | ||
233 | }, | ||
234 | { | ||
235 | .name = "AC97 Aux", | ||
236 | .stream_name = "AC97 Aux", | ||
237 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], | ||
238 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], | ||
239 | .ops = &tosa_ops, | ||
240 | }, | ||
241 | }; | ||
242 | |||
243 | static struct snd_soc_machine tosa = { | ||
244 | .name = "Tosa", | ||
245 | .dai_link = tosa_dai, | ||
246 | .num_links = ARRAY_SIZE(tosa_dai), | ||
247 | }; | ||
248 | |||
249 | static struct snd_soc_device tosa_snd_devdata = { | ||
250 | .machine = &tosa, | ||
251 | .platform = &pxa2xx_soc_platform, | ||
252 | .codec_dev = &soc_codec_dev_wm9712, | ||
253 | }; | ||
254 | |||
255 | static struct platform_device *tosa_snd_device; | ||
256 | |||
257 | static int __init tosa_init(void) | ||
258 | { | ||
259 | int ret; | ||
260 | |||
261 | if (!machine_is_tosa()) | ||
262 | return -ENODEV; | ||
263 | |||
264 | tosa_snd_device = platform_device_alloc("soc-audio", -1); | ||
265 | if (!tosa_snd_device) | ||
266 | return -ENOMEM; | ||
267 | |||
268 | platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata); | ||
269 | tosa_snd_devdata.dev = &tosa_snd_device->dev; | ||
270 | ret = platform_device_add(tosa_snd_device); | ||
271 | |||
272 | if (ret) | ||
273 | platform_device_put(tosa_snd_device); | ||
274 | |||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | static void __exit tosa_exit(void) | ||
279 | { | ||
280 | platform_device_unregister(tosa_snd_device); | ||
281 | } | ||
282 | |||
283 | module_init(tosa_init); | ||
284 | module_exit(tosa_exit); | ||
285 | |||
286 | /* Module information */ | ||
287 | MODULE_AUTHOR("Richard Purdie"); | ||
288 | MODULE_DESCRIPTION("ALSA SoC Tosa"); | ||
289 | MODULE_LICENSE("GPL"); | ||