2 * sound/arm/omap-alsa-mixer.c
4 * Alsa Driver Mixer for generic codecs for omap boards
6 * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
7 * Written by David Cohen, Daniel Petrini
8 * {david.cohen, daniel.petrini}@indt.org.br
10 * Based on es1688_lib.c,
11 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
21 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * You should have received a copy of the GNU General Public License along
30 * with this program; if not, write to the Free Software Foundation, Inc.,
31 * 675 Mass Ave, Cambridge, MA 02139, USA.
35 * 2005-08-02 INdT Kernel Team - Alsa mixer driver for omap osk. Creation of new
36 * file omap-alsa-mixer.c. Initial version
37 * with aic23 codec for osk5912
40 #include <linux/config.h>
41 #include <sound/driver.h>
42 #include <linux/module.h>
43 #include <linux/device.h>
44 #include <linux/init.h>
45 #include <linux/errno.h>
46 #include <linux/ioctl.h>
47 #include <linux/delay.h>
48 #include <linux/slab.h>
50 #include <asm/hardware.h>
51 #include <asm/mach-types.h>
52 #include <asm/arch/dma.h>
53 #include <asm/arch/aic23.h>
55 #include "omap-aic23.h"
56 #include <sound/initval.h>
57 #include <sound/control.h>
59 MODULE_AUTHOR("David Cohen, Daniel Petrini - INdT");
60 MODULE_LICENSE("GPL");
61 MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA");
64 * Codec dependent region
68 #ifdef CONFIG_SENSORS_TLV320AIC23
70 extern __inline__ void audio_aic23_write(u8, u16);
72 #define MIXER_NAME "Mixer AIC23"
73 #define SND_OMAP_WRITE(reg, val) audio_aic23_write(reg, val)
77 /* Callback Functions */
78 #define OMAP_BOOL(xname, xindex, reg, reg_index, mask, invert) \
80 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
83 .info = snd_omap_info_bool, \
84 .get = snd_omap_get_bool, \
85 .put = snd_omap_put_bool, \
86 .private_value = reg | (reg_index << 8) | (invert << 10) | (mask << 12) \
89 #define OMAP_MUX(xname, reg, reg_index, mask) \
91 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
93 .info = snd_omap_info_mux, \
94 .get = snd_omap_get_mux, \
95 .put = snd_omap_put_mux, \
96 .private_value = reg | (reg_index << 8) | (mask << 10) \
99 #define OMAP_SINGLE(xname, xindex, reg, reg_index, reg_val, mask) \
101 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
104 .info = snd_omap_info_single, \
105 .get = snd_omap_get_single, \
106 .put = snd_omap_put_single, \
107 .private_value = reg | (reg_val << 8) | (reg_index << 16) | (mask << 18) \
110 #define OMAP_DOUBLE(xname, xindex, left_reg, right_reg, reg_index, mask) \
112 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
115 .info = snd_omap_info_double, \
116 .get = snd_omap_get_double, \
117 .put = snd_omap_put_double, \
118 .private_value = left_reg | (right_reg << 8) | (reg_index << 16) | (mask << 18) \
121 /* Local Registers */
122 enum snd_device_index {
125 AAC_INDEX, /* Analog Audio Control: reg = l_reg */
142 u16 snd_sidetone[6] = {
151 /* Begin Bool Functions */
153 static int snd_omap_info_bool(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
155 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
157 uinfo->value.integer.min = 0;
158 uinfo->value.integer.max = 1;
163 static int snd_omap_get_bool(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
165 int mic_index = (kcontrol->private_value >> 8) & 0x03;
166 u16 mask = (kcontrol->private_value >> 12) & 0xff;
167 int invert = (kcontrol->private_value >> 10) & 0x03;
170 ucontrol->value.integer.value[0] = (omap_regs[mic_index].l_reg & mask) ? 0 : 1;
172 ucontrol->value.integer.value[0] = (omap_regs[mic_index].l_reg & mask) ? 1 : 0;
177 static int snd_omap_put_bool(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
179 int mic_index = (kcontrol->private_value >> 8) & 0x03;
180 u16 mask = (kcontrol->private_value >> 12) & 0xff;
181 u16 reg = kcontrol->private_value & 0xff;
182 int invert = (kcontrol->private_value >> 10) & 0x03;
186 if (ucontrol->value.integer.value[0]) /* XOR */
188 omap_regs[mic_index].l_reg &= ~mask;
190 omap_regs[mic_index].l_reg |= mask;
193 omap_regs[mic_index].l_reg |= mask;
195 omap_regs[mic_index].l_reg &= ~mask;
197 SND_OMAP_WRITE(reg, omap_regs[mic_index].l_reg);
202 /* End Bool Functions */
204 /* Begin Mux Functions */
206 static int snd_omap_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
210 static char *texts[2] = { "Mic", "Line" };
212 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
214 uinfo->value.enumerated.items = 2;
216 if (uinfo->value.enumerated.item > 1)
217 uinfo->value.enumerated.item = 1;
219 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
224 static int snd_omap_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
226 u16 mask = (kcontrol->private_value >> 10) & 0xff;
227 int mux_index = (kcontrol->private_value >> 8) & 0x03;
229 ucontrol->value.enumerated.item[0] = (omap_regs[mux_index].l_reg & mask) ? 0 /* Mic */ : 1 /* Line */;
234 static int snd_omap_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
236 u16 reg = kcontrol->private_value & 0xff;
237 u16 mask = (kcontrol->private_value >> 10) & 0xff;
238 int mux_index = (kcontrol->private_value >> 8) & 0x03;
242 if (!ucontrol->value.integer.value[0])
243 omap_regs[mux_index].l_reg |= mask; /* AIC23: Mic */
245 omap_regs[mux_index].l_reg &= ~mask; /* AIC23: Line */
247 SND_OMAP_WRITE(reg, omap_regs[mux_index].l_reg);
252 /* End Mux Functions */
254 /* Begin Single Functions */
256 static int snd_omap_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
258 int mask = (kcontrol->private_value >> 18) & 0xff;
259 int reg_val = (kcontrol->private_value >> 8) & 0xff;
261 uinfo->type = mask ? SNDRV_CTL_ELEM_TYPE_INTEGER : SNDRV_CTL_ELEM_TYPE_BOOLEAN;
263 uinfo->value.integer.min = 0;
264 uinfo->value.integer.max = reg_val-1;
269 static int snd_omap_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
271 u16 reg_val = (kcontrol->private_value >> 8) & 0xff;
273 ucontrol->value.integer.value[0] = snd_sidetone[reg_val];
278 static int snd_omap_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
280 u16 reg_index = (kcontrol->private_value >> 16) & 0x03;
281 u16 mask = (kcontrol->private_value >> 18) & 0x1ff;
282 u16 reg = kcontrol->private_value & 0xff;
283 u16 reg_val = (kcontrol->private_value >> 8) & 0xff;
288 if ((omap_regs[reg_index].l_reg != (ucontrol->value.integer.value[0] & mask)))
292 omap_regs[reg_index].l_reg &= ~mask;
293 omap_regs[reg_index].l_reg |= snd_sidetone[ucontrol->value.integer.value[0]];
295 snd_sidetone[reg_val] = ucontrol->value.integer.value[0];
296 SND_OMAP_WRITE(reg, omap_regs[reg_index].l_reg);
304 /* End Single Functions */
306 /* Begin Double Functions */
308 static int snd_omap_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
310 /* mask == 0 : Switch
311 * mask != 0 : Volume */
312 int mask = (kcontrol->private_value >> 18) & 0xff;
314 uinfo->type = mask ? SNDRV_CTL_ELEM_TYPE_INTEGER : SNDRV_CTL_ELEM_TYPE_BOOLEAN;
315 uinfo->count = mask ? 2 : 1;
316 uinfo->value.integer.min = 0;
317 uinfo->value.integer.max = mask ? mask : 1;
322 static int snd_omap_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
324 /* mask == 0 : Switch
325 * mask != 0 : Volume */
326 int mask = (kcontrol->private_value >> 18) & 0xff;
327 int vol_index = (kcontrol->private_value >> 16) & 0x03;
331 ucontrol->value.integer.value[0] = omap_regs[vol_index].sw;
335 ucontrol->value.integer.value[0] = omap_regs[vol_index].l_reg;
336 ucontrol->value.integer.value[1] = omap_regs[vol_index].r_reg;
342 static int snd_omap_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
344 /* mask == 0 : Switch
345 * mask != 0 : Volume */
346 int vol_index = (kcontrol->private_value >> 16) & 0x03;
347 int mask = (kcontrol->private_value >> 18) & 0xff;
348 int left_reg = kcontrol->private_value & 0xff;
349 int right_reg = (kcontrol->private_value >> 8) & 0xff;
356 if (!ucontrol->value.integer.value[0])
358 SND_OMAP_WRITE(left_reg, 0x00);
359 SND_OMAP_WRITE(right_reg, 0x00);
363 SND_OMAP_WRITE(left_reg, omap_regs[vol_index].l_reg);
364 SND_OMAP_WRITE(right_reg, omap_regs[vol_index].r_reg);
367 omap_regs[vol_index].sw = ucontrol->value.integer.value[0];
372 if ((omap_regs[vol_index].l_reg != (ucontrol->value.integer.value[0] & mask)) ||
373 (omap_regs[vol_index].r_reg != (ucontrol->value.integer.value[1] & mask)))
377 omap_regs[vol_index].l_reg &= ~mask;
378 omap_regs[vol_index].r_reg &= ~mask;
379 omap_regs[vol_index].l_reg |= (ucontrol->value.integer.value[0] & mask);
380 omap_regs[vol_index].r_reg |= (ucontrol->value.integer.value[1] & mask);
381 if (omap_regs[vol_index].sw)
383 /* write to registers only if sw is actived */
384 SND_OMAP_WRITE(left_reg, omap_regs[vol_index].l_reg);
385 SND_OMAP_WRITE(right_reg, omap_regs[vol_index].r_reg);
395 /* End Double Functions */
397 static snd_kcontrol_new_t snd_omap_controls[] = {
398 OMAP_DOUBLE("PCM Playback Switch", 0, LEFT_CHANNEL_VOLUME_ADDR, RIGHT_CHANNEL_VOLUME_ADDR,
400 OMAP_DOUBLE("PCM Playback Volume", 0, LEFT_CHANNEL_VOLUME_ADDR, RIGHT_CHANNEL_VOLUME_ADDR,
401 PCM_INDEX, OUTPUT_VOLUME_MASK),
402 OMAP_BOOL("Line Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, BYPASS_ON, 0),
403 OMAP_DOUBLE("Line Capture Switch", 0, LEFT_LINE_VOLUME_ADDR, RIGHT_LINE_VOLUME_ADDR,
405 OMAP_DOUBLE("Line Capture Volume", 0, LEFT_LINE_VOLUME_ADDR, RIGHT_LINE_VOLUME_ADDR,
406 LINE_INDEX, INPUT_VOLUME_MASK),
407 OMAP_BOOL("Mic Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, STE_ENABLED, 0),
408 OMAP_SINGLE("Mic Playback Volume", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, 5, SIDETONE_MASK),
409 OMAP_BOOL("Mic Capture Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, MICM_MUTED, 1),
410 OMAP_BOOL("Mic Booster Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, MICB_20DB, 0),
411 OMAP_MUX("Capture Source", ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, INSEL_MIC),
414 void snd_omap_init_mixer(void)
418 /* Line's default values */
419 omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
420 omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
421 omap_regs[LINE_INDEX].sw = 0;
422 SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
423 SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
425 /* Analog Audio Control's default values */
426 omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL;
428 /* Headphone's default values */
430 vol_reg &= ~OUTPUT_VOLUME_MASK;
431 vol_reg |= DEFAULT_OUTPUT_VOLUME;
432 omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME;
433 omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME;
434 omap_regs[PCM_INDEX].sw = 1;
435 SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg);
436 SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg);
441 void snd_omap_suspend_mixer(void)
443 /* Saves current values to wake-up correctly */
444 omap_pm_regs[LINE_INDEX].l_reg = omap_regs[LINE_INDEX].l_reg;
445 omap_pm_regs[LINE_INDEX].r_reg = omap_regs[LINE_INDEX].l_reg;
446 omap_pm_regs[LINE_INDEX].sw = omap_regs[LINE_INDEX].sw;
448 omap_pm_regs[AAC_INDEX].l_reg = omap_regs[AAC_INDEX].l_reg;
450 omap_pm_regs[PCM_INDEX].l_reg = omap_regs[PCM_INDEX].l_reg;
451 omap_pm_regs[PCM_INDEX].r_reg = omap_regs[PCM_INDEX].r_reg;
452 omap_pm_regs[PCM_INDEX].sw = omap_regs[PCM_INDEX].sw;
455 void snd_omap_resume_mixer(void)
457 /* Line's saved values */
458 omap_regs[LINE_INDEX].l_reg = omap_pm_regs[LINE_INDEX].l_reg;
459 omap_regs[LINE_INDEX].r_reg = omap_pm_regs[LINE_INDEX].l_reg;
460 omap_regs[LINE_INDEX].sw = omap_pm_regs[LINE_INDEX].sw;
461 SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, omap_pm_regs[LINE_INDEX].l_reg);
462 SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, omap_pm_regs[LINE_INDEX].l_reg);
464 /* Analog Audio Control's saved values */
465 omap_regs[AAC_INDEX].l_reg = omap_pm_regs[AAC_INDEX].l_reg;
466 SND_OMAP_WRITE(ANALOG_AUDIO_CONTROL_ADDR, omap_regs[AAC_INDEX].l_reg);
468 /* Headphone's saved values */
469 omap_regs[PCM_INDEX].l_reg = omap_pm_regs[PCM_INDEX].l_reg;
470 omap_regs[PCM_INDEX].r_reg = omap_pm_regs[PCM_INDEX].r_reg;
471 omap_regs[PCM_INDEX].sw = omap_pm_regs[PCM_INDEX].sw;
472 SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, omap_pm_regs[PCM_INDEX].l_reg);
473 SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, omap_pm_regs[PCM_INDEX].r_reg);
477 int snd_omap_mixer(struct snd_card_omap_aic23 *chip)
483 snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
487 strcpy(card->mixername, MIXER_NAME);
489 /* Registering alsa mixer controls */
490 for (idx = 0; idx < ARRAY_SIZE(snd_omap_controls); idx++)
491 if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_omap_controls[idx], chip))) < 0)