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