diff options
author | Jarkko Nikula <jhnikula@gmail.com> | 2010-05-05 04:14:22 -0400 |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-05-06 04:50:11 -0400 |
commit | 49100c98359a56ea4e8c9a76e3d625cdb25f25f5 (patch) | |
tree | bd20c5bbf1fd5c17b0ba07c6406540d52a372935 | |
parent | e5e5b31e8c729b6bae569bec0790c655ee0121a1 (diff) |
ASoC: omap: Add basic audio support for Nokia RX-51/N900
This patch adds support for integrated stereo speakers and digital
microphone found on Nokia RX-51 hardware. This is a cut down version based
on Maemo kernel sources and earlier patchset by Eduardo Valentin et al.
http://mailman.alsa-project.org/pipermail/alsa-devel/2009-October/022033.html
Signed-off-by: Jarkko Nikula <jhnikula@gmail.com>
Cc: Eduardo Valentin <eduardo.valentin@nokia.com>
Cc: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Eduardo Valentin <eduardo.valentin@nokia.com>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
-rw-r--r-- | sound/soc/omap/Kconfig | 10 | ||||
-rw-r--r-- | sound/soc/omap/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/omap/rx51.c | 294 |
3 files changed, 306 insertions, 0 deletions
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index f11963c21873..83be4a76d2bb 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -18,6 +18,16 @@ config SND_OMAP_SOC_N810 | |||
18 | help | 18 | help |
19 | Say Y if you want to add support for SoC audio on Nokia N810. | 19 | Say Y if you want to add support for SoC audio on Nokia N810. |
20 | 20 | ||
21 | config SND_OMAP_SOC_RX51 | ||
22 | tristate "SoC Audio support for Nokia RX-51" | ||
23 | depends on SND_OMAP_SOC && MACH_NOKIA_RX51 | ||
24 | select OMAP_MCBSP | ||
25 | select SND_OMAP_SOC_MCBSP | ||
26 | select SND_SOC_TLV320AIC3X | ||
27 | help | ||
28 | Say Y if you want to add support for SoC audio on Nokia RX-51 | ||
29 | hardware. This is also known as Nokia N900 product. | ||
30 | |||
21 | config SND_OMAP_SOC_AMS_DELTA | 31 | config SND_OMAP_SOC_AMS_DELTA |
22 | tristate "SoC Audio support for Amstrad E3 (Delta) videophone" | 32 | tristate "SoC Audio support for Amstrad E3 (Delta) videophone" |
23 | depends on SND_OMAP_SOC && MACH_AMS_DELTA | 33 | depends on SND_OMAP_SOC && MACH_AMS_DELTA |
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 0bc00ca14b37..3a75755f25e4 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o | |||
9 | 9 | ||
10 | # OMAP Machine Support | 10 | # OMAP Machine Support |
11 | snd-soc-n810-objs := n810.o | 11 | snd-soc-n810-objs := n810.o |
12 | snd-soc-rx51-objs := rx51.o | ||
12 | snd-soc-ams-delta-objs := ams-delta.o | 13 | snd-soc-ams-delta-objs := ams-delta.o |
13 | snd-soc-osk5912-objs := osk5912.o | 14 | snd-soc-osk5912-objs := osk5912.o |
14 | snd-soc-overo-objs := overo.o | 15 | snd-soc-overo-objs := overo.o |
@@ -22,6 +23,7 @@ snd-soc-zoom2-objs := zoom2.o | |||
22 | snd-soc-igep0020-objs := igep0020.o | 23 | snd-soc-igep0020-objs := igep0020.o |
23 | 24 | ||
24 | obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o | 25 | obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o |
26 | obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o | ||
25 | obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o | 27 | obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o |
26 | obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o | 28 | obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o |
27 | obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o | 29 | obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o |
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c new file mode 100644 index 000000000000..47d831ef2dbb --- /dev/null +++ b/sound/soc/omap/rx51.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * rx51.c -- SoC audio for Nokia RX-51 | ||
3 | * | ||
4 | * Copyright (C) 2008 - 2009 Nokia Corporation | ||
5 | * | ||
6 | * Contact: Peter Ujfalusi <peter.ujfalusi@nokia.com> | ||
7 | * Eduardo Valentin <eduardo.valentin@nokia.com> | ||
8 | * Jarkko Nikula <jhnikula@gmail.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * version 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
22 | * 02110-1301 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/delay.h> | ||
27 | #include <linux/gpio.h> | ||
28 | #include <linux/platform_device.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 | |||
36 | #include "omap-mcbsp.h" | ||
37 | #include "omap-pcm.h" | ||
38 | #include "../codecs/tlv320aic3x.h" | ||
39 | |||
40 | /* | ||
41 | * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This | ||
42 | * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c | ||
43 | */ | ||
44 | #define RX51_SPEAKER_AMP_TWL_GPIO (192 + 7) | ||
45 | |||
46 | static int rx51_spk_func; | ||
47 | static int rx51_dmic_func; | ||
48 | |||
49 | static void rx51_ext_control(struct snd_soc_codec *codec) | ||
50 | { | ||
51 | if (rx51_spk_func) | ||
52 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | ||
53 | else | ||
54 | snd_soc_dapm_disable_pin(codec, "Ext Spk"); | ||
55 | if (rx51_dmic_func) | ||
56 | snd_soc_dapm_enable_pin(codec, "DMic"); | ||
57 | else | ||
58 | snd_soc_dapm_disable_pin(codec, "DMic"); | ||
59 | |||
60 | snd_soc_dapm_sync(codec); | ||
61 | } | ||
62 | |||
63 | static int rx51_startup(struct snd_pcm_substream *substream) | ||
64 | { | ||
65 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
66 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
67 | struct snd_soc_codec *codec = rtd->socdev->card->codec; | ||
68 | |||
69 | snd_pcm_hw_constraint_minmax(runtime, | ||
70 | SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); | ||
71 | rx51_ext_control(codec); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int rx51_hw_params(struct snd_pcm_substream *substream, | ||
77 | struct snd_pcm_hw_params *params) | ||
78 | { | ||
79 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
80 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
81 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
82 | int err; | ||
83 | |||
84 | /* Set codec DAI configuration */ | ||
85 | err = snd_soc_dai_set_fmt(codec_dai, | ||
86 | SND_SOC_DAIFMT_DSP_A | | ||
87 | SND_SOC_DAIFMT_IB_NF | | ||
88 | SND_SOC_DAIFMT_CBM_CFM); | ||
89 | if (err < 0) | ||
90 | return err; | ||
91 | |||
92 | /* Set cpu DAI configuration */ | ||
93 | err = snd_soc_dai_set_fmt(cpu_dai, | ||
94 | SND_SOC_DAIFMT_DSP_A | | ||
95 | SND_SOC_DAIFMT_IB_NF | | ||
96 | SND_SOC_DAIFMT_CBM_CFM); | ||
97 | if (err < 0) | ||
98 | return err; | ||
99 | |||
100 | /* Set the codec system clock for DAC and ADC */ | ||
101 | return snd_soc_dai_set_sysclk(codec_dai, 0, 19200000, | ||
102 | SND_SOC_CLOCK_IN); | ||
103 | } | ||
104 | |||
105 | static struct snd_soc_ops rx51_ops = { | ||
106 | .startup = rx51_startup, | ||
107 | .hw_params = rx51_hw_params, | ||
108 | }; | ||
109 | |||
110 | static int rx51_get_spk(struct snd_kcontrol *kcontrol, | ||
111 | struct snd_ctl_elem_value *ucontrol) | ||
112 | { | ||
113 | ucontrol->value.integer.value[0] = rx51_spk_func; | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int rx51_set_spk(struct snd_kcontrol *kcontrol, | ||
119 | struct snd_ctl_elem_value *ucontrol) | ||
120 | { | ||
121 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
122 | |||
123 | if (rx51_spk_func == ucontrol->value.integer.value[0]) | ||
124 | return 0; | ||
125 | |||
126 | rx51_spk_func = ucontrol->value.integer.value[0]; | ||
127 | rx51_ext_control(codec); | ||
128 | |||
129 | return 1; | ||
130 | } | ||
131 | |||
132 | static int rx51_spk_event(struct snd_soc_dapm_widget *w, | ||
133 | struct snd_kcontrol *k, int event) | ||
134 | { | ||
135 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
136 | gpio_set_value(RX51_SPEAKER_AMP_TWL_GPIO, 1); | ||
137 | else | ||
138 | gpio_set_value(RX51_SPEAKER_AMP_TWL_GPIO, 0); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static int rx51_get_input(struct snd_kcontrol *kcontrol, | ||
144 | struct snd_ctl_elem_value *ucontrol) | ||
145 | { | ||
146 | ucontrol->value.integer.value[0] = rx51_dmic_func; | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static int rx51_set_input(struct snd_kcontrol *kcontrol, | ||
152 | struct snd_ctl_elem_value *ucontrol) | ||
153 | { | ||
154 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
155 | |||
156 | if (rx51_dmic_func == ucontrol->value.integer.value[0]) | ||
157 | return 0; | ||
158 | |||
159 | rx51_dmic_func = ucontrol->value.integer.value[0]; | ||
160 | rx51_ext_control(codec); | ||
161 | |||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { | ||
166 | SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), | ||
167 | SND_SOC_DAPM_MIC("DMic", NULL), | ||
168 | }; | ||
169 | |||
170 | static const struct snd_soc_dapm_route audio_map[] = { | ||
171 | {"Ext Spk", NULL, "HPLOUT"}, | ||
172 | {"Ext Spk", NULL, "HPROUT"}, | ||
173 | |||
174 | {"DMic Rate 64", NULL, "Mic Bias 2V"}, | ||
175 | {"Mic Bias 2V", NULL, "DMic"}, | ||
176 | }; | ||
177 | |||
178 | static const char *spk_function[] = {"Off", "On"}; | ||
179 | static const char *input_function[] = {"ADC", "Digital Mic"}; | ||
180 | |||
181 | static const struct soc_enum rx51_enum[] = { | ||
182 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), | ||
183 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), | ||
184 | }; | ||
185 | |||
186 | static const struct snd_kcontrol_new aic34_rx51_controls[] = { | ||
187 | SOC_ENUM_EXT("Speaker Function", rx51_enum[0], | ||
188 | rx51_get_spk, rx51_set_spk), | ||
189 | SOC_ENUM_EXT("Input Select", rx51_enum[1], | ||
190 | rx51_get_input, rx51_set_input), | ||
191 | }; | ||
192 | |||
193 | static int rx51_aic34_init(struct snd_soc_codec *codec) | ||
194 | { | ||
195 | int err; | ||
196 | |||
197 | /* Set up NC codec pins */ | ||
198 | snd_soc_dapm_nc_pin(codec, "MIC3L"); | ||
199 | snd_soc_dapm_nc_pin(codec, "MIC3R"); | ||
200 | snd_soc_dapm_nc_pin(codec, "LINE1R"); | ||
201 | |||
202 | /* Add RX-51 specific controls */ | ||
203 | err = snd_soc_add_controls(codec, aic34_rx51_controls, | ||
204 | ARRAY_SIZE(aic34_rx51_controls)); | ||
205 | if (err < 0) | ||
206 | return err; | ||
207 | |||
208 | /* Add RX-51 specific widgets */ | ||
209 | snd_soc_dapm_new_controls(codec, aic34_dapm_widgets, | ||
210 | ARRAY_SIZE(aic34_dapm_widgets)); | ||
211 | |||
212 | /* Set up RX-51 specific audio path audio_map */ | ||
213 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
214 | |||
215 | snd_soc_dapm_sync(codec); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
221 | static struct snd_soc_dai_link rx51_dai[] = { | ||
222 | { | ||
223 | .name = "TLV320AIC34", | ||
224 | .stream_name = "AIC34", | ||
225 | .cpu_dai = &omap_mcbsp_dai[0], | ||
226 | .codec_dai = &aic3x_dai, | ||
227 | .init = rx51_aic34_init, | ||
228 | .ops = &rx51_ops, | ||
229 | }, | ||
230 | }; | ||
231 | |||
232 | /* Audio private data */ | ||
233 | static struct aic3x_setup_data rx51_aic34_setup = { | ||
234 | .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED, | ||
235 | .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, | ||
236 | }; | ||
237 | |||
238 | /* Audio card */ | ||
239 | static struct snd_soc_card rx51_sound_card = { | ||
240 | .name = "RX-51", | ||
241 | .dai_link = rx51_dai, | ||
242 | .num_links = ARRAY_SIZE(rx51_dai), | ||
243 | .platform = &omap_soc_platform, | ||
244 | }; | ||
245 | |||
246 | /* Audio subsystem */ | ||
247 | static struct snd_soc_device rx51_snd_devdata = { | ||
248 | .card = &rx51_sound_card, | ||
249 | .codec_dev = &soc_codec_dev_aic3x, | ||
250 | .codec_data = &rx51_aic34_setup, | ||
251 | }; | ||
252 | |||
253 | static struct platform_device *rx51_snd_device; | ||
254 | |||
255 | static int __init rx51_soc_init(void) | ||
256 | { | ||
257 | int err; | ||
258 | |||
259 | if (!machine_is_nokia_rx51()) | ||
260 | return -ENODEV; | ||
261 | |||
262 | rx51_snd_device = platform_device_alloc("soc-audio", -1); | ||
263 | if (!rx51_snd_device) { | ||
264 | err = -ENOMEM; | ||
265 | goto err1; | ||
266 | } | ||
267 | |||
268 | platform_set_drvdata(rx51_snd_device, &rx51_snd_devdata); | ||
269 | rx51_snd_devdata.dev = &rx51_snd_device->dev; | ||
270 | *(unsigned int *)rx51_dai[0].cpu_dai->private_data = 1; /* McBSP2 */ | ||
271 | |||
272 | err = platform_device_add(rx51_snd_device); | ||
273 | if (err) | ||
274 | goto err2; | ||
275 | |||
276 | return 0; | ||
277 | err2: | ||
278 | platform_device_put(rx51_snd_device); | ||
279 | err1: | ||
280 | |||
281 | return err; | ||
282 | } | ||
283 | |||
284 | static void __exit rx51_soc_exit(void) | ||
285 | { | ||
286 | platform_device_unregister(rx51_snd_device); | ||
287 | } | ||
288 | |||
289 | module_init(rx51_soc_init); | ||
290 | module_exit(rx51_soc_exit); | ||
291 | |||
292 | MODULE_AUTHOR("Nokia Corporation"); | ||
293 | MODULE_DESCRIPTION("ALSA SoC Nokia RX-51"); | ||
294 | MODULE_LICENSE("GPL"); | ||