Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / sound / isa / msnd / msnd_pinnacle_mixer.c
1 /***************************************************************************
2                           msnd_pinnacle_mixer.c  -  description
3                              -------------------
4     begin               : Fre Jun 7 2002
5     copyright           : (C) 2002 by karsten wiese
6     email               : annabellesgarden@yahoo.de
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include <linux/io.h>
19 #include <linux/export.h>
20
21 #include <sound/core.h>
22 #include <sound/control.h>
23 #include "msnd.h"
24 #include "msnd_pinnacle.h"
25
26
27 #define MSND_MIXER_VOLUME       0
28 #define MSND_MIXER_PCM          1
29 #define MSND_MIXER_AUX          2       /* Input source 1  (aux1) */
30 #define MSND_MIXER_IMIX         3       /*  Recording monitor  */
31 #define MSND_MIXER_SYNTH        4
32 #define MSND_MIXER_SPEAKER      5
33 #define MSND_MIXER_LINE         6
34 #define MSND_MIXER_MIC          7
35 #define MSND_MIXER_RECLEV       11      /* Recording level */
36 #define MSND_MIXER_IGAIN        12      /* Input gain */
37 #define MSND_MIXER_OGAIN        13      /* Output gain */
38 #define MSND_MIXER_DIGITAL      17      /* Digital (input) 1 */
39
40 /*      Device mask bits        */
41
42 #define MSND_MASK_VOLUME        (1 << MSND_MIXER_VOLUME)
43 #define MSND_MASK_SYNTH         (1 << MSND_MIXER_SYNTH)
44 #define MSND_MASK_PCM           (1 << MSND_MIXER_PCM)
45 #define MSND_MASK_SPEAKER       (1 << MSND_MIXER_SPEAKER)
46 #define MSND_MASK_LINE          (1 << MSND_MIXER_LINE)
47 #define MSND_MASK_MIC           (1 << MSND_MIXER_MIC)
48 #define MSND_MASK_IMIX          (1 << MSND_MIXER_IMIX)
49 #define MSND_MASK_RECLEV        (1 << MSND_MIXER_RECLEV)
50 #define MSND_MASK_IGAIN         (1 << MSND_MIXER_IGAIN)
51 #define MSND_MASK_OGAIN         (1 << MSND_MIXER_OGAIN)
52 #define MSND_MASK_AUX           (1 << MSND_MIXER_AUX)
53 #define MSND_MASK_DIGITAL       (1 << MSND_MIXER_DIGITAL)
54
55 static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
56                                 struct snd_ctl_elem_info *uinfo)
57 {
58         static const char * const texts[3] = {
59                 "Analog", "MASS", "SPDIF",
60         };
61         struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
62         unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
63
64         return snd_ctl_enum_info(uinfo, 1, items, texts);
65 }
66
67 static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
68                                 struct snd_ctl_elem_value *ucontrol)
69 {
70         struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
71         /* MSND_MASK_IMIX is the default */
72         ucontrol->value.enumerated.item[0] = 0;
73
74         if (chip->recsrc & MSND_MASK_SYNTH) {
75                 ucontrol->value.enumerated.item[0] = 1;
76         } else if ((chip->recsrc & MSND_MASK_DIGITAL) &&
77                  test_bit(F_HAVEDIGITAL, &chip->flags)) {
78                 ucontrol->value.enumerated.item[0] = 2;
79         }
80
81
82         return 0;
83 }
84
85 static int snd_msndmix_set_mux(struct snd_msnd *chip, int val)
86 {
87         unsigned newrecsrc;
88         int change;
89         unsigned char msndbyte;
90
91         switch (val) {
92         case 0:
93                 newrecsrc = MSND_MASK_IMIX;
94                 msndbyte = HDEXAR_SET_ANA_IN;
95                 break;
96         case 1:
97                 newrecsrc = MSND_MASK_SYNTH;
98                 msndbyte = HDEXAR_SET_SYNTH_IN;
99                 break;
100         case 2:
101                 newrecsrc = MSND_MASK_DIGITAL;
102                 msndbyte = HDEXAR_SET_DAT_IN;
103                 break;
104         default:
105                 return -EINVAL;
106         }
107         change  = newrecsrc != chip->recsrc;
108         if (change) {
109                 change = 0;
110                 if (!snd_msnd_send_word(chip, 0, 0, msndbyte))
111                         if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) {
112                                 chip->recsrc = newrecsrc;
113                                 change = 1;
114                         }
115         }
116         return change;
117 }
118
119 static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol,
120                                 struct snd_ctl_elem_value *ucontrol)
121 {
122         struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
123         return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]);
124 }
125
126
127 static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol,
128                                    struct snd_ctl_elem_info *uinfo)
129 {
130         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
131         uinfo->count = 2;
132         uinfo->value.integer.min = 0;
133         uinfo->value.integer.max = 100;
134         return 0;
135 }
136
137 static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
138                                   struct snd_ctl_elem_value *ucontrol)
139 {
140         struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
141         int addr = kcontrol->private_value;
142         unsigned long flags;
143
144         spin_lock_irqsave(&msnd->mixer_lock, flags);
145         ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
146         ucontrol->value.integer.value[0] /= 0xFFFF;
147         ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
148         ucontrol->value.integer.value[1] /= 0xFFFF;
149         spin_unlock_irqrestore(&msnd->mixer_lock, flags);
150         return 0;
151 }
152
153 #define update_volm(a, b)                                               \
154         do {                                                            \
155                 writew((dev->left_levels[a] >> 1) *                     \
156                        readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
157                        dev->SMA + SMA_##b##Left);                       \
158                 writew((dev->right_levels[a] >> 1)  *                   \
159                        readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
160                        dev->SMA + SMA_##b##Right);                      \
161         } while (0);
162
163 #define update_potm(d, s, ar)                                           \
164         do {                                                            \
165                 writeb((dev->left_levels[d] >> 8) *                     \
166                        readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
167                        dev->SMA + SMA_##s##Left);                       \
168                 writeb((dev->right_levels[d] >> 8) *                    \
169                        readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
170                        dev->SMA + SMA_##s##Right);                      \
171                 if (snd_msnd_send_word(dev, 0, 0, ar) == 0)             \
172                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);       \
173         } while (0);
174
175 #define update_pot(d, s, ar)                                            \
176         do {                                                            \
177                 writeb(dev->left_levels[d] >> 8,                        \
178                        dev->SMA + SMA_##s##Left);                       \
179                 writeb(dev->right_levels[d] >> 8,                       \
180                        dev->SMA + SMA_##s##Right);                      \
181                 if (snd_msnd_send_word(dev, 0, 0, ar) == 0)             \
182                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);       \
183         } while (0);
184
185
186 static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
187 {
188         int bLeft, bRight;
189         int wLeft, wRight;
190         int updatemaster = 0;
191
192         if (d >= LEVEL_ENTRIES)
193                 return -EINVAL;
194
195         bLeft = left * 0xff / 100;
196         wLeft = left * 0xffff / 100;
197
198         bRight = right * 0xff / 100;
199         wRight = right * 0xffff / 100;
200
201         dev->left_levels[d] = wLeft;
202         dev->right_levels[d] = wRight;
203
204         switch (d) {
205                 /* master volume unscaled controls */
206         case MSND_MIXER_LINE:                   /* line pot control */
207                 /* scaled by IMIX in digital mix */
208                 writeb(bLeft, dev->SMA + SMA_bInPotPosLeft);
209                 writeb(bRight, dev->SMA + SMA_bInPotPosRight);
210                 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
211                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
212                 break;
213         case MSND_MIXER_MIC:                    /* mic pot control */
214                 if (dev->type == msndClassic)
215                         return -EINVAL;
216                 /* scaled by IMIX in digital mix */
217                 writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft);
218                 writeb(bRight, dev->SMA + SMA_bMicPotPosRight);
219                 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
220                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
221                 break;
222         case MSND_MIXER_VOLUME:         /* master volume */
223                 writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
224                 writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
225                 /* fall through */
226
227         case MSND_MIXER_AUX:                    /* aux pot control */
228                 /* scaled by master volume */
229                 /* fall through */
230
231                 /* digital controls */
232         case MSND_MIXER_SYNTH:                  /* synth vol (dsp mix) */
233         case MSND_MIXER_PCM:                    /* pcm vol (dsp mix) */
234         case MSND_MIXER_IMIX:                   /* input monitor (dsp mix) */
235                 /* scaled by master volume */
236                 updatemaster = 1;
237                 break;
238
239         default:
240                 return -EINVAL;
241         }
242
243         if (updatemaster) {
244                 /* update master volume scaled controls */
245                 update_volm(MSND_MIXER_PCM, wCurrPlayVol);
246                 update_volm(MSND_MIXER_IMIX, wCurrInVol);
247                 if (dev->type == msndPinnacle)
248                         update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
249                 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
250         }
251
252         return 0;
253 }
254
255 static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
256                                   struct snd_ctl_elem_value *ucontrol)
257 {
258         struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
259         int change, addr = kcontrol->private_value;
260         int left, right;
261         unsigned long flags;
262
263         left = ucontrol->value.integer.value[0] % 101;
264         right = ucontrol->value.integer.value[1] % 101;
265         spin_lock_irqsave(&msnd->mixer_lock, flags);
266         change = msnd->left_levels[addr] != left
267                 || msnd->right_levels[addr] != right;
268         snd_msndmix_set(msnd, addr, left, right);
269         spin_unlock_irqrestore(&msnd->mixer_lock, flags);
270         return change;
271 }
272
273
274 #define DUMMY_VOLUME(xname, xindex, addr) \
275 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
276   .info = snd_msndmix_volume_info, \
277   .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \
278   .private_value = addr }
279
280
281 static struct snd_kcontrol_new snd_msnd_controls[] = {
282 DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
283 DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
284 DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
285 DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),
286 DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),
287 DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
288 {
289         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
290         .name = "Capture Source",
291         .info = snd_msndmix_info_mux,
292         .get = snd_msndmix_get_mux,
293         .put = snd_msndmix_put_mux,
294 }
295 };
296
297
298 int snd_msndmix_new(struct snd_card *card)
299 {
300         struct snd_msnd *chip = card->private_data;
301         unsigned int idx;
302         int err;
303
304         if (snd_BUG_ON(!chip))
305                 return -EINVAL;
306         spin_lock_init(&chip->mixer_lock);
307         strcpy(card->mixername, "MSND Pinnacle Mixer");
308
309         for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
310                 err = snd_ctl_add(card,
311                                   snd_ctl_new1(snd_msnd_controls + idx, chip));
312                 if (err < 0)
313                         return err;
314         }
315
316         return 0;
317 }
318 EXPORT_SYMBOL(snd_msndmix_new);
319
320 void snd_msndmix_setup(struct snd_msnd *dev)
321 {
322         update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
323         update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
324         update_volm(MSND_MIXER_PCM, wCurrPlayVol);
325         update_volm(MSND_MIXER_IMIX, wCurrInVol);
326         if (dev->type == msndPinnacle) {
327                 update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
328                 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
329         }
330 }
331 EXPORT_SYMBOL(snd_msndmix_setup);
332
333 int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)
334 {
335         dev->recsrc = -1;
336         return snd_msndmix_set_mux(dev, recsrc);
337 }
338 EXPORT_SYMBOL(snd_msndmix_force_recsrc);