diff options
Diffstat (limited to 'sound/soc/omap')
-rw-r--r-- | sound/soc/omap/Kconfig | 15 | ||||
-rw-r--r-- | sound/soc/omap/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/omap/ams-delta.c | 646 | ||||
-rw-r--r-- | sound/soc/omap/n810.c | 12 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.c | 123 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.h | 4 | ||||
-rw-r--r-- | sound/soc/omap/omap-pcm.c | 53 | ||||
-rw-r--r-- | sound/soc/omap/omap-pcm.h | 2 | ||||
-rw-r--r-- | sound/soc/omap/sdp3430.c | 18 | ||||
-rw-r--r-- | sound/soc/omap/zoom2.c | 314 |
10 files changed, 1159 insertions, 32 deletions
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index b771238662b6..2dee9839be86 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -15,6 +15,14 @@ config SND_OMAP_SOC_N810 | |||
15 | help | 15 | help |
16 | Say Y if you want to add support for SoC audio on Nokia N810. | 16 | Say Y if you want to add support for SoC audio on Nokia N810. |
17 | 17 | ||
18 | config SND_OMAP_SOC_AMS_DELTA | ||
19 | tristate "SoC Audio support for Amstrad E3 (Delta) videophone" | ||
20 | depends on SND_OMAP_SOC && MACH_AMS_DELTA | ||
21 | select SND_OMAP_SOC_MCBSP | ||
22 | select SND_SOC_CX20442 | ||
23 | help | ||
24 | Say Y if you want to add support for SoC audio on Amstrad Delta. | ||
25 | |||
18 | config SND_OMAP_SOC_OSK5912 | 26 | config SND_OMAP_SOC_OSK5912 |
19 | tristate "SoC Audio support for omap osk5912" | 27 | tristate "SoC Audio support for omap osk5912" |
20 | depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C | 28 | depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C |
@@ -72,4 +80,11 @@ config SND_OMAP_SOC_OMAP3_BEAGLE | |||
72 | help | 80 | help |
73 | Say Y if you want to add support for SoC audio on the Beagleboard. | 81 | Say Y if you want to add support for SoC audio on the Beagleboard. |
74 | 82 | ||
83 | config SND_OMAP_SOC_ZOOM2 | ||
84 | tristate "SoC Audio support for Zoom2" | ||
85 | depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2 | ||
86 | select SND_OMAP_SOC_MCBSP | ||
87 | select SND_SOC_TWL4030 | ||
88 | help | ||
89 | Say Y if you want to add support for Soc audio on Zoom2 board. | ||
75 | 90 | ||
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index a37f49862389..02d69471dcb5 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o | |||
7 | 7 | ||
8 | # OMAP Machine Support | 8 | # OMAP Machine Support |
9 | snd-soc-n810-objs := n810.o | 9 | snd-soc-n810-objs := n810.o |
10 | snd-soc-ams-delta-objs := ams-delta.o | ||
10 | snd-soc-osk5912-objs := osk5912.o | 11 | snd-soc-osk5912-objs := osk5912.o |
11 | snd-soc-overo-objs := overo.o | 12 | snd-soc-overo-objs := overo.o |
12 | snd-soc-omap2evm-objs := omap2evm.o | 13 | snd-soc-omap2evm-objs := omap2evm.o |
@@ -14,8 +15,10 @@ snd-soc-omap3evm-objs := omap3evm.o | |||
14 | snd-soc-sdp3430-objs := sdp3430.o | 15 | snd-soc-sdp3430-objs := sdp3430.o |
15 | snd-soc-omap3pandora-objs := omap3pandora.o | 16 | snd-soc-omap3pandora-objs := omap3pandora.o |
16 | snd-soc-omap3beagle-objs := omap3beagle.o | 17 | snd-soc-omap3beagle-objs := omap3beagle.o |
18 | snd-soc-zoom2-objs := zoom2.o | ||
17 | 19 | ||
18 | obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o | 20 | obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o |
21 | obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o | ||
19 | obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o | 22 | obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o |
20 | obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o | 23 | obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o |
21 | obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o | 24 | obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o |
@@ -23,3 +26,4 @@ obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o | |||
23 | obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o | 26 | obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o |
24 | obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o | 27 | obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o |
25 | obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o | 28 | obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o |
29 | obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o | ||
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c new file mode 100644 index 000000000000..5a5166ac7279 --- /dev/null +++ b/sound/soc/omap/ams-delta.c | |||
@@ -0,0 +1,646 @@ | |||
1 | /* | ||
2 | * ams-delta.c -- SoC audio for Amstrad E3 (Delta) videophone | ||
3 | * | ||
4 | * Copyright (C) 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> | ||
5 | * | ||
6 | * Initially based on sound/soc/omap/osk5912.x | ||
7 | * Copyright (C) 2008 Mistral Solutions | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * version 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
21 | * 02110-1301 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <linux/gpio.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | #include <linux/tty.h> | ||
28 | |||
29 | #include <sound/soc-dapm.h> | ||
30 | #include <sound/jack.h> | ||
31 | |||
32 | #include <asm/mach-types.h> | ||
33 | |||
34 | #include <mach/board-ams-delta.h> | ||
35 | #include <mach/mcbsp.h> | ||
36 | |||
37 | #include "omap-mcbsp.h" | ||
38 | #include "omap-pcm.h" | ||
39 | #include "../codecs/cx20442.h" | ||
40 | |||
41 | |||
42 | /* Board specific DAPM widgets */ | ||
43 | const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = { | ||
44 | /* Handset */ | ||
45 | SND_SOC_DAPM_MIC("Mouthpiece", NULL), | ||
46 | SND_SOC_DAPM_HP("Earpiece", NULL), | ||
47 | /* Handsfree/Speakerphone */ | ||
48 | SND_SOC_DAPM_MIC("Microphone", NULL), | ||
49 | SND_SOC_DAPM_SPK("Speaker", NULL), | ||
50 | }; | ||
51 | |||
52 | /* How they are connected to codec pins */ | ||
53 | static const struct snd_soc_dapm_route ams_delta_audio_map[] = { | ||
54 | {"TELIN", NULL, "Mouthpiece"}, | ||
55 | {"Earpiece", NULL, "TELOUT"}, | ||
56 | |||
57 | {"MIC", NULL, "Microphone"}, | ||
58 | {"Speaker", NULL, "SPKOUT"}, | ||
59 | }; | ||
60 | |||
61 | /* | ||
62 | * Controls, functional after the modem line discipline is activated. | ||
63 | */ | ||
64 | |||
65 | /* Virtual switch: audio input/output constellations */ | ||
66 | static const char *ams_delta_audio_mode[] = | ||
67 | {"Mixed", "Handset", "Handsfree", "Speakerphone"}; | ||
68 | |||
69 | /* Selection <-> pin translation */ | ||
70 | #define AMS_DELTA_MOUTHPIECE 0 | ||
71 | #define AMS_DELTA_EARPIECE 1 | ||
72 | #define AMS_DELTA_MICROPHONE 2 | ||
73 | #define AMS_DELTA_SPEAKER 3 | ||
74 | #define AMS_DELTA_AGC 4 | ||
75 | |||
76 | #define AMS_DELTA_MIXED ((1 << AMS_DELTA_EARPIECE) | \ | ||
77 | (1 << AMS_DELTA_MICROPHONE)) | ||
78 | #define AMS_DELTA_HANDSET ((1 << AMS_DELTA_MOUTHPIECE) | \ | ||
79 | (1 << AMS_DELTA_EARPIECE)) | ||
80 | #define AMS_DELTA_HANDSFREE ((1 << AMS_DELTA_MICROPHONE) | \ | ||
81 | (1 << AMS_DELTA_SPEAKER)) | ||
82 | #define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC)) | ||
83 | |||
84 | unsigned short ams_delta_audio_mode_pins[] = { | ||
85 | AMS_DELTA_MIXED, | ||
86 | AMS_DELTA_HANDSET, | ||
87 | AMS_DELTA_HANDSFREE, | ||
88 | AMS_DELTA_SPEAKERPHONE, | ||
89 | }; | ||
90 | |||
91 | static unsigned short ams_delta_audio_agc; | ||
92 | |||
93 | static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, | ||
94 | struct snd_ctl_elem_value *ucontrol) | ||
95 | { | ||
96 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
97 | struct soc_enum *control = (struct soc_enum *)kcontrol->private_value; | ||
98 | unsigned short pins; | ||
99 | int pin, changed = 0; | ||
100 | |||
101 | /* Refuse any mode changes if we are not able to control the codec. */ | ||
102 | if (!codec->control_data) | ||
103 | return -EUNATCH; | ||
104 | |||
105 | if (ucontrol->value.enumerated.item[0] >= control->max) | ||
106 | return -EINVAL; | ||
107 | |||
108 | mutex_lock(&codec->mutex); | ||
109 | |||
110 | /* Translate selection to bitmap */ | ||
111 | pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]]; | ||
112 | |||
113 | /* Setup pins after corresponding bits if changed */ | ||
114 | pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE)); | ||
115 | if (pin != snd_soc_dapm_get_pin_status(codec, "Mouthpiece")) { | ||
116 | changed = 1; | ||
117 | if (pin) | ||
118 | snd_soc_dapm_enable_pin(codec, "Mouthpiece"); | ||
119 | else | ||
120 | snd_soc_dapm_disable_pin(codec, "Mouthpiece"); | ||
121 | } | ||
122 | pin = !!(pins & (1 << AMS_DELTA_EARPIECE)); | ||
123 | if (pin != snd_soc_dapm_get_pin_status(codec, "Earpiece")) { | ||
124 | changed = 1; | ||
125 | if (pin) | ||
126 | snd_soc_dapm_enable_pin(codec, "Earpiece"); | ||
127 | else | ||
128 | snd_soc_dapm_disable_pin(codec, "Earpiece"); | ||
129 | } | ||
130 | pin = !!(pins & (1 << AMS_DELTA_MICROPHONE)); | ||
131 | if (pin != snd_soc_dapm_get_pin_status(codec, "Microphone")) { | ||
132 | changed = 1; | ||
133 | if (pin) | ||
134 | snd_soc_dapm_enable_pin(codec, "Microphone"); | ||
135 | else | ||
136 | snd_soc_dapm_disable_pin(codec, "Microphone"); | ||
137 | } | ||
138 | pin = !!(pins & (1 << AMS_DELTA_SPEAKER)); | ||
139 | if (pin != snd_soc_dapm_get_pin_status(codec, "Speaker")) { | ||
140 | changed = 1; | ||
141 | if (pin) | ||
142 | snd_soc_dapm_enable_pin(codec, "Speaker"); | ||
143 | else | ||
144 | snd_soc_dapm_disable_pin(codec, "Speaker"); | ||
145 | } | ||
146 | pin = !!(pins & (1 << AMS_DELTA_AGC)); | ||
147 | if (pin != ams_delta_audio_agc) { | ||
148 | ams_delta_audio_agc = pin; | ||
149 | changed = 1; | ||
150 | if (pin) | ||
151 | snd_soc_dapm_enable_pin(codec, "AGCIN"); | ||
152 | else | ||
153 | snd_soc_dapm_disable_pin(codec, "AGCIN"); | ||
154 | } | ||
155 | if (changed) | ||
156 | snd_soc_dapm_sync(codec); | ||
157 | |||
158 | mutex_unlock(&codec->mutex); | ||
159 | |||
160 | return changed; | ||
161 | } | ||
162 | |||
163 | static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol, | ||
164 | struct snd_ctl_elem_value *ucontrol) | ||
165 | { | ||
166 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
167 | unsigned short pins, mode; | ||
168 | |||
169 | pins = ((snd_soc_dapm_get_pin_status(codec, "Mouthpiece") << | ||
170 | AMS_DELTA_MOUTHPIECE) | | ||
171 | (snd_soc_dapm_get_pin_status(codec, "Earpiece") << | ||
172 | AMS_DELTA_EARPIECE)); | ||
173 | if (pins) | ||
174 | pins |= (snd_soc_dapm_get_pin_status(codec, "Microphone") << | ||
175 | AMS_DELTA_MICROPHONE); | ||
176 | else | ||
177 | pins = ((snd_soc_dapm_get_pin_status(codec, "Microphone") << | ||
178 | AMS_DELTA_MICROPHONE) | | ||
179 | (snd_soc_dapm_get_pin_status(codec, "Speaker") << | ||
180 | AMS_DELTA_SPEAKER) | | ||
181 | (ams_delta_audio_agc << AMS_DELTA_AGC)); | ||
182 | |||
183 | for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++) | ||
184 | if (pins == ams_delta_audio_mode_pins[mode]) | ||
185 | break; | ||
186 | |||
187 | if (mode >= ARRAY_SIZE(ams_delta_audio_mode)) | ||
188 | return -EINVAL; | ||
189 | |||
190 | ucontrol->value.enumerated.item[0] = mode; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static const struct soc_enum ams_delta_audio_enum[] = { | ||
196 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ams_delta_audio_mode), | ||
197 | ams_delta_audio_mode), | ||
198 | }; | ||
199 | |||
200 | static const struct snd_kcontrol_new ams_delta_audio_controls[] = { | ||
201 | SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum[0], | ||
202 | ams_delta_get_audio_mode, ams_delta_set_audio_mode), | ||
203 | }; | ||
204 | |||
205 | /* Hook switch */ | ||
206 | static struct snd_soc_jack ams_delta_hook_switch; | ||
207 | static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = { | ||
208 | { | ||
209 | .gpio = 4, | ||
210 | .name = "hook_switch", | ||
211 | .report = SND_JACK_HEADSET, | ||
212 | .invert = 1, | ||
213 | .debounce_time = 150, | ||
214 | } | ||
215 | }; | ||
216 | |||
217 | /* After we are able to control the codec over the modem, | ||
218 | * the hook switch can be used for dynamic DAPM reconfiguration. */ | ||
219 | static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = { | ||
220 | /* Handset */ | ||
221 | { | ||
222 | .pin = "Mouthpiece", | ||
223 | .mask = SND_JACK_MICROPHONE, | ||
224 | }, | ||
225 | { | ||
226 | .pin = "Earpiece", | ||
227 | .mask = SND_JACK_HEADPHONE, | ||
228 | }, | ||
229 | /* Handsfree */ | ||
230 | { | ||
231 | .pin = "Microphone", | ||
232 | .mask = SND_JACK_MICROPHONE, | ||
233 | .invert = 1, | ||
234 | }, | ||
235 | { | ||
236 | .pin = "Speaker", | ||
237 | .mask = SND_JACK_HEADPHONE, | ||
238 | .invert = 1, | ||
239 | }, | ||
240 | }; | ||
241 | |||
242 | |||
243 | /* | ||
244 | * Modem line discipline, required for making above controls functional. | ||
245 | * Activated from userspace with ldattach, possibly invoked from udev rule. | ||
246 | */ | ||
247 | |||
248 | /* To actually apply any modem controlled configuration changes to the codec, | ||
249 | * we must connect codec DAI pins to the modem for a moment. Be carefull not | ||
250 | * to interfere with our digital mute function that shares the same hardware. */ | ||
251 | static struct timer_list cx81801_timer; | ||
252 | static bool cx81801_cmd_pending; | ||
253 | static bool ams_delta_muted; | ||
254 | static DEFINE_SPINLOCK(ams_delta_lock); | ||
255 | |||
256 | static void cx81801_timeout(unsigned long data) | ||
257 | { | ||
258 | int muted; | ||
259 | |||
260 | spin_lock(&ams_delta_lock); | ||
261 | cx81801_cmd_pending = 0; | ||
262 | muted = ams_delta_muted; | ||
263 | spin_unlock(&ams_delta_lock); | ||
264 | |||
265 | /* Reconnect the codec DAI back from the modem to the CPU DAI | ||
266 | * only if digital mute still off */ | ||
267 | if (!muted) | ||
268 | ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC, 0); | ||
269 | } | ||
270 | |||
271 | /* Line discipline .open() */ | ||
272 | static int cx81801_open(struct tty_struct *tty) | ||
273 | { | ||
274 | return v253_ops.open(tty); | ||
275 | } | ||
276 | |||
277 | /* Line discipline .close() */ | ||
278 | static void cx81801_close(struct tty_struct *tty) | ||
279 | { | ||
280 | struct snd_soc_codec *codec = tty->disc_data; | ||
281 | |||
282 | del_timer_sync(&cx81801_timer); | ||
283 | |||
284 | v253_ops.close(tty); | ||
285 | |||
286 | /* Prevent the hook switch from further changing the DAPM pins */ | ||
287 | INIT_LIST_HEAD(&ams_delta_hook_switch.pins); | ||
288 | |||
289 | /* Revert back to default audio input/output constellation */ | ||
290 | snd_soc_dapm_disable_pin(codec, "Mouthpiece"); | ||
291 | snd_soc_dapm_enable_pin(codec, "Earpiece"); | ||
292 | snd_soc_dapm_enable_pin(codec, "Microphone"); | ||
293 | snd_soc_dapm_disable_pin(codec, "Speaker"); | ||
294 | snd_soc_dapm_disable_pin(codec, "AGCIN"); | ||
295 | snd_soc_dapm_sync(codec); | ||
296 | } | ||
297 | |||
298 | /* Line discipline .hangup() */ | ||
299 | static int cx81801_hangup(struct tty_struct *tty) | ||
300 | { | ||
301 | cx81801_close(tty); | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | /* Line discipline .recieve_buf() */ | ||
306 | static void cx81801_receive(struct tty_struct *tty, | ||
307 | const unsigned char *cp, char *fp, int count) | ||
308 | { | ||
309 | struct snd_soc_codec *codec = tty->disc_data; | ||
310 | const unsigned char *c; | ||
311 | int apply, ret; | ||
312 | |||
313 | if (!codec->control_data) { | ||
314 | /* First modem response, complete setup procedure */ | ||
315 | |||
316 | /* Initialize timer used for config pulse generation */ | ||
317 | setup_timer(&cx81801_timer, cx81801_timeout, 0); | ||
318 | |||
319 | v253_ops.receive_buf(tty, cp, fp, count); | ||
320 | |||
321 | /* Link hook switch to DAPM pins */ | ||
322 | ret = snd_soc_jack_add_pins(&ams_delta_hook_switch, | ||
323 | ARRAY_SIZE(ams_delta_hook_switch_pins), | ||
324 | ams_delta_hook_switch_pins); | ||
325 | if (ret) | ||
326 | dev_warn(codec->socdev->card->dev, | ||
327 | "Failed to link hook switch to DAPM pins, " | ||
328 | "will continue with hook switch unlinked.\n"); | ||
329 | |||
330 | return; | ||
331 | } | ||
332 | |||
333 | v253_ops.receive_buf(tty, cp, fp, count); | ||
334 | |||
335 | for (c = &cp[count - 1]; c >= cp; c--) { | ||
336 | if (*c != '\r') | ||
337 | continue; | ||
338 | /* Complete modem response received, apply config to codec */ | ||
339 | |||
340 | spin_lock_bh(&ams_delta_lock); | ||
341 | mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150)); | ||
342 | apply = !ams_delta_muted && !cx81801_cmd_pending; | ||
343 | cx81801_cmd_pending = 1; | ||
344 | spin_unlock_bh(&ams_delta_lock); | ||
345 | |||
346 | /* Apply config pulse by connecting the codec to the modem | ||
347 | * if not already done */ | ||
348 | if (apply) | ||
349 | ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC, | ||
350 | AMS_DELTA_LATCH2_MODEM_CODEC); | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | /* Line discipline .write_wakeup() */ | ||
356 | static void cx81801_wakeup(struct tty_struct *tty) | ||
357 | { | ||
358 | v253_ops.write_wakeup(tty); | ||
359 | } | ||
360 | |||
361 | static struct tty_ldisc_ops cx81801_ops = { | ||
362 | .magic = TTY_LDISC_MAGIC, | ||
363 | .name = "cx81801", | ||
364 | .owner = THIS_MODULE, | ||
365 | .open = cx81801_open, | ||
366 | .close = cx81801_close, | ||
367 | .hangup = cx81801_hangup, | ||
368 | .receive_buf = cx81801_receive, | ||
369 | .write_wakeup = cx81801_wakeup, | ||
370 | }; | ||
371 | |||
372 | |||
373 | /* | ||
374 | * Even if not very usefull, the sound card can still work without any of the | ||
375 | * above functonality activated. You can still control its audio input/output | ||
376 | * constellation and speakerphone gain from userspace by issueing AT commands | ||
377 | * over the modem port. | ||
378 | */ | ||
379 | |||
380 | static int ams_delta_hw_params(struct snd_pcm_substream *substream, | ||
381 | struct snd_pcm_hw_params *params) | ||
382 | { | ||
383 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
384 | |||
385 | /* Set cpu DAI configuration */ | ||
386 | return snd_soc_dai_set_fmt(rtd->dai->cpu_dai, | ||
387 | SND_SOC_DAIFMT_DSP_A | | ||
388 | SND_SOC_DAIFMT_NB_NF | | ||
389 | SND_SOC_DAIFMT_CBM_CFM); | ||
390 | } | ||
391 | |||
392 | static struct snd_soc_ops ams_delta_ops = { | ||
393 | .hw_params = ams_delta_hw_params, | ||
394 | }; | ||
395 | |||
396 | |||
397 | /* Board specific codec bias level control */ | ||
398 | static int ams_delta_set_bias_level(struct snd_soc_card *card, | ||
399 | enum snd_soc_bias_level level) | ||
400 | { | ||
401 | struct snd_soc_codec *codec = card->codec; | ||
402 | |||
403 | switch (level) { | ||
404 | case SND_SOC_BIAS_ON: | ||
405 | case SND_SOC_BIAS_PREPARE: | ||
406 | case SND_SOC_BIAS_STANDBY: | ||
407 | if (codec->bias_level == SND_SOC_BIAS_OFF) | ||
408 | ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET, | ||
409 | AMS_DELTA_LATCH2_MODEM_NRESET); | ||
410 | break; | ||
411 | case SND_SOC_BIAS_OFF: | ||
412 | if (codec->bias_level != SND_SOC_BIAS_OFF) | ||
413 | ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET, | ||
414 | 0); | ||
415 | } | ||
416 | codec->bias_level = level; | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | /* Digital mute implemented using modem/CPU multiplexer. | ||
422 | * Shares hardware with codec config pulse generation */ | ||
423 | static bool ams_delta_muted = 1; | ||
424 | |||
425 | static int ams_delta_digital_mute(struct snd_soc_dai *dai, int mute) | ||
426 | { | ||
427 | int apply; | ||
428 | |||
429 | if (ams_delta_muted == mute) | ||
430 | return 0; | ||
431 | |||
432 | spin_lock_bh(&ams_delta_lock); | ||
433 | ams_delta_muted = mute; | ||
434 | apply = !cx81801_cmd_pending; | ||
435 | spin_unlock_bh(&ams_delta_lock); | ||
436 | |||
437 | if (apply) | ||
438 | ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC, | ||
439 | mute ? AMS_DELTA_LATCH2_MODEM_CODEC : 0); | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | /* Our codec DAI probably doesn't have its own .ops structure */ | ||
444 | static struct snd_soc_dai_ops ams_delta_dai_ops = { | ||
445 | .digital_mute = ams_delta_digital_mute, | ||
446 | }; | ||
447 | |||
448 | /* Will be used if the codec ever has its own digital_mute function */ | ||
449 | static int ams_delta_startup(struct snd_pcm_substream *substream) | ||
450 | { | ||
451 | return ams_delta_digital_mute(NULL, 0); | ||
452 | } | ||
453 | |||
454 | static void ams_delta_shutdown(struct snd_pcm_substream *substream) | ||
455 | { | ||
456 | ams_delta_digital_mute(NULL, 1); | ||
457 | } | ||
458 | |||
459 | |||
460 | /* | ||
461 | * Card initialization | ||
462 | */ | ||
463 | |||
464 | static int ams_delta_cx20442_init(struct snd_soc_codec *codec) | ||
465 | { | ||
466 | struct snd_soc_dai *codec_dai = codec->dai; | ||
467 | struct snd_soc_card *card = codec->socdev->card; | ||
468 | int ret; | ||
469 | /* Codec is ready, now add/activate board specific controls */ | ||
470 | |||
471 | /* Set up digital mute if not provided by the codec */ | ||
472 | if (!codec_dai->ops) { | ||
473 | codec_dai->ops = &ams_delta_dai_ops; | ||
474 | } else if (!codec_dai->ops->digital_mute) { | ||
475 | codec_dai->ops->digital_mute = ams_delta_digital_mute; | ||
476 | } else { | ||
477 | ams_delta_ops.startup = ams_delta_startup; | ||
478 | ams_delta_ops.shutdown = ams_delta_shutdown; | ||
479 | } | ||
480 | |||
481 | /* Set codec bias level */ | ||
482 | ams_delta_set_bias_level(card, SND_SOC_BIAS_STANDBY); | ||
483 | |||
484 | /* Add hook switch - can be used to control the codec from userspace | ||
485 | * even if line discipline fails */ | ||
486 | ret = snd_soc_jack_new(card, "hook_switch", | ||
487 | SND_JACK_HEADSET, &ams_delta_hook_switch); | ||
488 | if (ret) | ||
489 | dev_warn(card->dev, | ||
490 | "Failed to allocate resources for hook switch, " | ||
491 | "will continue without one.\n"); | ||
492 | else { | ||
493 | ret = snd_soc_jack_add_gpios(&ams_delta_hook_switch, | ||
494 | ARRAY_SIZE(ams_delta_hook_switch_gpios), | ||
495 | ams_delta_hook_switch_gpios); | ||
496 | if (ret) | ||
497 | dev_warn(card->dev, | ||
498 | "Failed to set up hook switch GPIO line, " | ||
499 | "will continue with hook switch inactive.\n"); | ||
500 | } | ||
501 | |||
502 | /* Register optional line discipline for over the modem control */ | ||
503 | ret = tty_register_ldisc(N_V253, &cx81801_ops); | ||
504 | if (ret) { | ||
505 | dev_warn(card->dev, | ||
506 | "Failed to register line discipline, " | ||
507 | "will continue without any controls.\n"); | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | /* Add board specific DAPM widgets and routes */ | ||
512 | ret = snd_soc_dapm_new_controls(codec, ams_delta_dapm_widgets, | ||
513 | ARRAY_SIZE(ams_delta_dapm_widgets)); | ||
514 | if (ret) { | ||
515 | dev_warn(card->dev, | ||
516 | "Failed to register DAPM controls, " | ||
517 | "will continue without any.\n"); | ||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | ret = snd_soc_dapm_add_routes(codec, ams_delta_audio_map, | ||
522 | ARRAY_SIZE(ams_delta_audio_map)); | ||
523 | if (ret) { | ||
524 | dev_warn(card->dev, | ||
525 | "Failed to set up DAPM routes, " | ||
526 | "will continue with codec default map.\n"); | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | /* Set up initial pin constellation */ | ||
531 | snd_soc_dapm_disable_pin(codec, "Mouthpiece"); | ||
532 | snd_soc_dapm_enable_pin(codec, "Earpiece"); | ||
533 | snd_soc_dapm_enable_pin(codec, "Microphone"); | ||
534 | snd_soc_dapm_disable_pin(codec, "Speaker"); | ||
535 | snd_soc_dapm_disable_pin(codec, "AGCIN"); | ||
536 | snd_soc_dapm_disable_pin(codec, "AGCOUT"); | ||
537 | snd_soc_dapm_sync(codec); | ||
538 | |||
539 | /* Add virtual switch */ | ||
540 | ret = snd_soc_add_controls(codec, ams_delta_audio_controls, | ||
541 | ARRAY_SIZE(ams_delta_audio_controls)); | ||
542 | if (ret) | ||
543 | dev_warn(card->dev, | ||
544 | "Failed to register audio mode control, " | ||
545 | "will continue without it.\n"); | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | /* DAI glue - connects codec <--> CPU */ | ||
551 | static struct snd_soc_dai_link ams_delta_dai_link = { | ||
552 | .name = "CX20442", | ||
553 | .stream_name = "CX20442", | ||
554 | .cpu_dai = &omap_mcbsp_dai[0], | ||
555 | .codec_dai = &cx20442_dai, | ||
556 | .init = ams_delta_cx20442_init, | ||
557 | .ops = &ams_delta_ops, | ||
558 | }; | ||
559 | |||
560 | /* Audio card driver */ | ||
561 | static struct snd_soc_card ams_delta_audio_card = { | ||
562 | .name = "AMS_DELTA", | ||
563 | .platform = &omap_soc_platform, | ||
564 | .dai_link = &ams_delta_dai_link, | ||
565 | .num_links = 1, | ||
566 | .set_bias_level = ams_delta_set_bias_level, | ||
567 | }; | ||
568 | |||
569 | /* Audio subsystem */ | ||
570 | static struct snd_soc_device ams_delta_snd_soc_device = { | ||
571 | .card = &ams_delta_audio_card, | ||
572 | .codec_dev = &cx20442_codec_dev, | ||
573 | }; | ||
574 | |||
575 | /* Module init/exit */ | ||
576 | static struct platform_device *ams_delta_audio_platform_device; | ||
577 | static struct platform_device *cx20442_platform_device; | ||
578 | |||
579 | static int __init ams_delta_module_init(void) | ||
580 | { | ||
581 | int ret; | ||
582 | |||
583 | if (!(machine_is_ams_delta())) | ||
584 | return -ENODEV; | ||
585 | |||
586 | ams_delta_audio_platform_device = | ||
587 | platform_device_alloc("soc-audio", -1); | ||
588 | if (!ams_delta_audio_platform_device) | ||
589 | return -ENOMEM; | ||
590 | |||
591 | platform_set_drvdata(ams_delta_audio_platform_device, | ||
592 | &ams_delta_snd_soc_device); | ||
593 | ams_delta_snd_soc_device.dev = &ams_delta_audio_platform_device->dev; | ||
594 | *(unsigned int *)ams_delta_dai_link.cpu_dai->private_data = OMAP_MCBSP1; | ||
595 | |||
596 | ret = platform_device_add(ams_delta_audio_platform_device); | ||
597 | if (ret) | ||
598 | goto err; | ||
599 | |||
600 | /* | ||
601 | * Codec platform device could be registered from elsewhere (board?), | ||
602 | * but I do it here as it makes sense only if used with the card. | ||
603 | */ | ||
604 | cx20442_platform_device = platform_device_register_simple("cx20442", | ||
605 | -1, NULL, 0); | ||
606 | return 0; | ||
607 | err: | ||
608 | platform_device_put(ams_delta_audio_platform_device); | ||
609 | return ret; | ||
610 | } | ||
611 | module_init(ams_delta_module_init); | ||
612 | |||
613 | static void __exit ams_delta_module_exit(void) | ||
614 | { | ||
615 | struct snd_soc_codec *codec; | ||
616 | struct tty_struct *tty; | ||
617 | |||
618 | if (ams_delta_audio_card.codec) { | ||
619 | codec = ams_delta_audio_card.codec; | ||
620 | |||
621 | if (codec->control_data) { | ||
622 | tty = codec->control_data; | ||
623 | |||
624 | tty_hangup(tty); | ||
625 | } | ||
626 | } | ||
627 | |||
628 | if (tty_unregister_ldisc(N_V253) != 0) | ||
629 | dev_warn(&ams_delta_audio_platform_device->dev, | ||
630 | "failed to unregister V253 line discipline\n"); | ||
631 | |||
632 | snd_soc_jack_free_gpios(&ams_delta_hook_switch, | ||
633 | ARRAY_SIZE(ams_delta_hook_switch_gpios), | ||
634 | ams_delta_hook_switch_gpios); | ||
635 | |||
636 | /* Keep modem power on */ | ||
637 | ams_delta_set_bias_level(&ams_delta_audio_card, SND_SOC_BIAS_STANDBY); | ||
638 | |||
639 | platform_device_unregister(cx20442_platform_device); | ||
640 | platform_device_unregister(ams_delta_audio_platform_device); | ||
641 | } | ||
642 | module_exit(ams_delta_module_exit); | ||
643 | |||
644 | MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); | ||
645 | MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone"); | ||
646 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index b60b1dfbc435..0a505938e42b 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c | |||
@@ -22,6 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/i2c.h> | ||
25 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
26 | #include <sound/core.h> | 27 | #include <sound/core.h> |
27 | #include <sound/pcm.h> | 28 | #include <sound/pcm.h> |
@@ -322,8 +323,6 @@ static struct snd_soc_card snd_soc_n810 = { | |||
322 | 323 | ||
323 | /* Audio private data */ | 324 | /* Audio private data */ |
324 | static struct aic3x_setup_data n810_aic33_setup = { | 325 | static struct aic3x_setup_data n810_aic33_setup = { |
325 | .i2c_bus = 2, | ||
326 | .i2c_address = 0x18, | ||
327 | .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED, | 326 | .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED, |
328 | .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, | 327 | .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, |
329 | }; | 328 | }; |
@@ -337,6 +336,13 @@ static struct snd_soc_device n810_snd_devdata = { | |||
337 | 336 | ||
338 | static struct platform_device *n810_snd_device; | 337 | static struct platform_device *n810_snd_device; |
339 | 338 | ||
339 | /* temporary i2c device creation until this can be moved into the machine | ||
340 | * support file. | ||
341 | */ | ||
342 | static struct i2c_board_info i2c_device[] = { | ||
343 | { I2C_BOARD_INFO("tlv320aic3x", 0x1b), } | ||
344 | }; | ||
345 | |||
340 | static int __init n810_soc_init(void) | 346 | static int __init n810_soc_init(void) |
341 | { | 347 | { |
342 | int err; | 348 | int err; |
@@ -345,6 +351,8 @@ static int __init n810_soc_init(void) | |||
345 | if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax())) | 351 | if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax())) |
346 | return -ENODEV; | 352 | return -ENODEV; |
347 | 353 | ||
354 | i2c_register_board_info(1, i2c_device, ARRAY_SIZE(i2c_device)); | ||
355 | |||
348 | n810_snd_device = platform_device_alloc("soc-audio", -1); | 356 | n810_snd_device = platform_device_alloc("soc-audio", -1); |
349 | if (!n810_snd_device) | 357 | if (!n810_snd_device) |
350 | return -ENOMEM; | 358 | return -ENOMEM; |
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index a5d46a7b196a..3341f49402ca 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
@@ -139,27 +139,67 @@ static const unsigned long omap34xx_mcbsp_port[][2] = { | |||
139 | static const unsigned long omap34xx_mcbsp_port[][2] = {}; | 139 | static const unsigned long omap34xx_mcbsp_port[][2] = {}; |
140 | #endif | 140 | #endif |
141 | 141 | ||
142 | static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) | ||
143 | { | ||
144 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
145 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
146 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | ||
147 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id); | ||
148 | int samples; | ||
149 | |||
150 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | ||
151 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | ||
152 | samples = snd_pcm_lib_period_bytes(substream) >> 1; | ||
153 | else | ||
154 | samples = 1; | ||
155 | |||
156 | /* Configure McBSP internal buffer usage */ | ||
157 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
158 | omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, samples - 1); | ||
159 | else | ||
160 | omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, samples - 1); | ||
161 | } | ||
162 | |||
142 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | 163 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, |
143 | struct snd_soc_dai *dai) | 164 | struct snd_soc_dai *dai) |
144 | { | 165 | { |
145 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 166 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
146 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 167 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
147 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | 168 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); |
169 | int bus_id = mcbsp_data->bus_id; | ||
148 | int err = 0; | 170 | int err = 0; |
149 | 171 | ||
150 | if (cpu_is_omap343x() && mcbsp_data->bus_id == 1) { | 172 | if (!cpu_dai->active) |
173 | err = omap_mcbsp_request(bus_id); | ||
174 | |||
175 | if (cpu_is_omap343x()) { | ||
176 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id); | ||
177 | int max_period; | ||
178 | |||
151 | /* | 179 | /* |
152 | * McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer. | 180 | * McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer. |
153 | * Set constraint for minimum buffer size to the same than FIFO | 181 | * Set constraint for minimum buffer size to the same than FIFO |
154 | * size in order to avoid underruns in playback startup because | 182 | * size in order to avoid underruns in playback startup because |
155 | * HW is keeping the DMA request active until FIFO is filled. | 183 | * HW is keeping the DMA request active until FIFO is filled. |
156 | */ | 184 | */ |
157 | snd_pcm_hw_constraint_minmax(substream->runtime, | 185 | if (bus_id == 1) |
158 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4096, UINT_MAX); | 186 | snd_pcm_hw_constraint_minmax(substream->runtime, |
159 | } | 187 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, |
188 | 4096, UINT_MAX); | ||
160 | 189 | ||
161 | if (!cpu_dai->active) | 190 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
162 | err = omap_mcbsp_request(mcbsp_data->bus_id); | 191 | max_period = omap_mcbsp_get_max_tx_threshold(bus_id); |
192 | else | ||
193 | max_period = omap_mcbsp_get_max_rx_threshold(bus_id); | ||
194 | |||
195 | max_period++; | ||
196 | max_period <<= 1; | ||
197 | |||
198 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | ||
199 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
200 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, | ||
201 | 32, max_period); | ||
202 | } | ||
163 | 203 | ||
164 | return err; | 204 | return err; |
165 | } | 205 | } |
@@ -183,21 +223,21 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
183 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 223 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
184 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 224 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
185 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | 225 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); |
186 | int err = 0; | 226 | int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); |
187 | 227 | ||
188 | switch (cmd) { | 228 | switch (cmd) { |
189 | case SNDRV_PCM_TRIGGER_START: | 229 | case SNDRV_PCM_TRIGGER_START: |
190 | case SNDRV_PCM_TRIGGER_RESUME: | 230 | case SNDRV_PCM_TRIGGER_RESUME: |
191 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 231 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
192 | if (!mcbsp_data->active++) | 232 | mcbsp_data->active++; |
193 | omap_mcbsp_start(mcbsp_data->bus_id); | 233 | omap_mcbsp_start(mcbsp_data->bus_id, play, !play); |
194 | break; | 234 | break; |
195 | 235 | ||
196 | case SNDRV_PCM_TRIGGER_STOP: | 236 | case SNDRV_PCM_TRIGGER_STOP: |
197 | case SNDRV_PCM_TRIGGER_SUSPEND: | 237 | case SNDRV_PCM_TRIGGER_SUSPEND: |
198 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 238 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
199 | if (!--mcbsp_data->active) | 239 | omap_mcbsp_stop(mcbsp_data->bus_id, play, !play); |
200 | omap_mcbsp_stop(mcbsp_data->bus_id); | 240 | mcbsp_data->active--; |
201 | break; | 241 | break; |
202 | default: | 242 | default: |
203 | err = -EINVAL; | 243 | err = -EINVAL; |
@@ -215,7 +255,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
215 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | 255 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); |
216 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; | 256 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; |
217 | int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; | 257 | int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; |
218 | int wlen, channels, wpf; | 258 | int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; |
219 | unsigned long port; | 259 | unsigned long port; |
220 | unsigned int format; | 260 | unsigned int format; |
221 | 261 | ||
@@ -231,6 +271,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
231 | } else if (cpu_is_omap343x()) { | 271 | } else if (cpu_is_omap343x()) { |
232 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; | 272 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; |
233 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; | 273 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; |
274 | omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold = | ||
275 | omap_mcbsp_set_threshold; | ||
276 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | ||
277 | if (omap_mcbsp_get_dma_op_mode(bus_id) == | ||
278 | MCBSP_DMA_MODE_THRESHOLD) | ||
279 | sync_mode = OMAP_DMA_SYNC_FRAME; | ||
234 | } else { | 280 | } else { |
235 | return -ENODEV; | 281 | return -ENODEV; |
236 | } | 282 | } |
@@ -238,6 +284,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
238 | substream->stream ? "Audio Capture" : "Audio Playback"; | 284 | substream->stream ? "Audio Capture" : "Audio Playback"; |
239 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; | 285 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; |
240 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; | 286 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; |
287 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; | ||
241 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; | 288 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; |
242 | 289 | ||
243 | if (mcbsp_data->configured) { | 290 | if (mcbsp_data->configured) { |
@@ -321,11 +368,14 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, | |||
321 | /* Generic McBSP register settings */ | 368 | /* Generic McBSP register settings */ |
322 | regs->spcr2 |= XINTM(3) | FREE; | 369 | regs->spcr2 |= XINTM(3) | FREE; |
323 | regs->spcr1 |= RINTM(3); | 370 | regs->spcr1 |= RINTM(3); |
324 | regs->rcr2 |= RFIG; | 371 | /* RFIG and XFIG are not defined in 34xx */ |
325 | regs->xcr2 |= XFIG; | 372 | if (!cpu_is_omap34xx()) { |
373 | regs->rcr2 |= RFIG; | ||
374 | regs->xcr2 |= XFIG; | ||
375 | } | ||
326 | if (cpu_is_omap2430() || cpu_is_omap34xx()) { | 376 | if (cpu_is_omap2430() || cpu_is_omap34xx()) { |
327 | regs->xccr = DXENDLY(1) | XDMAEN; | 377 | regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE; |
328 | regs->rccr = RFULL_CYCLE | RDMAEN; | 378 | regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE; |
329 | } | 379 | } |
330 | 380 | ||
331 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 381 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
@@ -462,6 +512,40 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, | |||
462 | return 0; | 512 | return 0; |
463 | } | 513 | } |
464 | 514 | ||
515 | static int omap_mcbsp_dai_set_rcvr_src(struct omap_mcbsp_data *mcbsp_data, | ||
516 | int clk_id) | ||
517 | { | ||
518 | int sel_bit, set = 0; | ||
519 | u16 reg = OMAP2_CONTROL_DEVCONF0; | ||
520 | |||
521 | if (cpu_class_is_omap1()) | ||
522 | return -EINVAL; /* TODO: Can this be implemented for OMAP1? */ | ||
523 | if (mcbsp_data->bus_id != 0) | ||
524 | return -EINVAL; | ||
525 | |||
526 | switch (clk_id) { | ||
527 | case OMAP_MCBSP_CLKR_SRC_CLKX: | ||
528 | set = 1; | ||
529 | case OMAP_MCBSP_CLKR_SRC_CLKR: | ||
530 | sel_bit = 3; | ||
531 | break; | ||
532 | case OMAP_MCBSP_FSR_SRC_FSX: | ||
533 | set = 1; | ||
534 | case OMAP_MCBSP_FSR_SRC_FSR: | ||
535 | sel_bit = 4; | ||
536 | break; | ||
537 | default: | ||
538 | return -EINVAL; | ||
539 | } | ||
540 | |||
541 | if (set) | ||
542 | omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg); | ||
543 | else | ||
544 | omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg); | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
465 | static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | 549 | static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, |
466 | int clk_id, unsigned int freq, | 550 | int clk_id, unsigned int freq, |
467 | int dir) | 551 | int dir) |
@@ -484,6 +568,13 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | |||
484 | case OMAP_MCBSP_SYSCLK_CLKR_EXT: | 568 | case OMAP_MCBSP_SYSCLK_CLKR_EXT: |
485 | regs->pcr0 |= SCLKME; | 569 | regs->pcr0 |= SCLKME; |
486 | break; | 570 | break; |
571 | |||
572 | case OMAP_MCBSP_CLKR_SRC_CLKR: | ||
573 | case OMAP_MCBSP_CLKR_SRC_CLKX: | ||
574 | case OMAP_MCBSP_FSR_SRC_FSR: | ||
575 | case OMAP_MCBSP_FSR_SRC_FSX: | ||
576 | err = omap_mcbsp_dai_set_rcvr_src(mcbsp_data, clk_id); | ||
577 | break; | ||
487 | default: | 578 | default: |
488 | err = -ENODEV; | 579 | err = -ENODEV; |
489 | } | 580 | } |
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index c8147aace813..647d2f981ab0 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h | |||
@@ -32,6 +32,10 @@ enum omap_mcbsp_clksrg_clk { | |||
32 | OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */ | 32 | OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */ |
33 | OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */ | 33 | OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */ |
34 | OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */ | 34 | OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */ |
35 | OMAP_MCBSP_CLKR_SRC_CLKR, /* CLKR from CLKR pin */ | ||
36 | OMAP_MCBSP_CLKR_SRC_CLKX, /* CLKR from CLKX pin */ | ||
37 | OMAP_MCBSP_FSR_SRC_FSR, /* FSR from FSR pin */ | ||
38 | OMAP_MCBSP_FSR_SRC_FSX, /* FSR from FSX pin */ | ||
35 | }; | 39 | }; |
36 | 40 | ||
37 | /* McBSP dividers */ | 41 | /* McBSP dividers */ |
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 84a1950880eb..5735945788bf 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c | |||
@@ -59,16 +59,31 @@ static void omap_pcm_dma_irq(int ch, u16 stat, void *data) | |||
59 | struct omap_runtime_data *prtd = runtime->private_data; | 59 | struct omap_runtime_data *prtd = runtime->private_data; |
60 | unsigned long flags; | 60 | unsigned long flags; |
61 | 61 | ||
62 | if (cpu_is_omap1510()) { | 62 | if ((cpu_is_omap1510()) && |
63 | (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) { | ||
63 | /* | 64 | /* |
64 | * OMAP1510 doesn't support DMA chaining so have to restart | 65 | * OMAP1510 doesn't fully support DMA progress counter |
65 | * the transfer after all periods are transferred | 66 | * and there is no software emulation implemented yet, |
67 | * so have to maintain our own playback progress counter | ||
68 | * that can be used by omap_pcm_pointer() instead. | ||
66 | */ | 69 | */ |
67 | spin_lock_irqsave(&prtd->lock, flags); | 70 | spin_lock_irqsave(&prtd->lock, flags); |
71 | if ((stat == OMAP_DMA_LAST_IRQ) && | ||
72 | (prtd->period_index == runtime->periods - 1)) { | ||
73 | /* we are in sync, do nothing */ | ||
74 | spin_unlock_irqrestore(&prtd->lock, flags); | ||
75 | return; | ||
76 | } | ||
68 | if (prtd->period_index >= 0) { | 77 | if (prtd->period_index >= 0) { |
69 | if (++prtd->period_index == runtime->periods) { | 78 | if (stat & OMAP_DMA_BLOCK_IRQ) { |
79 | /* end of buffer reached, loop back */ | ||
80 | prtd->period_index = 0; | ||
81 | } else if (stat & OMAP_DMA_LAST_IRQ) { | ||
82 | /* update the counter for the last period */ | ||
83 | prtd->period_index = runtime->periods - 1; | ||
84 | } else if (++prtd->period_index >= runtime->periods) { | ||
85 | /* end of buffer missed? loop back */ | ||
70 | prtd->period_index = 0; | 86 | prtd->period_index = 0; |
71 | omap_start_dma(prtd->dma_ch); | ||
72 | } | 87 | } |
73 | } | 88 | } |
74 | spin_unlock_irqrestore(&prtd->lock, flags); | 89 | spin_unlock_irqrestore(&prtd->lock, flags); |
@@ -100,7 +115,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, | |||
100 | prtd->dma_data = dma_data; | 115 | prtd->dma_data = dma_data; |
101 | err = omap_request_dma(dma_data->dma_req, dma_data->name, | 116 | err = omap_request_dma(dma_data->dma_req, dma_data->name, |
102 | omap_pcm_dma_irq, substream, &prtd->dma_ch); | 117 | omap_pcm_dma_irq, substream, &prtd->dma_ch); |
103 | if (!err && !cpu_is_omap1510()) { | 118 | if (!err) { |
104 | /* | 119 | /* |
105 | * Link channel with itself so DMA doesn't need any | 120 | * Link channel with itself so DMA doesn't need any |
106 | * reprogramming while looping the buffer | 121 | * reprogramming while looping the buffer |
@@ -119,8 +134,7 @@ static int omap_pcm_hw_free(struct snd_pcm_substream *substream) | |||
119 | if (prtd->dma_data == NULL) | 134 | if (prtd->dma_data == NULL) |
120 | return 0; | 135 | return 0; |
121 | 136 | ||
122 | if (!cpu_is_omap1510()) | 137 | omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch); |
123 | omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch); | ||
124 | omap_free_dma(prtd->dma_ch); | 138 | omap_free_dma(prtd->dma_ch); |
125 | prtd->dma_data = NULL; | 139 | prtd->dma_data = NULL; |
126 | 140 | ||
@@ -148,7 +162,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
148 | */ | 162 | */ |
149 | dma_params.data_type = OMAP_DMA_DATA_TYPE_S16; | 163 | dma_params.data_type = OMAP_DMA_DATA_TYPE_S16; |
150 | dma_params.trigger = dma_data->dma_req; | 164 | dma_params.trigger = dma_data->dma_req; |
151 | dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT; | 165 | dma_params.sync_mode = dma_data->sync_mode; |
152 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 166 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
153 | dma_params.src_amode = OMAP_DMA_AMODE_POST_INC; | 167 | dma_params.src_amode = OMAP_DMA_AMODE_POST_INC; |
154 | dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT; | 168 | dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT; |
@@ -174,7 +188,15 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
174 | dma_params.frame_count = runtime->periods; | 188 | dma_params.frame_count = runtime->periods; |
175 | omap_set_dma_params(prtd->dma_ch, &dma_params); | 189 | omap_set_dma_params(prtd->dma_ch, &dma_params); |
176 | 190 | ||
177 | omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ); | 191 | if ((cpu_is_omap1510()) && |
192 | (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) | ||
193 | omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ | | ||
194 | OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ); | ||
195 | else | ||
196 | omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ); | ||
197 | |||
198 | omap_set_dma_src_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16); | ||
199 | omap_set_dma_dest_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16); | ||
178 | 200 | ||
179 | return 0; | 201 | return 0; |
180 | } | 202 | } |
@@ -183,6 +205,7 @@ static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
183 | { | 205 | { |
184 | struct snd_pcm_runtime *runtime = substream->runtime; | 206 | struct snd_pcm_runtime *runtime = substream->runtime; |
185 | struct omap_runtime_data *prtd = runtime->private_data; | 207 | struct omap_runtime_data *prtd = runtime->private_data; |
208 | struct omap_pcm_dma_data *dma_data = prtd->dma_data; | ||
186 | unsigned long flags; | 209 | unsigned long flags; |
187 | int ret = 0; | 210 | int ret = 0; |
188 | 211 | ||
@@ -192,6 +215,10 @@ static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
192 | case SNDRV_PCM_TRIGGER_RESUME: | 215 | case SNDRV_PCM_TRIGGER_RESUME: |
193 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 216 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
194 | prtd->period_index = 0; | 217 | prtd->period_index = 0; |
218 | /* Configure McBSP internal buffer usage */ | ||
219 | if (dma_data->set_threshold) | ||
220 | dma_data->set_threshold(substream); | ||
221 | |||
195 | omap_start_dma(prtd->dma_ch); | 222 | omap_start_dma(prtd->dma_ch); |
196 | break; | 223 | break; |
197 | 224 | ||
@@ -288,7 +315,7 @@ static struct snd_pcm_ops omap_pcm_ops = { | |||
288 | .mmap = omap_pcm_mmap, | 315 | .mmap = omap_pcm_mmap, |
289 | }; | 316 | }; |
290 | 317 | ||
291 | static u64 omap_pcm_dmamask = DMA_BIT_MASK(32); | 318 | static u64 omap_pcm_dmamask = DMA_BIT_MASK(64); |
292 | 319 | ||
293 | static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, | 320 | static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, |
294 | int stream) | 321 | int stream) |
@@ -330,7 +357,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) | |||
330 | } | 357 | } |
331 | } | 358 | } |
332 | 359 | ||
333 | int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | 360 | static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, |
334 | struct snd_pcm *pcm) | 361 | struct snd_pcm *pcm) |
335 | { | 362 | { |
336 | int ret = 0; | 363 | int ret = 0; |
@@ -338,7 +365,7 @@ int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
338 | if (!card->dev->dma_mask) | 365 | if (!card->dev->dma_mask) |
339 | card->dev->dma_mask = &omap_pcm_dmamask; | 366 | card->dev->dma_mask = &omap_pcm_dmamask; |
340 | if (!card->dev->coherent_dma_mask) | 367 | if (!card->dev->coherent_dma_mask) |
341 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | 368 | card->dev->coherent_dma_mask = DMA_BIT_MASK(64); |
342 | 369 | ||
343 | if (dai->playback.channels_min) { | 370 | if (dai->playback.channels_min) { |
344 | ret = omap_pcm_preallocate_dma_buffer(pcm, | 371 | ret = omap_pcm_preallocate_dma_buffer(pcm, |
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h index 8d9d26916b05..38a821dd4118 100644 --- a/sound/soc/omap/omap-pcm.h +++ b/sound/soc/omap/omap-pcm.h | |||
@@ -29,6 +29,8 @@ struct omap_pcm_dma_data { | |||
29 | char *name; /* stream identifier */ | 29 | char *name; /* stream identifier */ |
30 | int dma_req; /* DMA request line */ | 30 | int dma_req; /* DMA request line */ |
31 | unsigned long port_addr; /* transmit/receive register */ | 31 | unsigned long port_addr; /* transmit/receive register */ |
32 | int sync_mode; /* DMA sync mode */ | ||
33 | void (*set_threshold)(struct snd_pcm_substream *substream); | ||
32 | }; | 34 | }; |
33 | 35 | ||
34 | extern struct snd_soc_platform omap_soc_platform; | 36 | extern struct snd_soc_platform omap_soc_platform; |
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index b719e5db4f57..4a3f62d1f295 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include <linux/clk.h> | 25 | #include <linux/clk.h> |
26 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
27 | #include <linux/i2c/twl4030.h> | ||
27 | #include <sound/core.h> | 28 | #include <sound/core.h> |
28 | #include <sound/pcm.h> | 29 | #include <sound/pcm.h> |
29 | #include <sound/soc.h> | 30 | #include <sound/soc.h> |
@@ -39,6 +40,11 @@ | |||
39 | #include "omap-pcm.h" | 40 | #include "omap-pcm.h" |
40 | #include "../codecs/twl4030.h" | 41 | #include "../codecs/twl4030.h" |
41 | 42 | ||
43 | /* TWL4030 PMBR1 Register */ | ||
44 | #define TWL4030_INTBR_PMBR1 0x0D | ||
45 | /* TWL4030 PMBR1 Register GPIO6 mux bit */ | ||
46 | #define TWL4030_GPIO6_PWM0_MUTE(value) (value << 2) | ||
47 | |||
42 | static struct snd_soc_card snd_soc_sdp3430; | 48 | static struct snd_soc_card snd_soc_sdp3430; |
43 | 49 | ||
44 | static int sdp3430_hw_params(struct snd_pcm_substream *substream, | 50 | static int sdp3430_hw_params(struct snd_pcm_substream *substream, |
@@ -96,7 +102,7 @@ static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream, | |||
96 | ret = snd_soc_dai_set_fmt(codec_dai, | 102 | ret = snd_soc_dai_set_fmt(codec_dai, |
97 | SND_SOC_DAIFMT_DSP_A | | 103 | SND_SOC_DAIFMT_DSP_A | |
98 | SND_SOC_DAIFMT_IB_NF | | 104 | SND_SOC_DAIFMT_IB_NF | |
99 | SND_SOC_DAIFMT_CBS_CFM); | 105 | SND_SOC_DAIFMT_CBM_CFM); |
100 | if (ret) { | 106 | if (ret) { |
101 | printk(KERN_ERR "can't set codec DAI configuration\n"); | 107 | printk(KERN_ERR "can't set codec DAI configuration\n"); |
102 | return ret; | 108 | return ret; |
@@ -280,6 +286,7 @@ static struct snd_soc_card snd_soc_sdp3430 = { | |||
280 | static struct twl4030_setup_data twl4030_setup = { | 286 | static struct twl4030_setup_data twl4030_setup = { |
281 | .ramp_delay_value = 3, | 287 | .ramp_delay_value = 3, |
282 | .sysclk = 26000, | 288 | .sysclk = 26000, |
289 | .hs_extmute = 1, | ||
283 | }; | 290 | }; |
284 | 291 | ||
285 | /* Audio subsystem */ | 292 | /* Audio subsystem */ |
@@ -294,6 +301,7 @@ static struct platform_device *sdp3430_snd_device; | |||
294 | static int __init sdp3430_soc_init(void) | 301 | static int __init sdp3430_soc_init(void) |
295 | { | 302 | { |
296 | int ret; | 303 | int ret; |
304 | u8 pin_mux; | ||
297 | 305 | ||
298 | if (!machine_is_omap_3430sdp()) { | 306 | if (!machine_is_omap_3430sdp()) { |
299 | pr_debug("Not SDP3430!\n"); | 307 | pr_debug("Not SDP3430!\n"); |
@@ -312,6 +320,14 @@ static int __init sdp3430_soc_init(void) | |||
312 | *(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */ | 320 | *(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */ |
313 | *(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */ | 321 | *(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */ |
314 | 322 | ||
323 | /* Set TWL4030 GPIO6 as EXTMUTE signal */ | ||
324 | twl4030_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux, | ||
325 | TWL4030_INTBR_PMBR1); | ||
326 | pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03); | ||
327 | pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02); | ||
328 | twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux, | ||
329 | TWL4030_INTBR_PMBR1); | ||
330 | |||
315 | ret = platform_device_add(sdp3430_snd_device); | 331 | ret = platform_device_add(sdp3430_snd_device); |
316 | if (ret) | 332 | if (ret) |
317 | goto err1; | 333 | goto err1; |
diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c new file mode 100644 index 000000000000..f90b45f56220 --- /dev/null +++ b/sound/soc/omap/zoom2.c | |||
@@ -0,0 +1,314 @@ | |||
1 | /* | ||
2 | * zoom2.c -- SoC audio for Zoom2 | ||
3 | * | ||
4 | * Author: Misael Lopez Cruz <x0052729@ti.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/clk.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/soc.h> | ||
27 | #include <sound/soc-dapm.h> | ||
28 | |||
29 | #include <asm/mach-types.h> | ||
30 | #include <mach/hardware.h> | ||
31 | #include <mach/gpio.h> | ||
32 | #include <mach/mcbsp.h> | ||
33 | |||
34 | #include "omap-mcbsp.h" | ||
35 | #include "omap-pcm.h" | ||
36 | #include "../codecs/twl4030.h" | ||
37 | |||
38 | #define ZOOM2_HEADSET_MUX_GPIO (OMAP_MAX_GPIO_LINES + 15) | ||
39 | #define ZOOM2_HEADSET_EXTMUTE_GPIO 153 | ||
40 | |||
41 | static int zoom2_hw_params(struct snd_pcm_substream *substream, | ||
42 | struct snd_pcm_hw_params *params) | ||
43 | { | ||
44 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
45 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
46 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
47 | int ret; | ||
48 | |||
49 | /* Set codec DAI configuration */ | ||
50 | ret = snd_soc_dai_set_fmt(codec_dai, | ||
51 | SND_SOC_DAIFMT_I2S | | ||
52 | SND_SOC_DAIFMT_NB_NF | | ||
53 | SND_SOC_DAIFMT_CBM_CFM); | ||
54 | if (ret < 0) { | ||
55 | printk(KERN_ERR "can't set codec DAI configuration\n"); | ||
56 | return ret; | ||
57 | } | ||
58 | |||
59 | /* Set cpu DAI configuration */ | ||
60 | ret = snd_soc_dai_set_fmt(cpu_dai, | ||
61 | SND_SOC_DAIFMT_I2S | | ||
62 | SND_SOC_DAIFMT_NB_NF | | ||
63 | SND_SOC_DAIFMT_CBM_CFM); | ||
64 | if (ret < 0) { | ||
65 | printk(KERN_ERR "can't set cpu DAI configuration\n"); | ||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | /* Set the codec system clock for DAC and ADC */ | ||
70 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, | ||
71 | SND_SOC_CLOCK_IN); | ||
72 | if (ret < 0) { | ||
73 | printk(KERN_ERR "can't set codec system clock\n"); | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static struct snd_soc_ops zoom2_ops = { | ||
81 | .hw_params = zoom2_hw_params, | ||
82 | }; | ||
83 | |||
84 | static int zoom2_hw_voice_params(struct snd_pcm_substream *substream, | ||
85 | struct snd_pcm_hw_params *params) | ||
86 | { | ||
87 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
88 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
89 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
90 | int ret; | ||
91 | |||
92 | /* Set codec DAI configuration */ | ||
93 | ret = snd_soc_dai_set_fmt(codec_dai, | ||
94 | SND_SOC_DAIFMT_DSP_A | | ||
95 | SND_SOC_DAIFMT_IB_NF | | ||
96 | SND_SOC_DAIFMT_CBM_CFM); | ||
97 | if (ret) { | ||
98 | printk(KERN_ERR "can't set codec DAI configuration\n"); | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | /* Set cpu DAI configuration */ | ||
103 | ret = snd_soc_dai_set_fmt(cpu_dai, | ||
104 | SND_SOC_DAIFMT_DSP_A | | ||
105 | SND_SOC_DAIFMT_IB_NF | | ||
106 | SND_SOC_DAIFMT_CBM_CFM); | ||
107 | if (ret < 0) { | ||
108 | printk(KERN_ERR "can't set cpu DAI configuration\n"); | ||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | /* Set the codec system clock for DAC and ADC */ | ||
113 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, | ||
114 | SND_SOC_CLOCK_IN); | ||
115 | if (ret < 0) { | ||
116 | printk(KERN_ERR "can't set codec system clock\n"); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static struct snd_soc_ops zoom2_voice_ops = { | ||
124 | .hw_params = zoom2_hw_voice_params, | ||
125 | }; | ||
126 | |||
127 | /* Zoom2 machine DAPM */ | ||
128 | static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = { | ||
129 | SND_SOC_DAPM_MIC("Ext Mic", NULL), | ||
130 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | ||
131 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||
132 | SND_SOC_DAPM_HP("Headset Stereophone", NULL), | ||
133 | SND_SOC_DAPM_LINE("Aux In", NULL), | ||
134 | }; | ||
135 | |||
136 | static const struct snd_soc_dapm_route audio_map[] = { | ||
137 | /* External Mics: MAINMIC, SUBMIC with bias*/ | ||
138 | {"MAINMIC", NULL, "Mic Bias 1"}, | ||
139 | {"SUBMIC", NULL, "Mic Bias 2"}, | ||
140 | {"Mic Bias 1", NULL, "Ext Mic"}, | ||
141 | {"Mic Bias 2", NULL, "Ext Mic"}, | ||
142 | |||
143 | /* External Speakers: HFL, HFR */ | ||
144 | {"Ext Spk", NULL, "HFL"}, | ||
145 | {"Ext Spk", NULL, "HFR"}, | ||
146 | |||
147 | /* Headset Stereophone: HSOL, HSOR */ | ||
148 | {"Headset Stereophone", NULL, "HSOL"}, | ||
149 | {"Headset Stereophone", NULL, "HSOR"}, | ||
150 | |||
151 | /* Headset Mic: HSMIC with bias */ | ||
152 | {"HSMIC", NULL, "Headset Mic Bias"}, | ||
153 | {"Headset Mic Bias", NULL, "Headset Mic"}, | ||
154 | |||
155 | /* Aux In: AUXL, AUXR */ | ||
156 | {"Aux In", NULL, "AUXL"}, | ||
157 | {"Aux In", NULL, "AUXR"}, | ||
158 | }; | ||
159 | |||
160 | static int zoom2_twl4030_init(struct snd_soc_codec *codec) | ||
161 | { | ||
162 | int ret; | ||
163 | |||
164 | /* Add Zoom2 specific widgets */ | ||
165 | ret = snd_soc_dapm_new_controls(codec, zoom2_twl4030_dapm_widgets, | ||
166 | ARRAY_SIZE(zoom2_twl4030_dapm_widgets)); | ||
167 | if (ret) | ||
168 | return ret; | ||
169 | |||
170 | /* Set up Zoom2 specific audio path audio_map */ | ||
171 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
172 | |||
173 | /* Zoom2 connected pins */ | ||
174 | snd_soc_dapm_enable_pin(codec, "Ext Mic"); | ||
175 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | ||
176 | snd_soc_dapm_enable_pin(codec, "Headset Mic"); | ||
177 | snd_soc_dapm_enable_pin(codec, "Headset Stereophone"); | ||
178 | snd_soc_dapm_enable_pin(codec, "Aux In"); | ||
179 | |||
180 | /* TWL4030 not connected pins */ | ||
181 | snd_soc_dapm_nc_pin(codec, "CARKITMIC"); | ||
182 | snd_soc_dapm_nc_pin(codec, "DIGIMIC0"); | ||
183 | snd_soc_dapm_nc_pin(codec, "DIGIMIC1"); | ||
184 | |||
185 | snd_soc_dapm_nc_pin(codec, "OUTL"); | ||
186 | snd_soc_dapm_nc_pin(codec, "OUTR"); | ||
187 | snd_soc_dapm_nc_pin(codec, "EARPIECE"); | ||
188 | snd_soc_dapm_nc_pin(codec, "PREDRIVEL"); | ||
189 | snd_soc_dapm_nc_pin(codec, "PREDRIVER"); | ||
190 | snd_soc_dapm_nc_pin(codec, "CARKITL"); | ||
191 | snd_soc_dapm_nc_pin(codec, "CARKITR"); | ||
192 | |||
193 | ret = snd_soc_dapm_sync(codec); | ||
194 | |||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | static int zoom2_twl4030_voice_init(struct snd_soc_codec *codec) | ||
199 | { | ||
200 | unsigned short reg; | ||
201 | |||
202 | /* Enable voice interface */ | ||
203 | reg = codec->read(codec, TWL4030_REG_VOICE_IF); | ||
204 | reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN; | ||
205 | codec->write(codec, TWL4030_REG_VOICE_IF, reg); | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
211 | static struct snd_soc_dai_link zoom2_dai[] = { | ||
212 | { | ||
213 | .name = "TWL4030 I2S", | ||
214 | .stream_name = "TWL4030 Audio", | ||
215 | .cpu_dai = &omap_mcbsp_dai[0], | ||
216 | .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], | ||
217 | .init = zoom2_twl4030_init, | ||
218 | .ops = &zoom2_ops, | ||
219 | }, | ||
220 | { | ||
221 | .name = "TWL4030 PCM", | ||
222 | .stream_name = "TWL4030 Voice", | ||
223 | .cpu_dai = &omap_mcbsp_dai[1], | ||
224 | .codec_dai = &twl4030_dai[TWL4030_DAI_VOICE], | ||
225 | .init = zoom2_twl4030_voice_init, | ||
226 | .ops = &zoom2_voice_ops, | ||
227 | }, | ||
228 | }; | ||
229 | |||
230 | /* Audio machine driver */ | ||
231 | static struct snd_soc_card snd_soc_zoom2 = { | ||
232 | .name = "Zoom2", | ||
233 | .platform = &omap_soc_platform, | ||
234 | .dai_link = zoom2_dai, | ||
235 | .num_links = ARRAY_SIZE(zoom2_dai), | ||
236 | }; | ||
237 | |||
238 | /* EXTMUTE callback function */ | ||
239 | void zoom2_set_hs_extmute(int mute) | ||
240 | { | ||
241 | gpio_set_value(ZOOM2_HEADSET_EXTMUTE_GPIO, mute); | ||
242 | } | ||
243 | |||
244 | /* twl4030 setup */ | ||
245 | static struct twl4030_setup_data twl4030_setup = { | ||
246 | .ramp_delay_value = 3, /* 161 ms */ | ||
247 | .sysclk = 26000, | ||
248 | .hs_extmute = 1, | ||
249 | .set_hs_extmute = zoom2_set_hs_extmute, | ||
250 | }; | ||
251 | |||
252 | /* Audio subsystem */ | ||
253 | static struct snd_soc_device zoom2_snd_devdata = { | ||
254 | .card = &snd_soc_zoom2, | ||
255 | .codec_dev = &soc_codec_dev_twl4030, | ||
256 | .codec_data = &twl4030_setup, | ||
257 | }; | ||
258 | |||
259 | static struct platform_device *zoom2_snd_device; | ||
260 | |||
261 | static int __init zoom2_soc_init(void) | ||
262 | { | ||
263 | int ret; | ||
264 | |||
265 | if (!machine_is_omap_zoom2()) { | ||
266 | pr_debug("Not Zoom2!\n"); | ||
267 | return -ENODEV; | ||
268 | } | ||
269 | printk(KERN_INFO "Zoom2 SoC init\n"); | ||
270 | |||
271 | zoom2_snd_device = platform_device_alloc("soc-audio", -1); | ||
272 | if (!zoom2_snd_device) { | ||
273 | printk(KERN_ERR "Platform device allocation failed\n"); | ||
274 | return -ENOMEM; | ||
275 | } | ||
276 | |||
277 | platform_set_drvdata(zoom2_snd_device, &zoom2_snd_devdata); | ||
278 | zoom2_snd_devdata.dev = &zoom2_snd_device->dev; | ||
279 | *(unsigned int *)zoom2_dai[0].cpu_dai->private_data = 1; /* McBSP2 */ | ||
280 | *(unsigned int *)zoom2_dai[1].cpu_dai->private_data = 2; /* McBSP3 */ | ||
281 | |||
282 | ret = platform_device_add(zoom2_snd_device); | ||
283 | if (ret) | ||
284 | goto err1; | ||
285 | |||
286 | BUG_ON(gpio_request(ZOOM2_HEADSET_MUX_GPIO, "hs_mux") < 0); | ||
287 | gpio_direction_output(ZOOM2_HEADSET_MUX_GPIO, 0); | ||
288 | |||
289 | BUG_ON(gpio_request(ZOOM2_HEADSET_EXTMUTE_GPIO, "ext_mute") < 0); | ||
290 | gpio_direction_output(ZOOM2_HEADSET_EXTMUTE_GPIO, 0); | ||
291 | |||
292 | return 0; | ||
293 | |||
294 | err1: | ||
295 | printk(KERN_ERR "Unable to add platform device\n"); | ||
296 | platform_device_put(zoom2_snd_device); | ||
297 | |||
298 | return ret; | ||
299 | } | ||
300 | module_init(zoom2_soc_init); | ||
301 | |||
302 | static void __exit zoom2_soc_exit(void) | ||
303 | { | ||
304 | gpio_free(ZOOM2_HEADSET_MUX_GPIO); | ||
305 | gpio_free(ZOOM2_HEADSET_EXTMUTE_GPIO); | ||
306 | |||
307 | platform_device_unregister(zoom2_snd_device); | ||
308 | } | ||
309 | module_exit(zoom2_soc_exit); | ||
310 | |||
311 | MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); | ||
312 | MODULE_DESCRIPTION("ALSA SoC Zoom2"); | ||
313 | MODULE_LICENSE("GPL"); | ||
314 | |||