diff options
author | Takashi Iwai <tiwai@suse.de> | 2008-12-25 05:40:25 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-12-25 05:40:25 -0500 |
commit | 5c8261e44eaebbc91f9fc1bbd3f3167e91a50a57 (patch) | |
tree | 6b932687edc73c07e544ccba3f0130fdb257d902 /sound/soc/pxa/palm27x.c | |
parent | facef8685b3ff95c01c33d9d836401d0dd26211d (diff) | |
parent | 472346da9cc4231bec03ff2032e0d5fd4037232c (diff) |
Merge branch 'topic/asoc' into to-push
Diffstat (limited to 'sound/soc/pxa/palm27x.c')
-rw-r--r-- | sound/soc/pxa/palm27x.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c new file mode 100644 index 000000000000..4a9cf3083af0 --- /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_card palm27x_asoc = { | ||
193 | .name = "Palm/PXA27x", | ||
194 | .platform = &pxa2xx_soc_platform, | ||
195 | .dai_link = palm27x_dai, | ||
196 | .num_links = ARRAY_SIZE(palm27x_dai), | ||
197 | }; | ||
198 | |||
199 | static struct snd_soc_device palm27x_snd_devdata = { | ||
200 | .card = &palm27x_asoc, | ||
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"); | ||