aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/pxa
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/pxa')
-rw-r--r--sound/soc/pxa/Kconfig60
-rw-r--r--sound/soc/pxa/Makefile20
-rw-r--r--sound/soc/pxa/corgi.c383
-rw-r--r--sound/soc/pxa/poodle.c352
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c431
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h22
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c318
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h20
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c372
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.h34
-rw-r--r--sound/soc/pxa/spitz.c394
-rw-r--r--sound/soc/pxa/tosa.c289
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 @@
1menu "SoC Audio for the Intel PXA2xx"
2
3config 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
12config SND_PXA2XX_AC97
13 tristate
14 select SND_AC97_CODEC
15
16config SND_PXA2XX_SOC_AC97
17 tristate
18 select AC97_BUS
19 select SND_SOC_AC97_BUS
20
21config SND_PXA2XX_SOC_I2S
22 tristate
23
24config 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
33config 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
42config 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
51config 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
60endmenu
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
2snd-soc-pxa2xx-objs := pxa2xx-pcm.o
3snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
4snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
5
6obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
7obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
8obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
9
10# PXA Machine Support
11snd-soc-corgi-objs := corgi.o
12snd-soc-poodle-objs := poodle.o
13snd-soc-tosa-objs := tosa.o
14snd-soc-spitz-objs := spitz.o
15
16obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
17obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
18obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
19obj-$(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
53static int corgi_jack_func;
54static int corgi_spk_func;
55
56static 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
101static 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 */
112static 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
123static 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
173static struct snd_soc_ops corgi_ops = {
174 .startup = corgi_startup,
175 .hw_params = corgi_hw_params,
176 .shutdown = corgi_shutdown,
177};
178
179static 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
186static 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
199static 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
206static 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
219static 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
229static 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 */
240static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
241SND_SOC_DAPM_HP("Headphone Jack", NULL),
242SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
243SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
244SND_SOC_DAPM_LINE("Line Jack", NULL),
245SND_SOC_DAPM_HP("Headset Jack", NULL),
246};
247
248/* Corgi machine audio map (connections to the codec pins) */
249static 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
271static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
272 "Off"};
273static const char *spk_function[] = {"On", "Off"};
274static const struct soc_enum corgi_enum[] = {
275 SOC_ENUM_SINGLE_EXT(5, jack_function),
276 SOC_ENUM_SINGLE_EXT(2, spk_function),
277};
278
279static 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 */
289static 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 */
320static 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 */
330static 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 */
337static struct wm8731_setup_data corgi_wm8731_setup = {
338 .i2c_address = 0x1b,
339};
340
341/* corgi audio subsystem */
342static 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
349static struct platform_device *corgi_snd_device;
350
351static 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
372static void __exit corgi_exit(void)
373{
374 platform_device_unregister(corgi_snd_device);
375}
376
377module_init(corgi_init);
378module_exit(corgi_exit);
379
380/* Module information */
381MODULE_AUTHOR("Richard Purdie");
382MODULE_DESCRIPTION("ALSA SoC Corgi");
383MODULE_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
47static int poodle_jack_func;
48static int poodle_spk_func;
49
50static 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
80static 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 */
91static 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
104static 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
154static struct snd_soc_ops poodle_ops = {
155 .startup = poodle_startup,
156 .hw_params = poodle_hw_params,
157 .shutdown = poodle_shutdown,
158};
159
160static 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
167static 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
180static 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
187static 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
200static 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 */
213static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
214SND_SOC_DAPM_HP("Headphone Jack", NULL),
215SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
216};
217
218/* Corgi machine audio_mapnections to the codec pins */
219static 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
232static const char *jack_function[] = {"Off", "Headphone"};
233static const char *spk_function[] = {"Off", "On"};
234static const struct soc_enum poodle_enum[] = {
235 SOC_ENUM_SINGLE_EXT(2, jack_function),
236 SOC_ENUM_SINGLE_EXT(2, spk_function),
237};
238
239static 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 */
249static 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 */
281static 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 */
291static 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 */
298static struct wm8731_setup_data poodle_wm8731_setup = {
299 .i2c_address = 0x1b,
300};
301
302/* poodle audio subsystem */
303static 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
310static struct platform_device *poodle_snd_device;
311
312static 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
341static void __exit poodle_exit(void)
342{
343 platform_device_unregister(poodle_snd_device);
344}
345
346module_init(poodle_init);
347module_exit(poodle_exit);
348
349/* Module information */
350MODULE_AUTHOR("Richard Purdie");
351MODULE_DESCRIPTION("ALSA SoC Poodle");
352MODULE_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
36static DEFINE_MUTEX(car_mutex);
37static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
38static 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
50static 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
97out: mutex_unlock(&car_mutex);
98 return val;
99}
100
101static 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
130static 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
155static 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
182static 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
207struct 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
214static 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
222static 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
230static 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
238static 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
246static 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
255static 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
263static 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
283static 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
311static 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
318static 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
332static 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
346static 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 */
368struct 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
426EXPORT_SYMBOL_GPL(pxa_ac97_dai);
427EXPORT_SYMBOL_GPL(soc_ac97_ops);
428
429MODULE_AUTHOR("Nicolas Pitre");
430MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
431MODULE_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
17extern struct snd_soc_cpu_dai pxa_ac97_dai[3];
18
19/* platform data */
20extern 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
34struct pxa_i2s_port {
35 u32 sadiv;
36 u32 sacr0;
37 u32 sacr1;
38 u32 saimr;
39 int master;
40 u32 fmt;
41};
42static struct pxa_i2s_port pxa_i2s;
43
44static 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
52static 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
60static 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
80static 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 */
94static 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
104static 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
130static 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
142static 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
203static 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
224static 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
242static 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
260static 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
286struct 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
313EXPORT_SYMBOL_GPL(pxa_i2s_dai);
314
315/* Module information */
316MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
317MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
318MODULE_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
18extern 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
32static 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
49struct 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
56static 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
73static 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
137static 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
153static 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
165static 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
197static snd_pcm_uframes_t
198pxa2xx_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
212static 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
262static 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
273static 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
283struct 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
295static 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
311static 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
332static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
333
334int 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
361struct 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
368EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
369
370MODULE_AUTHOR("Nicolas Pitre");
371MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
372MODULE_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
16struct 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
23struct pxa2xx_gpio {
24 u32 sys;
25 u32 rx;
26 u32 tx;
27 u32 clk;
28 u32 frm;
29};
30
31/* platform data */
32extern 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
53static int spitz_jack_func;
54static int spitz_spk_func;
55
56static 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
115static 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
125static 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
175static struct snd_soc_ops spitz_ops = {
176 .startup = spitz_startup,
177 .hw_params = spitz_hw_params,
178};
179
180static 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
187static 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
200static 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
207static 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
220static 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 */
243static 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 */
254static 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
277static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
278 "Off"};
279static const char *spk_function[] = {"On", "Off"};
280static const struct soc_enum spitz_enum[] = {
281 SOC_ENUM_SINGLE_EXT(5, jack_function),
282 SOC_ENUM_SINGLE_EXT(2, spk_function),
283};
284
285static 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 */
295static 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 */
332static 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 */
342static 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 */
349static struct wm8750_setup_data spitz_wm8750_setup = {
350 .i2c_address = 0x1b,
351};
352
353/* spitz audio subsystem */
354static 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
361static struct platform_device *spitz_snd_device;
362
363static 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
384static void __exit spitz_exit(void)
385{
386 platform_device_unregister(spitz_snd_device);
387}
388
389module_init(spitz_init);
390module_exit(spitz_exit);
391
392MODULE_AUTHOR("Richard Purdie");
393MODULE_DESCRIPTION("ALSA SoC Spitz");
394MODULE_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
45static 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
54static int tosa_jack_func;
55static int tosa_spk_func;
56
57static 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
84static 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
94static struct snd_soc_ops tosa_ops = {
95 .startup = tosa_startup,
96};
97
98static 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
105static 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
118static 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
125static 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 */
139static 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 */
149static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
150SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
151SND_SOC_DAPM_HP("Headset Jack", NULL),
152SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
153SND_SOC_DAPM_SPK("Speaker", NULL),
154};
155
156/* tosa audio map */
157static 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
180static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
181 "Off"};
182static const char *spk_function[] = {"On", "Off"};
183static const struct soc_enum tosa_enum[] = {
184 SOC_ENUM_SINGLE_EXT(5, jack_function),
185 SOC_ENUM_SINGLE_EXT(2, spk_function),
186};
187
188static 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
195static 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
225static 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
243static struct snd_soc_machine tosa = {
244 .name = "Tosa",
245 .dai_link = tosa_dai,
246 .num_links = ARRAY_SIZE(tosa_dai),
247};
248
249static struct snd_soc_device tosa_snd_devdata = {
250 .machine = &tosa,
251 .platform = &pxa2xx_soc_platform,
252 .codec_dev = &soc_codec_dev_wm9712,
253};
254
255static struct platform_device *tosa_snd_device;
256
257static 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
278static void __exit tosa_exit(void)
279{
280 platform_device_unregister(tosa_snd_device);
281}
282
283module_init(tosa_init);
284module_exit(tosa_exit);
285
286/* Module information */
287MODULE_AUTHOR("Richard Purdie");
288MODULE_DESCRIPTION("ALSA SoC Tosa");
289MODULE_LICENSE("GPL");