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