]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - sound/arm/omap/omap-alsa-tsc2102-mixer.c
Merge current mainline tree into linux-omap tree
[linux-2.6-omap-h63xx.git] / sound / arm / omap / omap-alsa-tsc2102-mixer.c
1 /*
2  * sound/arm/omap/omap-alsa-tsc2102-mixer.c
3  *
4  * Alsa mixer driver for TSC2102 chip for OMAP platforms.
5  *
6  * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
7  * Code based on the TSC2101 ALSA driver.
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version.
13  *
14  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
20  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * You should have received a copy of the  GNU General Public License along
26  * with this program; if not, write  to the Free Software Foundation, Inc.,
27  * 675 Mass Ave, Cambridge, MA 02139, USA.
28  */
29
30 #include <linux/types.h>
31 #include <linux/spi/tsc2102.h>
32
33 #include <mach/omap-alsa.h>
34
35 #include <sound/initval.h>
36 #include <sound/control.h>
37
38 #include "omap-alsa-tsc2102.h"
39 #include "omap-alsa-dma.h"
40
41 static int vol[2], mute[2], filter[2];
42
43 /*
44  * Converts the Alsa mixer volume (0 - 100) to actual Digital
45  * Gain Control (DGC) value that can be written or read from the
46  * TSC2102 registers.
47  *
48  * Note that the number "OUTPUT_VOLUME_MAX" is smaller than
49  * OUTPUT_VOLUME_MIN because DGC works as a volume decreaser.  (The
50  * higher the value sent to DAC, the more the volume of controlled
51  * channel is decreased)
52  */
53 static void set_dac_gain_stereo(int left_ch, int right_ch)
54 {
55         int lch, rch;
56
57         if (left_ch > 100)
58                 vol[0] = 100;
59         else if (left_ch < 0)
60                 vol[0] = 0;
61         else
62                 vol[0] = left_ch;
63         lch = OUTPUT_VOLUME_MIN - vol[0] *
64                 (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX) / 100;
65
66         if (right_ch > 100)
67                 vol[1] = 100;
68         else if (right_ch < 0)
69                 vol[1] = 0;
70         else
71                 vol[1] = right_ch;
72         rch = OUTPUT_VOLUME_MIN - vol[1] *
73                 (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX) / 100;
74
75         tsc2102_set_volume(lch, rch);
76 }
77
78 void init_playback_targets(void)
79 {
80         set_dac_gain_stereo(DEFAULT_OUTPUT_VOLUME, DEFAULT_OUTPUT_VOLUME);
81
82         /* Unmute */
83         tsc2102_set_mute(0, 0);
84
85         mute[0] = 0;
86         mute[1] = 0;
87         filter[0] = 0;
88         filter[1] = 0;
89 }
90
91 /*
92  * Initializes TSC 2102 and playback target.
93  */
94 void snd_omap_init_mixer(void)
95 {
96         FN_IN;
97
98         init_playback_targets();
99
100         FN_OUT(0);
101 }
102
103 static int __pcm_playback_volume_info(struct snd_kcontrol *kcontrol,
104                 struct snd_ctl_elem_info *uinfo)
105 {
106         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_INTEGER;
107         uinfo->count                    = 2;
108         uinfo->value.integer.min        = 0;
109         uinfo->value.integer.max        = 100;
110         return 0;
111 }
112
113 static int __pcm_playback_volume_get(struct snd_kcontrol *kcontrol,
114                 struct snd_ctl_elem_value *ucontrol)
115 {
116         ucontrol->value.integer.value[0] = vol[0];      /* L */
117         ucontrol->value.integer.value[1] = vol[1];      /* R */
118
119         return 0;
120 }
121
122 static int __pcm_playback_volume_put(struct snd_kcontrol *kcontrol,
123                 struct snd_ctl_elem_value *ucontrol)
124 {
125         set_dac_gain_stereo(
126                         ucontrol->value.integer.value[0],       /* L */
127                         ucontrol->value.integer.value[1]);      /* R */
128         return 1;
129 }
130
131 static int __pcm_playback_switch_info(struct snd_kcontrol *kcontrol,
132                 struct snd_ctl_elem_info *uinfo)
133 {
134         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
135         uinfo->count                    = 2;
136         uinfo->value.integer.min        = 0;
137         uinfo->value.integer.max        = 1;
138         return 0;
139 }
140
141 static int __pcm_playback_switch_get(struct snd_kcontrol *kcontrol,
142                 struct snd_ctl_elem_value *ucontrol)
143 {
144         ucontrol->value.integer.value[0] = !mute[0];            /* L */
145         ucontrol->value.integer.value[1] = !mute[1];            /* R */
146
147         return 0;
148 }
149
150 static int __pcm_playback_switch_put(struct snd_kcontrol *kcontrol,
151                 struct snd_ctl_elem_value *ucontrol)
152 {
153         mute[0] = (ucontrol->value.integer.value[0] == 0);      /* L */
154         mute[1] = (ucontrol->value.integer.value[1] == 0);      /* R */
155
156         tsc2102_set_mute(mute[0], mute[1]);
157         return 1;
158 }
159
160 static int __pcm_playback_deemphasis_info(struct snd_kcontrol *kcontrol,
161                 struct snd_ctl_elem_info *uinfo)
162 {
163         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
164         uinfo->count                    = 1;
165         uinfo->value.integer.min        = 0;
166         uinfo->value.integer.max        = 1;
167         return 0;
168 }
169
170 static int __pcm_playback_deemphasis_get(struct snd_kcontrol *kcontrol,
171                 struct snd_ctl_elem_value *ucontrol)
172 {
173         ucontrol->value.integer.value[0] = filter[0];
174         return 0;
175 }
176
177 static int __pcm_playback_deemphasis_put(struct snd_kcontrol *kcontrol,
178                 struct snd_ctl_elem_value *ucontrol)
179 {
180         filter[0] = (ucontrol->value.integer.value[0] > 0);
181
182         tsc2102_set_deemphasis(filter[0]);
183         return 1;
184 }
185
186 static int __pcm_playback_bassboost_info(struct snd_kcontrol *kcontrol,
187                 struct snd_ctl_elem_info *uinfo)
188 {
189         uinfo->type                     = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
190         uinfo->count                    = 1;
191         uinfo->value.integer.min        = 0;
192         uinfo->value.integer.max        = 1;
193         return 0;
194 }
195
196 static int __pcm_playback_bassboost_get(struct snd_kcontrol *kcontrol,
197                 struct snd_ctl_elem_value *ucontrol)
198 {
199         ucontrol->value.integer.value[0] = filter[1];
200         return 0;
201 }
202
203 static int __pcm_playback_bassboost_put(struct snd_kcontrol *kcontrol,
204                 struct snd_ctl_elem_value *ucontrol)
205 {
206         filter[1] = (ucontrol->value.integer.value[0] > 0);
207
208         tsc2102_set_bassboost(filter[1]);
209         return 1;
210 }
211
212 static struct snd_kcontrol_new tsc2102_controls[] __devinitdata = {
213         {
214                 .name   = "Master Playback Volume",
215                 .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
216                 .index  = 0,
217                 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
218                 .info   = __pcm_playback_volume_info,
219                 .get    = __pcm_playback_volume_get,
220                 .put    = __pcm_playback_volume_put,
221         },
222         {
223                 .name   = "Master Playback Switch",
224                 .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
225                 .index  = 0,
226                 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
227                 .info   = __pcm_playback_switch_info,
228                 .get    = __pcm_playback_switch_get,
229                 .put    = __pcm_playback_switch_put,
230         },
231         {
232                 .name   = "De-emphasis Filter Switch",
233                 .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
234                 .index  = 0,
235                 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
236                 .info   = __pcm_playback_deemphasis_info,
237                 .get    = __pcm_playback_deemphasis_get,
238                 .put    = __pcm_playback_deemphasis_put,
239         },
240         {
241                 .name   = "Bass-boost Filter Switch",
242                 .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
243                 .index  = 0,
244                 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
245                 .info   = __pcm_playback_bassboost_info,
246                 .get    = __pcm_playback_bassboost_get,
247                 .put    = __pcm_playback_bassboost_put,
248         },
249 };
250
251 #ifdef CONFIG_PM
252 void snd_omap_suspend_mixer(void)
253 {
254         /* Nothing to do */
255 }
256
257 void snd_omap_resume_mixer(void)
258 {
259         /* The chip was reset, restore the last used values */
260         set_dac_gain_stereo(vol[0], vol[1]);
261
262         tsc2102_set_mute(mute[0], mute[1]);
263         tsc2102_set_deemphasis(filter[0]);
264         tsc2102_set_bassboost(filter[1]);
265 }
266 #endif
267
268 int snd_omap_mixer(struct snd_card_omap_codec *tsc2102)
269 {
270         int i, err;
271
272         if (!tsc2102)
273                 return -EINVAL;
274
275         for (i = 0; i < ARRAY_SIZE(tsc2102_controls); i++) {
276                 err = snd_ctl_add(tsc2102->card,
277                                 snd_ctl_new1(&tsc2102_controls[i],
278                                 tsc2102->card));
279
280                 if (err < 0)
281                         return err;
282         }
283         return 0;
284 }