diff options
author | Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 2006-10-12 08:32:13 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2007-02-09 03:00:47 -0500 |
commit | 7fb290d03af69bfca5876573ac0eada40bd4e292 (patch) | |
tree | d8499af8dcfddcf98f8d8b9469cfc5ddc6a098c2 /sound/soc | |
parent | a1eb4b3caf3abd0d1a8474f07d29959e1879bb29 (diff) |
[ALSA] ASoC pxa2xx Spitz machine support
This patch adds Alsa audio support to the Sharp Zaurus SL-C1000/SL-C3x00
(Akita/Spitz) machines.
From: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/pxa/spitz.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c new file mode 100644 index 000000000000..17c8e61efe6f --- /dev/null +++ b/sound/soc/pxa/spitz.c | |||
@@ -0,0 +1,374 @@ | |||
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 | |||
41 | #define SPITZ_HP 0 | ||
42 | #define SPITZ_MIC 1 | ||
43 | #define SPITZ_LINE 2 | ||
44 | #define SPITZ_HEADSET 3 | ||
45 | #define SPITZ_HP_OFF 4 | ||
46 | #define SPITZ_SPK_ON 0 | ||
47 | #define SPITZ_SPK_OFF 1 | ||
48 | |||
49 | /* audio clock in Hz - rounded from 12.235MHz */ | ||
50 | #define SPITZ_AUDIO_CLOCK 12288000 | ||
51 | |||
52 | static int spitz_jack_func; | ||
53 | static int spitz_spk_func; | ||
54 | |||
55 | static void spitz_ext_control(struct snd_soc_codec *codec) | ||
56 | { | ||
57 | if (spitz_spk_func == SPITZ_SPK_ON) | ||
58 | snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1); | ||
59 | else | ||
60 | snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0); | ||
61 | |||
62 | /* set up jack connection */ | ||
63 | switch (spitz_jack_func) { | ||
64 | case SPITZ_HP: | ||
65 | /* enable and unmute hp jack, disable mic bias */ | ||
66 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
67 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); | ||
68 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
69 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); | ||
70 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
71 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
72 | break; | ||
73 | case SPITZ_MIC: | ||
74 | /* enable mic jack and bias, mute hp */ | ||
75 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
76 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
77 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
78 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); | ||
79 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
80 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
81 | break; | ||
82 | case SPITZ_LINE: | ||
83 | /* enable line jack, disable mic bias and mute hp */ | ||
84 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
85 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
86 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); | ||
87 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 1); | ||
88 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
89 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
90 | break; | ||
91 | case SPITZ_HEADSET: | ||
92 | /* enable and unmute headset jack enable mic bias, mute L hp */ | ||
93 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
94 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); | ||
95 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
96 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1); | ||
97 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
98 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
99 | break; | ||
100 | case SPITZ_HP_OFF: | ||
101 | |||
102 | /* jack removed, everything off */ | ||
103 | snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); | ||
104 | snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); | ||
105 | snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); | ||
106 | snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); | ||
107 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | ||
108 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | ||
109 | break; | ||
110 | } | ||
111 | snd_soc_dapm_sync_endpoints(codec); | ||
112 | } | ||
113 | |||
114 | static int spitz_startup(struct snd_pcm_substream *substream) | ||
115 | { | ||
116 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
117 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
118 | |||
119 | /* check the jack status at stream startup */ | ||
120 | spitz_ext_control(codec); | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static struct snd_soc_ops spitz_ops = { | ||
125 | .startup = spitz_startup, | ||
126 | }; | ||
127 | |||
128 | static int spitz_get_jack(struct snd_kcontrol *kcontrol, | ||
129 | struct snd_ctl_elem_value *ucontrol) | ||
130 | { | ||
131 | ucontrol->value.integer.value[0] = spitz_jack_func; | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static int spitz_set_jack(struct snd_kcontrol *kcontrol, | ||
136 | struct snd_ctl_elem_value *ucontrol) | ||
137 | { | ||
138 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
139 | |||
140 | if (spitz_jack_func == ucontrol->value.integer.value[0]) | ||
141 | return 0; | ||
142 | |||
143 | spitz_jack_func = ucontrol->value.integer.value[0]; | ||
144 | spitz_ext_control(codec); | ||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | static int spitz_get_spk(struct snd_kcontrol *kcontrol, | ||
149 | struct snd_ctl_elem_value *ucontrol) | ||
150 | { | ||
151 | ucontrol->value.integer.value[0] = spitz_spk_func; | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int spitz_set_spk(struct snd_kcontrol *kcontrol, | ||
156 | struct snd_ctl_elem_value *ucontrol) | ||
157 | { | ||
158 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
159 | |||
160 | if (spitz_spk_func == ucontrol->value.integer.value[0]) | ||
161 | return 0; | ||
162 | |||
163 | spitz_spk_func = ucontrol->value.integer.value[0]; | ||
164 | spitz_ext_control(codec); | ||
165 | return 1; | ||
166 | } | ||
167 | |||
168 | static int spitz_mic_bias(struct snd_soc_dapm_widget *w, int event) | ||
169 | { | ||
170 | if (machine_is_borzoi() || machine_is_spitz()) { | ||
171 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
172 | set_scoop_gpio(&spitzscoop2_device.dev, | ||
173 | SPITZ_SCP2_MIC_BIAS); | ||
174 | else | ||
175 | reset_scoop_gpio(&spitzscoop2_device.dev, | ||
176 | SPITZ_SCP2_MIC_BIAS); | ||
177 | } | ||
178 | |||
179 | if (machine_is_akita()) { | ||
180 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
181 | akita_set_ioexp(&akitaioexp_device.dev, | ||
182 | AKITA_IOEXP_MIC_BIAS); | ||
183 | else | ||
184 | akita_reset_ioexp(&akitaioexp_device.dev, | ||
185 | AKITA_IOEXP_MIC_BIAS); | ||
186 | } | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* spitz machine dapm widgets */ | ||
191 | static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { | ||
192 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
193 | SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), | ||
194 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | ||
195 | SND_SOC_DAPM_LINE("Line Jack", NULL), | ||
196 | |||
197 | /* headset is a mic and mono headphone */ | ||
198 | SND_SOC_DAPM_HP("Headset Jack", NULL), | ||
199 | }; | ||
200 | |||
201 | /* Spitz machine audio_map */ | ||
202 | static const char *audio_map[][3] = { | ||
203 | |||
204 | /* headphone connected to LOUT1, ROUT1 */ | ||
205 | {"Headphone Jack", NULL, "LOUT1"}, | ||
206 | {"Headphone Jack", NULL, "ROUT1"}, | ||
207 | |||
208 | /* headset connected to ROUT1 and LINPUT1 with bias (def below) */ | ||
209 | {"Headset Jack", NULL, "ROUT1"}, | ||
210 | |||
211 | /* ext speaker connected to LOUT2, ROUT2 */ | ||
212 | {"Ext Spk", NULL , "ROUT2"}, | ||
213 | {"Ext Spk", NULL , "LOUT2"}, | ||
214 | |||
215 | /* mic is connected to input 1 - with bias */ | ||
216 | {"LINPUT1", NULL, "Mic Bias"}, | ||
217 | {"Mic Bias", NULL, "Mic Jack"}, | ||
218 | |||
219 | /* line is connected to input 1 - no bias */ | ||
220 | {"LINPUT1", NULL, "Line Jack"}, | ||
221 | |||
222 | {NULL, NULL, NULL}, | ||
223 | }; | ||
224 | |||
225 | static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", | ||
226 | "Off"}; | ||
227 | static const char *spk_function[] = {"On", "Off"}; | ||
228 | static const struct soc_enum spitz_enum[] = { | ||
229 | SOC_ENUM_SINGLE_EXT(5, jack_function), | ||
230 | SOC_ENUM_SINGLE_EXT(2, spk_function), | ||
231 | }; | ||
232 | |||
233 | static const struct snd_kcontrol_new wm8750_spitz_controls[] = { | ||
234 | SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack, | ||
235 | spitz_set_jack), | ||
236 | SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk, | ||
237 | spitz_set_spk), | ||
238 | }; | ||
239 | |||
240 | /* | ||
241 | * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device | ||
242 | */ | ||
243 | static int spitz_wm8750_init(struct snd_soc_codec *codec) | ||
244 | { | ||
245 | int i, err; | ||
246 | |||
247 | /* NC codec pins */ | ||
248 | snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0); | ||
249 | snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0); | ||
250 | snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0); | ||
251 | snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0); | ||
252 | snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0); | ||
253 | snd_soc_dapm_set_endpoint(codec, "OUT3", 0); | ||
254 | snd_soc_dapm_set_endpoint(codec, "MONO", 0); | ||
255 | |||
256 | /* Add spitz specific controls */ | ||
257 | for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { | ||
258 | err = snd_ctl_add(codec->card, | ||
259 | snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL)); | ||
260 | if (err < 0) | ||
261 | return err; | ||
262 | } | ||
263 | |||
264 | /* Add spitz specific widgets */ | ||
265 | for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) { | ||
266 | snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]); | ||
267 | } | ||
268 | |||
269 | /* Set up spitz specific audio path audio_map */ | ||
270 | for (i = 0; audio_map[i][0] != NULL; i++) { | ||
271 | snd_soc_dapm_connect_input(codec, audio_map[i][0], | ||
272 | audio_map[i][1], audio_map[i][2]); | ||
273 | } | ||
274 | |||
275 | snd_soc_dapm_sync_endpoints(codec); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static unsigned int spitz_config_sysclk(struct snd_soc_pcm_runtime *rtd, | ||
280 | struct snd_soc_clock_info *info) | ||
281 | { | ||
282 | if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) { | ||
283 | /* pxa2xx is i2s master */ | ||
284 | switch (info->rate) { | ||
285 | case 11025: | ||
286 | case 22050: | ||
287 | case 44100: | ||
288 | case 88200: | ||
289 | /* configure codec digital filters | ||
290 | * for 11.025, 22.05, 44.1, 88.2 */ | ||
291 | rtd->codec_dai->config_sysclk(rtd->codec_dai, info, | ||
292 | 11289600); | ||
293 | break; | ||
294 | default: | ||
295 | /* configure codec digital filters for all other rates */ | ||
296 | rtd->codec_dai->config_sysclk(rtd->codec_dai, info, | ||
297 | SPITZ_AUDIO_CLOCK); | ||
298 | break; | ||
299 | } | ||
300 | /* configure pxa2xx i2s interface clocks as master */ | ||
301 | return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, | ||
302 | SPITZ_AUDIO_CLOCK); | ||
303 | } else { | ||
304 | /* codec is i2s master - only configure codec DAI clock */ | ||
305 | return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, | ||
306 | SPITZ_AUDIO_CLOCK); | ||
307 | } | ||
308 | } | ||
309 | |||
310 | /* spitz digital audio interface glue - connects codec <--> CPU */ | ||
311 | static struct snd_soc_dai_link spitz_dai = { | ||
312 | .name = "wm8750", | ||
313 | .stream_name = "WM8750", | ||
314 | .cpu_dai = &pxa_i2s_dai, | ||
315 | .codec_dai = &wm8750_dai, | ||
316 | .init = spitz_wm8750_init, | ||
317 | .config_sysclk = spitz_config_sysclk, | ||
318 | }; | ||
319 | |||
320 | /* spitz audio machine driver */ | ||
321 | static struct snd_soc_machine snd_soc_machine_spitz = { | ||
322 | .name = "Spitz", | ||
323 | .dai_link = &spitz_dai, | ||
324 | .num_links = 1, | ||
325 | .ops = &spitz_ops, | ||
326 | }; | ||
327 | |||
328 | /* spitz audio private data */ | ||
329 | static struct wm8750_setup_data spitz_wm8750_setup = { | ||
330 | .i2c_address = 0x1b, | ||
331 | }; | ||
332 | |||
333 | /* spitz audio subsystem */ | ||
334 | static struct snd_soc_device spitz_snd_devdata = { | ||
335 | .machine = &snd_soc_machine_spitz, | ||
336 | .platform = &pxa2xx_soc_platform, | ||
337 | .codec_dev = &soc_codec_dev_wm8750, | ||
338 | .codec_data = &spitz_wm8750_setup, | ||
339 | }; | ||
340 | |||
341 | static struct platform_device *spitz_snd_device; | ||
342 | |||
343 | static int __init spitz_init(void) | ||
344 | { | ||
345 | int ret; | ||
346 | |||
347 | if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) | ||
348 | return -ENODEV; | ||
349 | |||
350 | spitz_snd_device = platform_device_alloc("soc-audio", -1); | ||
351 | if (!spitz_snd_device) | ||
352 | return -ENOMEM; | ||
353 | |||
354 | platform_set_drvdata(spitz_snd_device, &spitz_snd_devdata); | ||
355 | spitz_snd_devdata.dev = &spitz_snd_device->dev; | ||
356 | ret = platform_device_add(spitz_snd_device); | ||
357 | |||
358 | if (ret) | ||
359 | platform_device_put(spitz_snd_device); | ||
360 | |||
361 | return ret; | ||
362 | } | ||
363 | |||
364 | static void __exit spitz_exit(void) | ||
365 | { | ||
366 | platform_device_unregister(spitz_snd_device); | ||
367 | } | ||
368 | |||
369 | module_init(spitz_init); | ||
370 | module_exit(spitz_exit); | ||
371 | |||
372 | MODULE_AUTHOR("Richard Purdie"); | ||
373 | MODULE_DESCRIPTION("ALSA SoC Spitz"); | ||
374 | MODULE_LICENSE("GPL"); | ||