]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - sound/arm/omap-alsa-mixer.c
476f10609215b4d46158c9f322bb5217f9532ca2
[linux-2.6-omap-h63xx.git] / sound / arm / omap-alsa-mixer.c
1 /*
2  * sound/arm/omap-alsa-mixer.c
3  * 
4  * Alsa Driver Mixer for generic codecs for omap boards
5  *
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
9  *
10  * Based on es1688_lib.c, 
11  * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
12  *  
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.
17  *
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.
28  *
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.
32  *
33  * History:
34  *
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
38  */
39
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>
49
50 #include <asm/hardware.h>
51 #include <asm/mach-types.h>
52 #include <asm/arch/dma.h>
53 #include <asm/arch/aic23.h>
54
55 #include "omap-aic23.h"
56 #include <sound/initval.h>
57 #include <sound/control.h>
58
59 MODULE_AUTHOR("David Cohen, Daniel Petrini - INdT");
60 MODULE_LICENSE("GPL");
61 MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA");
62
63 /*
64  * Codec dependent region
65  */
66
67 /* Codec AIC23 */
68 #if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE)
69
70 extern __inline__ void audio_aic23_write(u8, u16);
71
72 #define MIXER_NAME                   "Mixer AIC23"
73 #define SND_OMAP_WRITE(reg, val)     audio_aic23_write(reg, val)
74
75 #endif
76
77 /* Callback Functions */
78 #define OMAP_BOOL(xname, xindex, reg, reg_index, mask, invert) \
79 { \
80         .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
81         .name = xname, \
82         .index = xindex, \
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) \
87 }
88
89 #define OMAP_MUX(xname, reg, reg_index, mask) \
90 { \
91         .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
92         .name = xname, \
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) \
97 }
98
99 #define OMAP_SINGLE(xname, xindex, reg, reg_index, reg_val, mask) \
100 {\
101         .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
102         .name = xname, \
103         .index = xindex, \
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) \
108 }
109
110 #define OMAP_DOUBLE(xname, xindex, left_reg, right_reg, reg_index, mask) \
111 {\
112         .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
113         .name = xname, \
114         .index = xindex, \
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) \
119 }
120
121 /* Local Registers */
122 enum snd_device_index {
123         PCM_INDEX = 0,
124         LINE_INDEX,
125         AAC_INDEX, /* Analog Audio Control: reg = l_reg */
126 };
127
128 struct {
129         u16 l_reg;
130         u16 r_reg;
131         u8 sw;
132 } omap_regs[3];
133
134 #ifdef CONFIG_PM
135 struct {
136         u16 l_reg;
137         u16 r_reg;
138         u8 sw;
139 } omap_pm_regs[3];
140 #endif
141
142 u16 snd_sidetone[6] = {
143         SIDETONE_18,
144         SIDETONE_12,
145         SIDETONE_9,
146         SIDETONE_6,
147         SIDETONE_0,
148         0
149 };
150
151 /* Begin Bool Functions */
152
153 static int snd_omap_info_bool(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
154 {
155         uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
156         uinfo->count = 1;
157         uinfo->value.integer.min = 0;
158         uinfo->value.integer.max = 1;
159         
160         return 0;
161 }
162
163 static int snd_omap_get_bool(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
164 {
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;
168         
169         if (invert)
170                 ucontrol->value.integer.value[0] = (omap_regs[mic_index].l_reg & mask) ? 0 : 1;
171         else
172                 ucontrol->value.integer.value[0] = (omap_regs[mic_index].l_reg & mask) ? 1 : 0;
173         
174         return 0;
175 }
176
177 static int snd_omap_put_bool(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
178 {
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;
183         
184         int changed = 1;
185
186         if (ucontrol->value.integer.value[0]) /* XOR */
187                 if (invert)
188                         omap_regs[mic_index].l_reg &= ~mask;
189                 else
190                         omap_regs[mic_index].l_reg |= mask;
191         else
192                 if (invert)
193                         omap_regs[mic_index].l_reg |= mask;
194                 else
195                         omap_regs[mic_index].l_reg &= ~mask;
196                 
197         SND_OMAP_WRITE(reg, omap_regs[mic_index].l_reg);
198         
199         return changed;
200 }
201
202 /* End Bool Functions */
203
204 /* Begin Mux Functions */
205
206 static int snd_omap_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
207 {
208         /* Mic = 0
209          * Line = 1 */
210         static char *texts[2] = { "Mic", "Line" };
211
212         uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
213         uinfo->count = 1;
214         uinfo->value.enumerated.items = 2;
215         
216         if (uinfo->value.enumerated.item > 1)
217                 uinfo->value.enumerated.item = 1;
218         
219         strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
220         
221         return 0;
222 }
223
224 static int snd_omap_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
225 {
226         u16 mask = (kcontrol->private_value >> 10) & 0xff;
227         int mux_index = (kcontrol->private_value >> 8) & 0x03;
228
229         ucontrol->value.enumerated.item[0] = (omap_regs[mux_index].l_reg & mask) ? 0 /* Mic */ : 1 /* Line */;
230         
231         return 0;
232 }
233
234 static int snd_omap_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
235 {
236         u16 reg = kcontrol->private_value & 0xff;
237         u16 mask = (kcontrol->private_value >> 10) & 0xff;
238         int mux_index = (kcontrol->private_value >> 8) & 0x03;
239         
240         int changed = 1;
241
242         if (!ucontrol->value.integer.value[0])
243                 omap_regs[mux_index].l_reg |= mask; /* AIC23: Mic */
244         else
245                 omap_regs[mux_index].l_reg &= ~mask; /* AIC23: Line */
246         
247         SND_OMAP_WRITE(reg, omap_regs[mux_index].l_reg);
248         
249         return changed;
250 }
251
252 /* End Mux Functions */
253
254 /* Begin Single Functions */
255
256 static int snd_omap_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
257 {
258         int mask = (kcontrol->private_value >> 18) & 0xff;
259         int reg_val = (kcontrol->private_value >> 8) & 0xff;
260         
261         uinfo->type = mask ? SNDRV_CTL_ELEM_TYPE_INTEGER : SNDRV_CTL_ELEM_TYPE_BOOLEAN;
262         uinfo->count = 1;
263         uinfo->value.integer.min = 0;
264         uinfo->value.integer.max = reg_val-1;
265         
266         return 0;
267 }
268
269 static int snd_omap_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
270 {
271         u16 reg_val = (kcontrol->private_value >> 8) & 0xff;
272
273         ucontrol->value.integer.value[0] = snd_sidetone[reg_val];
274         
275         return 0;
276 }
277
278 static int snd_omap_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
279 {
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;
284
285         int changed = 0;
286
287         /* Volume */
288         if ((omap_regs[reg_index].l_reg != (ucontrol->value.integer.value[0] & mask)))
289         {
290                 changed = 1;
291         
292                 omap_regs[reg_index].l_reg &= ~mask;
293                 omap_regs[reg_index].l_reg |= snd_sidetone[ucontrol->value.integer.value[0]];
294
295                 snd_sidetone[reg_val] = ucontrol->value.integer.value[0];
296                 SND_OMAP_WRITE(reg, omap_regs[reg_index].l_reg);
297         }
298         else
299                 changed = 0;
300         
301         return changed;
302 }
303
304 /* End Single Functions */
305
306 /* Begin Double Functions */
307
308 static int snd_omap_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
309 {
310         /* mask == 0 : Switch
311          * mask != 0 : Volume */
312         int mask = (kcontrol->private_value >> 18) & 0xff;
313
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;
318         
319         return 0;
320 }
321
322 static int snd_omap_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
323 {
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;
328         
329         if (!mask)
330                 /* Switch */
331                 ucontrol->value.integer.value[0] = omap_regs[vol_index].sw;
332         else
333         {
334                 /* Volume */
335                 ucontrol->value.integer.value[0] = omap_regs[vol_index].l_reg;
336                 ucontrol->value.integer.value[1] = omap_regs[vol_index].r_reg;
337         }
338
339         return 0;
340 }
341
342 static int snd_omap_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
343 {
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;
350
351         int changed = 0;
352
353         if (!mask)
354         {
355                 /* Switch */
356                 if (!ucontrol->value.integer.value[0])
357                 {
358                         SND_OMAP_WRITE(left_reg, 0x00);
359                         SND_OMAP_WRITE(right_reg, 0x00);
360                 }
361                 else
362                 {
363                         SND_OMAP_WRITE(left_reg, omap_regs[vol_index].l_reg);
364                         SND_OMAP_WRITE(right_reg, omap_regs[vol_index].r_reg);
365                 }
366                 changed = 1;
367                 omap_regs[vol_index].sw = ucontrol->value.integer.value[0]; 
368         }
369         else
370         {
371                 /* Volume */
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)))
374                 {
375                         changed = 1;
376                 
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)
382                         {
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);
386                         }
387                 }
388                 else
389                         changed = 0;
390         }
391         
392         return changed;
393 }
394
395 /* End Double Functions */
396
397 static snd_kcontrol_new_t snd_omap_controls[] = {
398         OMAP_DOUBLE("PCM Playback Switch", 0, LEFT_CHANNEL_VOLUME_ADDR, RIGHT_CHANNEL_VOLUME_ADDR,
399                      PCM_INDEX, 0x00),
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,
404                      LINE_INDEX, 0x00),
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),
412 };
413
414 void snd_omap_init_mixer(void)
415 {
416         u16 vol_reg;
417
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);
424         
425         /* Analog Audio Control's default values */
426         omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL;
427         
428         /* Headphone's default values */
429         vol_reg = LZC_ON;
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);
437 }
438
439 #ifdef CONFIG_PM
440
441 void snd_omap_suspend_mixer(void)
442 {
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;
447         
448         omap_pm_regs[AAC_INDEX].l_reg = omap_regs[AAC_INDEX].l_reg;
449         
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;
453 }
454
455 void snd_omap_resume_mixer(void)
456 {
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);
463         
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);
467         
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);
474 }
475 #endif
476
477 int snd_omap_mixer(struct snd_card_omap_codec *chip)
478 {
479         snd_card_t *card;
480         unsigned int idx;
481         int err;
482
483         snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
484
485         card = chip->card;
486
487         strcpy(card->mixername, MIXER_NAME);
488
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)
492                         return err;
493
494         return 0;
495 }
496