]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - sound/arm/omap/omap-alsa-tsc2101-mixer.c
1c7627f3c025b18dbff26a697a2e10d7949a3c12
[linux-2.6-omap-h63xx.git] / sound / arm / omap / omap-alsa-tsc2101-mixer.c
1 /*
2  * sound/arm/omap/omap-alsa-tsc2101-mixer.c
3  * 
4  * Alsa Driver for TSC2101 codec for OMAP platform boards.
5  *
6  * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and 
7  *                   Everett Coleman II <gcc80x86@fuzzyneural.net>
8  *
9  * Board initialization code is based on the code in TSC2101 OSS driver.
10  * Copyright (C) 2004 Texas Instruments, Inc.
11  *      Written by Nishanth Menon and Sriram Kannan
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  * 2006-03-01   Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
36  *              Can switch between headset and loudspeaker playback, 
37  *              mute and unmute dgc, set dgc volume. Record source switch,
38  *              keyclick, buzzer and headset volume and handset volume control 
39  *              are still missing.
40  *              
41  */
42  
43 #include "omap-alsa-tsc2101.h"
44 #include "omap-alsa-tsc2101-mixer.h"
45
46 #include <linux/types.h>
47 #include <sound/initval.h>
48 #include <sound/control.h>
49
50 //#define M_DPRINTK(ARGS...)  printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
51 #define M_DPRINTK(ARGS...)              /* nop */
52
53 #define CHECK_BIT(INDX, ARG) (((ARG) & TSC2101_BIT(INDX)) >> INDX)
54 #define IS_UNMUTED(INDX, ARG) (((CHECK_BIT(INDX, ARG)) == 0))
55
56 #define DGC_DALVL_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
57 #define DGC_DARVL_EXTRACT(ARG) ((ARG & 0x007f))
58
59 #define HGC_ADPGA_HED_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
60 #define HNGC_ADPGA_HND_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
61 #define BGC_ADPGA_BGC_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
62
63 static int current_playback_target      = PLAYBACK_TARGET_LOUDSPEAKER;
64 static int current_rec_src              = REC_SRC_SINGLE_ENDED_MICIN_HED;
65
66 /* 
67  * Simplified write for the tsc2101 audio registers.
68  */
69 inline void omap_tsc2101_audio_write(u8 address, u16 data)
70 {
71         omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS, address, data);
72 }
73
74 /* 
75  * Simplified read for the tsc2101 audio registers.
76  */
77 inline u16 omap_tsc2101_audio_read(u8 address)
78 {
79         return (omap_tsc2101_read(PAGE2_AUDIO_CODEC_REGISTERS, address));
80 }
81
82 /*
83  * For selecting tsc2101 recourd source.
84  */
85 static void set_record_source(int val)
86 {
87         u16     data;
88         
89         /* Mute Analog Sidetone
90          * Analog sidetone gain db?
91          * Cell Phone In not connected to ADC
92          * Input selected by MICSEL connected to ADC
93          */
94         data    = MPC_ASTMU | MPC_ASTG(0x45);
95         data    &= ~MPC_MICSEL(7); /* clear all MICSEL bits */
96         data    |= MPC_MICSEL(val);
97         data    |= MPC_MICADC;
98         omap_tsc2101_audio_write(TSC2101_MIXER_PGA_CTRL, data);
99         
100         current_rec_src = val;
101 }
102
103 /*
104  * Converts the Alsa mixer volume (0 - 100) to real 
105  * Digital Gain Control (DGC) value that can be written
106  * or read from the TSC2101 registry.
107  * 
108  * Note that the number "OUTPUT_VOLUME_MAX" is smaller than OUTPUT_VOLUME_MIN
109  * because DGC works as a volume decreaser. (The more bigger value is put
110  * to DGC, the more the volume of controlled channel is decreased)
111  * 
112  * In addition the TCS2101 chip would allow the maximum volume reduction be 63.5 DB
113  * but according to some tests user can not hear anything with this chip
114  * when the volume is set to be less than 25 db.
115  * Therefore this function will return a value that means 38.5 db (63.5 db - 25 db) 
116  * reduction in the channel volume, when mixer is set to 0.
117  * For mixer value 100, this will return a value that means 0 db volume reduction.
118  * ([mute_left_bit]0000000[mute_right_bit]0000000)
119 */
120 int get_mixer_volume_as_dac_gain_control_volume(int vol)
121 {
122         u16 retVal;
123
124         /* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */
125         retVal  = ((vol * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MAX;
126         /* invert the value for getting the proper range 0 min and 100 max */
127         retVal  = OUTPUT_VOLUME_MIN - retVal;
128         
129         return retVal;
130 }
131
132 /*
133  * Converts the Alsa mixer volume (0 - 100) to TSC2101 
134  * Digital Gain Control (DGC) volume. Alsa mixer volume 0
135  * is converted to value meaning the volume reduction of -38.5 db
136  * and Alsa mixer volume 100 is converted to value meaning the
137  * reduction of 0 db.
138  */
139 int set_mixer_volume_as_dac_gain_control_volume(int mixerVolL, int mixerVolR) 
140 {
141         u16 val;
142         int retVal;
143         int volL;
144         int volR;
145         
146         if ((mixerVolL < 0) || 
147             (mixerVolL > 100) ||
148             (mixerVolR < 0) ||
149             (mixerVolR > 100)) {
150                 printk(KERN_ERR "Trying a bad mixer volume as dac gain control volume value, left (%d), right (%d)!\n", mixerVolL, mixerVolR);
151                 return -EPERM;
152         }
153         M_DPRINTK("mixer volume left = %d, right = %d\n", mixerVolL, mixerVolR);        
154         volL    = get_mixer_volume_as_dac_gain_control_volume(mixerVolL);
155         volR    = get_mixer_volume_as_dac_gain_control_volume(mixerVolR);
156         
157         val     = omap_tsc2101_audio_read(TSC2101_DAC_GAIN_CTRL);
158         /* keep the old mute bit settings */
159         val     &= ~(DGC_DALVL(OUTPUT_VOLUME_MIN) | DGC_DARVL(OUTPUT_VOLUME_MIN));
160         val     |= DGC_DALVL(volL) | DGC_DARVL(volR);
161         retVal  = 2;
162         if (retVal) {
163                 omap_tsc2101_audio_write(TSC2101_DAC_GAIN_CTRL, val);
164         }
165         M_DPRINTK("to registry: left = %d, right = %d, total = %d\n", DGC_DALVL_EXTRACT(val), DGC_DARVL_EXTRACT(val), val);
166         return retVal;
167 }
168
169 /**
170  * If unmuteLeft/unmuteRight == 0  --> mute
171  * If unmuteLeft/unmuteRight == 1 --> unmute
172  */
173 int dac_gain_control_unmute(int unmuteLeft, int unmuteRight)
174 {
175         u16 val;
176         int count;
177
178         count   = 0;
179         val     = omap_tsc2101_audio_read(TSC2101_DAC_GAIN_CTRL);
180         /* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
181          * so if values are same, it's time to change the registry value.
182          */
183         if (unmuteLeft != IS_UNMUTED(15, val)) {
184                 if (unmuteLeft == 0) {
185                         /* mute --> turn bit on */
186                         val     = val | DGC_DALMU;
187                 }
188                 else {
189                         /* unmute --> turn bit off */
190                         val     = val & ~DGC_DALMU;
191                 }
192                 count++;
193         } /* L */
194         if (unmuteRight != IS_UNMUTED(7, val)) {
195                 if (unmuteRight == 0) {
196                         /* mute --> turn bit on */
197                         val     = val | DGC_DARMU;
198                 }
199                 else {
200                         /* unmute --> turn bit off */
201                         val     = val & ~DGC_DARMU;
202                 }               
203                 count++;
204         } /* R */
205         if (count) {
206                 omap_tsc2101_audio_write(TSC2101_DAC_GAIN_CTRL, val);
207                 M_DPRINTK("changed value, is_unmuted left = %d, right = %d\n", 
208                         IS_UNMUTED(15, val),
209                         IS_UNMUTED(7, val));
210         }
211         return count;   
212 }
213
214 /**
215  * unmute: 0 --> mute, 1 --> unmute
216  * page2RegIndx: Registry index in tsc2101 page2.
217  * muteBitIndx: Index number for the bit in registry that indicates whether muted or unmuted.
218  */
219 int adc_pga_unmute_control(int unmute, int page2regIndx, int muteBitIndx)
220 {
221         int count;
222         u16 val;
223         
224         count   = 0;
225         val     = omap_tsc2101_audio_read(page2regIndx);
226         /* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
227          * so if the values are same, it's time to change the registry value...
228          */
229         if (unmute != IS_UNMUTED(muteBitIndx, val)) {
230                 if (unmute == 0) {
231                         /* mute --> turn bit on */
232                         val     = val | TSC2101_BIT(muteBitIndx);
233                 }
234                 else {
235                         /* unmute --> turn bit off */
236                         val     = val & ~TSC2101_BIT(muteBitIndx);
237                 }
238                 M_DPRINTK("changed value, is_unmuted = %d\n", IS_UNMUTED(muteBitIndx, val));
239                 count++;
240         }
241         if (count) {
242                 omap_tsc2101_audio_write(page2regIndx, val);
243         }
244         return count;
245 }
246
247 /*
248  * Converts the DGC registry value read from the TSC2101 registry to 
249  * Alsa mixer volume format (0 - 100).
250  */
251 int get_dac_gain_control_volume_as_mixer_volume(u16 vol) 
252 {
253         u16 retVal;     
254
255         retVal  = OUTPUT_VOLUME_MIN - vol;
256         retVal  = ((retVal - OUTPUT_VOLUME_MAX) * 100) / OUTPUT_VOLUME_RANGE;
257         /* fix scaling error */
258         if ((retVal > 0) && (retVal < 100)) {
259                 retVal++;
260         }
261         return retVal;
262 }
263
264 /*
265  * Converts the headset gain control volume (0 - 63.5 db)
266  * to Alsa mixer volume (0 - 100)
267  */
268 int get_headset_gain_control_volume_as_mixer_volume(u16 registerVal) 
269 {
270         u16 retVal;
271         
272         retVal  = ((registerVal * 100) / INPUT_VOLUME_RANGE);
273         return retVal;
274 }
275
276 /*
277  * Converts the handset gain control volume (0 - 63.5 db)
278  * to Alsa mixer volume (0 - 100)
279  */
280 int get_handset_gain_control_volume_as_mixer_volume(u16 registerVal) 
281 {
282         return get_headset_gain_control_volume_as_mixer_volume(registerVal);
283 }
284
285 /*
286  * Converts the Alsa mixer volume (0 - 100) to 
287  * headset gain control volume (0 - 63.5 db)
288  */
289 int get_mixer_volume_as_headset_gain_control_volume(u16 mixerVal) 
290 {
291         u16 retVal;
292         
293         retVal  = ((mixerVal * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;   
294         return retVal;
295 }
296
297 /*
298  * Writes Alsa mixer volume (0 - 100) to TSC2101 headset volume registry in
299  * a TSC2101 format. (0 - 63.5 db)
300  * In TSC2101 OSS driver this functionality was controlled with "SET_LINE" parameter.
301  */
302 int set_mixer_volume_as_headset_gain_control_volume(int mixerVol) 
303 {
304         int volume;
305         int retVal;
306         u16 val;
307
308         if (mixerVol < 0 || mixerVol > 100) {
309                 M_DPRINTK("Trying a bad headset mixer volume value(%d)!\n", mixerVol);
310                 return -EPERM;
311         }
312         M_DPRINTK("mixer volume = %d\n", mixerVol);
313         /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
314         /* NOTE: 0 is minimum volume and not mute */
315         volume  = get_mixer_volume_as_headset_gain_control_volume(mixerVol);    
316         val     = omap_tsc2101_audio_read(TSC2101_HEADSET_GAIN_CTRL);
317         /* preserve the old mute settings */
318         val     &= ~(HGC_ADPGA_HED(INPUT_VOLUME_MAX));
319         val     |= HGC_ADPGA_HED(volume);
320         omap_tsc2101_audio_write(TSC2101_HEADSET_GAIN_CTRL, val);       
321         retVal  = 1;
322         
323         M_DPRINTK("to registry = %d\n", val);   
324         return retVal;
325 }
326
327 /*
328  * Writes Alsa mixer volume (0 - 100) to TSC2101 handset volume registry in
329  * a TSC2101 format. (0 - 63.5 db)
330  * In TSC2101 OSS driver this functionality was controlled with "SET_MIC" parameter.
331  */
332 int set_mixer_volume_as_handset_gain_control_volume(int mixerVol) 
333 {
334         int volume;
335         int retVal;
336         u16 val;        
337
338         if (mixerVol < 0 || mixerVol > 100) {
339                 M_DPRINTK("Trying a bad mic mixer volume value(%d)!\n", mixerVol);
340                 return -EPERM;
341         }
342         M_DPRINTK("mixer volume = %d\n", mixerVol);
343         /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range
344          * NOTE: 0 is minimum volume and not mute 
345          */
346         volume  = get_mixer_volume_as_headset_gain_control_volume(mixerVol);
347         val     = omap_tsc2101_audio_read(TSC2101_HANDSET_GAIN_CTRL);
348         /* preserve the old mute settigns */
349         val     &= ~(HNGC_ADPGA_HND(INPUT_VOLUME_MAX));
350         val     |= HNGC_ADPGA_HND(volume);
351         omap_tsc2101_audio_write(TSC2101_HANDSET_GAIN_CTRL, val);
352         retVal  = 1;
353         
354         M_DPRINTK("to registry = %d\n", val);   
355         return retVal;
356 }
357
358 void set_loudspeaker_to_playback_target(void)
359 {
360         /* power down SPK1, SPK2 and loudspeaker */
361         omap_tsc2101_audio_write(TSC2101_CODEC_POWER_CTRL,
362                         CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);       
363         /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled
364          * 1dB AGC hysteresis
365          * MICes bias 2V
366          */
367         omap_tsc2101_audio_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0));
368
369         /* DAC left and right routed to SPK1/SPK2
370          * SPK1/SPK2 unmuted
371          * Keyclicks routed to SPK1/SPK2 */
372         omap_tsc2101_audio_write(TSC2101_AUDIO_CTRL_5, 
373                         AC5_DIFFIN |
374                         AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
375                         AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2);
376         
377         /* routing selected to SPK1 goes also to OUT8P/OUT8N. (loudspeaker)
378          * analog sidetone routed to loudspeaker
379          * buzzer pga routed to loudspeaker
380          * keyclick routing to loudspeaker
381          * cellphone input routed to loudspeaker
382          * mic selection (control register 04h/page2) routed to cell phone output (CP_OUT)
383          * routing selected for SPK1 goes also to cellphone output (CP_OUT)
384          * OUT8P/OUT8N (loudspeakers) unmuted (0 = unmuted)
385          * Cellphone output is not muted (0 = unmuted)
386          * Enable loudspeaker short protection control (0 = enable protection)
387          * VGND short protection control (0 = enable protection)
388          */
389         omap_tsc2101_audio_write(TSC2101_AUDIO_CTRL_6,
390                         AC6_SPL2LSK | AC6_AST2LSK | AC6_BUZ2LSK | AC6_KCL2LSK |
391                         AC6_CPI2LSK | AC6_MIC2CPO | AC6_SPL2CPO);
392         current_playback_target = PLAYBACK_TARGET_LOUDSPEAKER;
393 }
394
395 void set_headphone_to_playback_target(void)
396 {
397         /* power down SPK1, SPK2 and loudspeaker */
398         omap_tsc2101_audio_write(TSC2101_CODEC_POWER_CTRL,
399                         CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);
400         /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
401         /* 1dB AGC hysteresis */
402         /* MICes bias 2V */
403         omap_tsc2101_audio_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0));
404                                 
405         /* DAC left and right routed to SPK1/SPK2
406          * SPK1/SPK2 unmuted
407          * Keyclicks routed to SPK1/SPK2 */
408         omap_tsc2101_audio_write(TSC2101_AUDIO_CTRL_5,
409                         AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
410                         AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
411                         AC5_HDSCPTC);
412                         
413         /* OUT8P/OUT8N muted, CPOUT muted */
414         omap_tsc2101_audio_write(TSC2101_AUDIO_CTRL_6,
415                         AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
416                         AC6_VGNDSCPTC);
417         current_playback_target = PLAYBACK_TARGET_HEADPHONE;
418 }
419
420 /*
421  * Checks whether the headset is detected.
422  * If headset is detected, the type is returned. Type can be
423  *      0x01    = stereo headset detected
424  *      0x02    = cellurar headset detected
425  *      0x03    = stereo + cellurar headset detected
426  * If headset is not detected 0 is returned.
427  */
428 u16 get_headset_detected(void)
429 {
430         u16     curDetected;
431         u16     curType;
432         u16     curVal;
433         
434         curType = 0;    /* not detected */
435         curVal  = omap_tsc2101_audio_read(TSC2101_AUDIO_CTRL_7);
436         curDetected     = curVal & AC7_HDDETFL;
437         if (curDetected) {
438                 printk("headset detected, checking type from %d \n", curVal);
439                 curType = ((curVal & 0x6000) >> 13);
440                 printk("headset type detected = %d \n", curType);
441         }
442         else {
443                 printk("headset not detected\n");
444         }
445         return curType;
446 }
447
448 void init_playback_targets(void)
449 {
450         u16     val;
451
452         set_loudspeaker_to_playback_target();
453         /* Left line input volume control
454          * = SET_LINE in the OSS driver
455          */
456         set_mixer_volume_as_headset_gain_control_volume(DEFAULT_INPUT_VOLUME);
457
458         /* Set headset to be controllable by handset mixer
459          * AGC enable for handset input
460          * Handset input not muted
461          */
462         val     = omap_tsc2101_audio_read(TSC2101_HANDSET_GAIN_CTRL);
463         val     = val | HNGC_AGCEN_HND; 
464         val     = val & ~HNGC_ADMUT_HND;
465         omap_tsc2101_audio_write(TSC2101_HANDSET_GAIN_CTRL, val);       
466                         
467         /* mic input volume control
468          * SET_MIC in the OSS driver 
469          */
470         set_mixer_volume_as_handset_gain_control_volume(DEFAULT_INPUT_VOLUME);
471
472         /* Left/Right headphone channel volume control
473          * Zero-cross detect on
474          */
475         set_mixer_volume_as_dac_gain_control_volume(DEFAULT_OUTPUT_VOLUME, DEFAULT_OUTPUT_VOLUME);      
476         /* unmute */
477         dac_gain_control_unmute(1, 1);
478 }
479
480 /*
481  * Initializes tsc2101 recourd source (to line) and playback target (to loudspeaker)
482  */
483 void snd_omap_init_mixer(void)
484 {       
485         FN_IN;
486         
487         /* Headset/Hook switch detect enabled */
488         omap_tsc2101_audio_write(TSC2101_AUDIO_CTRL_7, AC7_DETECT);
489
490         /* Select headset to record source (MIC_INHED)*/
491         set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HED);
492         /* Init loudspeaker as a default playback target*/
493         init_playback_targets();
494
495         FN_OUT(0);
496 }
497
498 static int __pcm_playback_target_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
499 {
500         static char *texts[PLAYBACK_TARGET_COUNT] = {
501                 "Loudspeaker", "Headphone"
502         };
503
504         uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
505         uinfo->count = 1;
506         uinfo->value.enumerated.items = PLAYBACK_TARGET_COUNT;
507         if (uinfo->value.enumerated.item > PLAYBACK_TARGET_COUNT - 1) {
508                 uinfo->value.enumerated.item = PLAYBACK_TARGET_COUNT - 1;
509         }
510         strcpy(uinfo->value.enumerated.name,
511         texts[uinfo->value.enumerated.item]);
512         return 0;
513 }
514
515 static int __pcm_playback_target_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
516 {
517         ucontrol->value.integer.value[0] = current_playback_target;
518         return 0;
519 }
520
521 static int __pcm_playback_target_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
522 {
523         int     retVal;
524         int     curVal;
525         
526         retVal  = 0;
527         curVal  = ucontrol->value.integer.value[0];
528         if ((curVal >= 0) &&
529             (curVal < PLAYBACK_TARGET_COUNT) &&
530             (curVal != current_playback_target)) {              
531                 if (curVal == 0) {
532                         set_loudspeaker_to_playback_target();           
533                 }
534                 else {
535                         set_headphone_to_playback_target();
536                 }
537                 retVal  = 1;
538         }
539         return retVal;
540 }       
541
542 static int __pcm_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
543 {
544         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_INTEGER;
545         uinfo->count                    = 2;
546         uinfo->value.integer.min        = 0;
547         uinfo->value.integer.max        = 100;
548         return 0;
549 }
550
551 /*
552  * Alsa mixer interface function for getting the volume read from the DGC in a 
553  * 0 -100 alsa mixer format.
554  */
555 static int __pcm_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
556 {
557         u16 volL;
558         u16 volR;       
559         u16 val;
560         
561         val     = omap_tsc2101_audio_read(TSC2101_DAC_GAIN_CTRL);
562         M_DPRINTK("registry value = %d!\n", val);
563         volL    = DGC_DALVL_EXTRACT(val);
564         volR    = DGC_DARVL_EXTRACT(val);
565         /* make sure that other bits are not on */
566         volL    = volL & ~DGC_DALMU;
567         volR    = volR & ~DGC_DARMU;
568
569         volL    = get_dac_gain_control_volume_as_mixer_volume(volL);
570         volR    = get_dac_gain_control_volume_as_mixer_volume(volR);
571         
572         ucontrol->value.integer.value[0]        = volL; /* L */
573         ucontrol->value.integer.value[1]        = volR; /* R */
574         
575         M_DPRINTK("mixer volume left = %ld, right = %ld\n", ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]);
576         return 0;
577 }
578
579 static int __pcm_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
580 {
581         return set_mixer_volume_as_dac_gain_control_volume(ucontrol->value.integer.value[0], 
582                                                         ucontrol->value.integer.value[1]);
583 }
584
585 static int __pcm_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
586 {
587         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
588         uinfo->count                    = 2;
589         uinfo->value.integer.min        = 0;
590         uinfo->value.integer.max        = 1;
591         return 0;
592 }
593
594 /* 
595  * When DGC_DALMU (bit 15) is 1, the left channel is muted.
596  * When DGC_DALMU is 0, left channel is not muted.
597  * Same logic apply also for the right channel.
598  */
599 static int __pcm_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
600 {
601         u16 val = omap_tsc2101_audio_read(TSC2101_DAC_GAIN_CTRL);
602         
603         ucontrol->value.integer.value[0]        = IS_UNMUTED(15, val);  // left
604         ucontrol->value.integer.value[1]        = IS_UNMUTED(7, val);   // right
605         return 0;
606 }
607
608 static int __pcm_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
609 {
610         return dac_gain_control_unmute(ucontrol->value.integer.value[0], 
611                                         ucontrol->value.integer.value[1]);
612 }
613
614 static int __headset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
615 {
616         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_INTEGER;
617         uinfo->count                    = 1;
618         uinfo->value.integer.min        = 0;
619         uinfo->value.integer.max        = 100;
620         return 0;
621 }
622
623 static int __headset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
624 {
625         u16 val;
626         u16 vol;
627         
628         val     = omap_tsc2101_audio_read(TSC2101_HEADSET_GAIN_CTRL);
629         M_DPRINTK("registry value = %d\n", val);
630         vol     = HGC_ADPGA_HED_EXTRACT(val);
631         vol     = vol & ~HGC_ADMUT_HED;
632
633         vol     = get_headset_gain_control_volume_as_mixer_volume(vol);
634         ucontrol->value.integer.value[0]        = vol;
635         
636         M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
637         return 0;
638 }
639
640 static int __headset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
641 {
642         return set_mixer_volume_as_headset_gain_control_volume(ucontrol->value.integer.value[0]);       
643 }
644
645 static int __headset_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
646 {
647         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
648         uinfo->count                    = 1;
649         uinfo->value.integer.min        = 0;
650         uinfo->value.integer.max        = 1;
651         return 0;
652 }
653
654 /* When HGC_ADMUT_HED (bit 15) is 1, the headset is muted.
655  * When HGC_ADMUT_HED is 0, headset is not muted.
656  */
657 static int __headset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
658 {
659         u16 val = omap_tsc2101_audio_read(TSC2101_HEADSET_GAIN_CTRL);
660         ucontrol->value.integer.value[0]        = IS_UNMUTED(15, val);
661         return 0;
662 }
663
664 static int __headset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
665 {
666         // mute/unmute headset
667         return adc_pga_unmute_control(ucontrol->value.integer.value[0],
668                                 TSC2101_HEADSET_GAIN_CTRL,
669                                 15);
670 }
671
672 static int __handset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
673 {
674         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_INTEGER;
675         uinfo->count                    = 1;
676         uinfo->value.integer.min        = 0;
677         uinfo->value.integer.max        = 100;
678         return 0;
679 }
680
681 static int __handset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
682 {
683         u16 val;
684         u16 vol;
685         
686         val     = omap_tsc2101_audio_read(TSC2101_HANDSET_GAIN_CTRL);
687         M_DPRINTK("registry value = %d\n", val);
688         vol     = HNGC_ADPGA_HND_EXTRACT(val);
689         vol     = vol & ~HNGC_ADMUT_HND;
690         vol     = get_handset_gain_control_volume_as_mixer_volume(vol);
691         ucontrol->value.integer.value[0]        = vol;
692         
693         M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
694         return 0;
695 }
696
697 static int __handset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
698 {
699         return set_mixer_volume_as_handset_gain_control_volume(ucontrol->value.integer.value[0]);       
700 }
701
702 static int __handset_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
703 {
704         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
705         uinfo->count                    = 1;
706         uinfo->value.integer.min        = 0;
707         uinfo->value.integer.max        = 1;
708         return 0;
709 }
710
711 /* When HNGC_ADMUT_HND (bit 15) is 1, the handset is muted.
712  * When HNGC_ADMUT_HND is 0, handset is not muted.
713  */
714 static int __handset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
715 {
716         u16 val = omap_tsc2101_audio_read(TSC2101_HANDSET_GAIN_CTRL);
717         ucontrol->value.integer.value[0]        = IS_UNMUTED(15, val);
718         return 0;
719 }
720
721 static int __handset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
722 {
723         // handset mute/unmute
724         return adc_pga_unmute_control(ucontrol->value.integer.value[0],
725                                 TSC2101_HANDSET_GAIN_CTRL,
726                                 15);
727 }
728
729 static snd_kcontrol_new_t tsc2101_control[] __devinitdata = {
730         {
731                 .name  = "Target Playback Route",
732                 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
733                 .index = 0,
734                 .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
735                 .info  = __pcm_playback_target_info,
736                 .get   = __pcm_playback_target_get,
737                 .put   = __pcm_playback_target_put,
738         }, {
739                 .name  = "Master Playback Volume",
740                 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
741                 .index = 0,
742                 .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
743                 .info  = __pcm_playback_volume_info,
744                 .get   = __pcm_playback_volume_get,
745                 .put   = __pcm_playback_volume_put,
746         }, {
747                 .name  = "Master Playback Switch",
748                 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
749                 .index = 0,
750                 .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
751                 .info  = __pcm_playback_switch_info,
752                 .get   = __pcm_playback_switch_get,
753                 .put   = __pcm_playback_switch_put,
754         }, {
755                 .name  = "Headset Playback Volume",
756                 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
757                 .index = 0,
758                 .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
759                 .info  = __headset_playback_volume_info,
760                 .get   = __headset_playback_volume_get,
761                 .put   = __headset_playback_volume_put,
762         }, {
763                 .name  = "Headset Playback Switch",
764                 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
765                 .index = 0,
766                 .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
767                 .info  = __headset_playback_switch_info,
768                 .get   = __headset_playback_switch_get,
769                 .put   = __headset_playback_switch_put,
770         }, {
771                 .name  = "Handset Playback Volume",
772                 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
773                 .index = 0,
774                 .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
775                 .info  = __handset_playback_volume_info,
776                 .get   = __handset_playback_volume_get,
777                 .put   = __handset_playback_volume_put,
778         }, {
779                 .name  = "Handset Playback Switch",
780                 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
781                 .index = 0,
782                 .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
783                 .info  = __handset_playback_switch_info,
784                 .get   = __handset_playback_switch_get,
785                 .put   = __handset_playback_switch_put,
786         }       
787 };
788
789 #ifdef CONFIG_PM
790
791 void snd_omap_suspend_mixer(void)
792 {
793 }
794
795 void snd_omap_resume_mixer(void)
796 {
797         snd_omap_init_mixer();
798 }
799 #endif
800
801 int snd_omap_mixer(struct snd_card_omap_codec *tsc2101) 
802 {
803         int i=0;
804         int err=0;
805
806         if (!tsc2101) {
807                 return -EINVAL;
808         }
809         for (i=0; i < ARRAY_SIZE(tsc2101_control); i++) {
810                 if ((err = snd_ctl_add(tsc2101->card, 
811                                 snd_ctl_new1(&tsc2101_control[i], 
812                                 tsc2101->card))) < 0) {
813                         return err;
814                 }
815         }
816         return 0;
817 }