diff options
author | Marek Vasut <marek.vasut@gmail.com> | 2008-11-03 07:02:12 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2008-11-05 17:11:16 -0500 |
commit | 74e722015fe47c8f0e7ef7c0b4cf32d3e4ae11a0 (patch) | |
tree | fb02245ccb73daf0e4069b47e002479dfcaa5d48 /sound/soc/pxa | |
parent | 0ee4663617fb0f78cec4cc6558a096ccbd8c3ffc (diff) |
ASoC: Add Palm/PXA27x unified ASoC audio driver
this patch adds asoc audio driver for pxa27x based Palm PDAs. I tested it for
palmtx, t5 and ld, it should work with palmz72 as well (slapin, please test).
I sent it here some time ago, but now I got to fixing bugs in it. It should
be somehow mostly ok and ready for applying.
[Converted to use snd_soc_dapm_nc_pin() and bool Kconfig -- broonie]
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/pxa')
-rw-r--r-- | sound/soc/pxa/Kconfig | 9 | ||||
-rw-r--r-- | sound/soc/pxa/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/pxa/palm27x.c | 269 |
3 files changed, 280 insertions, 0 deletions
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 4235524238f9..f82e10699471 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig | |||
@@ -79,6 +79,15 @@ config SND_PXA2XX_SOC_EM_X270 | |||
79 | Say Y if you want to add support for SoC audio on | 79 | Say Y if you want to add support for SoC audio on |
80 | CompuLab EM-x270. | 80 | CompuLab EM-x270. |
81 | 81 | ||
82 | config SND_PXA2XX_SOC_PALM27X | ||
83 | bool "SoC Audio support for Palm T|X, T5 and LifeDrive" | ||
84 | depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || MACH_PALMT5) | ||
85 | select SND_PXA2XX_SOC_AC97 | ||
86 | select SND_SOC_WM9712 | ||
87 | help | ||
88 | Say Y if you want to add support for SoC audio on | ||
89 | Palm T|X, T5 or LifeDrive handheld computer. | ||
90 | |||
82 | config SND_SOC_ZYLONITE | 91 | config SND_SOC_ZYLONITE |
83 | tristate "SoC Audio support for Marvell Zylonite" | 92 | tristate "SoC Audio support for Marvell Zylonite" |
84 | depends on SND_PXA2XX_SOC && MACH_ZYLONITE | 93 | depends on SND_PXA2XX_SOC && MACH_ZYLONITE |
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 00258abb84a8..1a3a36e75bf0 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile | |||
@@ -16,6 +16,7 @@ snd-soc-tosa-objs := tosa.o | |||
16 | snd-soc-e800-objs := e800_wm9712.o | 16 | snd-soc-e800-objs := e800_wm9712.o |
17 | snd-soc-spitz-objs := spitz.o | 17 | snd-soc-spitz-objs := spitz.o |
18 | snd-soc-em-x270-objs := em-x270.o | 18 | snd-soc-em-x270-objs := em-x270.o |
19 | snd-soc-palm27x-objs := palm27x.o | ||
19 | 20 | ||
20 | obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o | 21 | obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o |
21 | obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o | 22 | obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o |
@@ -23,3 +24,4 @@ obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o | |||
23 | obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o | 24 | obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o |
24 | obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o | 25 | obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o |
25 | obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o | 26 | obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o |
27 | obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o | ||
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c new file mode 100644 index 000000000000..e364abc700db --- /dev/null +++ b/sound/soc/pxa/palm27x.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * linux/sound/soc/pxa/palm27x.c | ||
3 | * | ||
4 | * SoC Audio driver for Palm T|X, T5 and LifeDrive | ||
5 | * | ||
6 | * based on tosa.c | ||
7 | * | ||
8 | * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/moduleparam.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/gpio.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/irq.h> | ||
22 | |||
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 <mach/audio.h> | ||
30 | #include <mach/palmasoc.h> | ||
31 | |||
32 | #include "../codecs/wm9712.h" | ||
33 | #include "pxa2xx-pcm.h" | ||
34 | #include "pxa2xx-ac97.h" | ||
35 | |||
36 | static int palm27x_jack_func = 1; | ||
37 | static int palm27x_spk_func = 1; | ||
38 | static int palm27x_ep_gpio = -1; | ||
39 | |||
40 | static void palm27x_ext_control(struct snd_soc_codec *codec) | ||
41 | { | ||
42 | if (!palm27x_spk_func) | ||
43 | snd_soc_dapm_enable_pin(codec, "Speaker"); | ||
44 | else | ||
45 | snd_soc_dapm_disable_pin(codec, "Speaker"); | ||
46 | |||
47 | if (!palm27x_jack_func) | ||
48 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | ||
49 | else | ||
50 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | ||
51 | |||
52 | snd_soc_dapm_sync(codec); | ||
53 | } | ||
54 | |||
55 | static int palm27x_startup(struct snd_pcm_substream *substream) | ||
56 | { | ||
57 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
58 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
59 | |||
60 | /* check the jack status at stream startup */ | ||
61 | palm27x_ext_control(codec); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static struct snd_soc_ops palm27x_ops = { | ||
66 | .startup = palm27x_startup, | ||
67 | }; | ||
68 | |||
69 | static irqreturn_t palm27x_interrupt(int irq, void *v) | ||
70 | { | ||
71 | palm27x_spk_func = gpio_get_value(palm27x_ep_gpio); | ||
72 | palm27x_jack_func = !palm27x_spk_func; | ||
73 | return IRQ_HANDLED; | ||
74 | } | ||
75 | |||
76 | static int palm27x_get_jack(struct snd_kcontrol *kcontrol, | ||
77 | struct snd_ctl_elem_value *ucontrol) | ||
78 | { | ||
79 | ucontrol->value.integer.value[0] = palm27x_jack_func; | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int palm27x_set_jack(struct snd_kcontrol *kcontrol, | ||
84 | struct snd_ctl_elem_value *ucontrol) | ||
85 | { | ||
86 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
87 | |||
88 | if (palm27x_jack_func == ucontrol->value.integer.value[0]) | ||
89 | return 0; | ||
90 | |||
91 | palm27x_jack_func = ucontrol->value.integer.value[0]; | ||
92 | palm27x_ext_control(codec); | ||
93 | return 1; | ||
94 | } | ||
95 | |||
96 | static int palm27x_get_spk(struct snd_kcontrol *kcontrol, | ||
97 | struct snd_ctl_elem_value *ucontrol) | ||
98 | { | ||
99 | ucontrol->value.integer.value[0] = palm27x_spk_func; | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int palm27x_set_spk(struct snd_kcontrol *kcontrol, | ||
104 | struct snd_ctl_elem_value *ucontrol) | ||
105 | { | ||
106 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
107 | |||
108 | if (palm27x_spk_func == ucontrol->value.integer.value[0]) | ||
109 | return 0; | ||
110 | |||
111 | palm27x_spk_func = ucontrol->value.integer.value[0]; | ||
112 | palm27x_ext_control(codec); | ||
113 | return 1; | ||
114 | } | ||
115 | |||
116 | /* PalmTX machine dapm widgets */ | ||
117 | static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = { | ||
118 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
119 | SND_SOC_DAPM_SPK("Speaker", NULL), | ||
120 | }; | ||
121 | |||
122 | /* PalmTX audio map */ | ||
123 | static const struct snd_soc_dapm_route audio_map[] = { | ||
124 | /* headphone connected to HPOUTL, HPOUTR */ | ||
125 | {"Headphone Jack", NULL, "HPOUTL"}, | ||
126 | {"Headphone Jack", NULL, "HPOUTR"}, | ||
127 | |||
128 | /* ext speaker connected to ROUT2, LOUT2 */ | ||
129 | {"Speaker", NULL, "LOUT2"}, | ||
130 | {"Speaker", NULL, "ROUT2"}, | ||
131 | }; | ||
132 | |||
133 | static const char *jack_function[] = {"Headphone", "Off"}; | ||
134 | static const char *spk_function[] = {"On", "Off"}; | ||
135 | static const struct soc_enum palm27x_enum[] = { | ||
136 | SOC_ENUM_SINGLE_EXT(2, jack_function), | ||
137 | SOC_ENUM_SINGLE_EXT(2, spk_function), | ||
138 | }; | ||
139 | |||
140 | static const struct snd_kcontrol_new palm27x_controls[] = { | ||
141 | SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack, | ||
142 | palm27x_set_jack), | ||
143 | SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk, | ||
144 | palm27x_set_spk), | ||
145 | }; | ||
146 | |||
147 | static int palm27x_ac97_init(struct snd_soc_codec *codec) | ||
148 | { | ||
149 | int i, err; | ||
150 | |||
151 | snd_soc_dapm_nc_pin(codec, "OUT3"); | ||
152 | snd_soc_dapm_nc_pin(codec, "MONOOUT"); | ||
153 | |||
154 | /* add palm27x specific controls */ | ||
155 | for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) { | ||
156 | err = snd_ctl_add(codec->card, | ||
157 | snd_soc_cnew(&palm27x_controls[i], | ||
158 | codec, NULL)); | ||
159 | if (err < 0) | ||
160 | return err; | ||
161 | } | ||
162 | |||
163 | /* add palm27x specific widgets */ | ||
164 | snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets, | ||
165 | ARRAY_SIZE(palm27x_dapm_widgets)); | ||
166 | |||
167 | /* set up palm27x specific audio path audio_map */ | ||
168 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
169 | |||
170 | snd_soc_dapm_sync(codec); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct snd_soc_dai_link palm27x_dai[] = { | ||
175 | { | ||
176 | .name = "AC97 HiFi", | ||
177 | .stream_name = "AC97 HiFi", | ||
178 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], | ||
179 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], | ||
180 | .init = palm27x_ac97_init, | ||
181 | .ops = &palm27x_ops, | ||
182 | }, | ||
183 | { | ||
184 | .name = "AC97 Aux", | ||
185 | .stream_name = "AC97 Aux", | ||
186 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], | ||
187 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], | ||
188 | .ops = &palm27x_ops, | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | static struct snd_soc_machine palm27x_asoc = { | ||
193 | .name = "Palm/PXA27x", | ||
194 | .dai_link = palm27x_dai, | ||
195 | .num_links = ARRAY_SIZE(palm27x_dai), | ||
196 | }; | ||
197 | |||
198 | static struct snd_soc_device palm27x_snd_devdata = { | ||
199 | .machine = &palm27x_asoc, | ||
200 | .platform = &pxa2xx_soc_platform, | ||
201 | .codec_dev = &soc_codec_dev_wm9712, | ||
202 | }; | ||
203 | |||
204 | static struct platform_device *palm27x_snd_device; | ||
205 | |||
206 | static int __init palm27x_asoc_init(void) | ||
207 | { | ||
208 | int ret; | ||
209 | |||
210 | if (!(machine_is_palmtx() || machine_is_palmt5() || | ||
211 | machine_is_palmld())) | ||
212 | return -ENODEV; | ||
213 | |||
214 | ret = gpio_request(palm27x_ep_gpio, "Headphone Jack"); | ||
215 | if (ret) | ||
216 | return ret; | ||
217 | ret = gpio_direction_input(palm27x_ep_gpio); | ||
218 | if (ret) | ||
219 | goto err_alloc; | ||
220 | |||
221 | if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt, | ||
222 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
223 | "Headphone jack", NULL)) | ||
224 | goto err_alloc; | ||
225 | |||
226 | palm27x_snd_device = platform_device_alloc("soc-audio", -1); | ||
227 | if (!palm27x_snd_device) { | ||
228 | ret = -ENOMEM; | ||
229 | goto err_dev; | ||
230 | } | ||
231 | |||
232 | platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata); | ||
233 | palm27x_snd_devdata.dev = &palm27x_snd_device->dev; | ||
234 | ret = platform_device_add(palm27x_snd_device); | ||
235 | |||
236 | if (ret != 0) | ||
237 | goto put_device; | ||
238 | |||
239 | return 0; | ||
240 | |||
241 | put_device: | ||
242 | platform_device_put(palm27x_snd_device); | ||
243 | err_dev: | ||
244 | free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); | ||
245 | err_alloc: | ||
246 | gpio_free(palm27x_ep_gpio); | ||
247 | |||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | static void __exit palm27x_asoc_exit(void) | ||
252 | { | ||
253 | free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); | ||
254 | gpio_free(palm27x_ep_gpio); | ||
255 | platform_device_unregister(palm27x_snd_device); | ||
256 | } | ||
257 | |||
258 | void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) | ||
259 | { | ||
260 | palm27x_ep_gpio = data->jack_gpio; | ||
261 | } | ||
262 | |||
263 | module_init(palm27x_asoc_init); | ||
264 | module_exit(palm27x_asoc_exit); | ||
265 | |||
266 | /* Module information */ | ||
267 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | ||
268 | MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive"); | ||
269 | MODULE_LICENSE("GPL"); | ||