Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / sound / isa / i386 / pas2 / pas2_mixer.c
1 /*
2  * sound/pas2_mixer.c
3  * 
4  * Mixer routines for the Pro Audio Spectrum cards.
5  * 
6  * Copyright by Hannu Savolainen 1993
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  * 
28  * $FreeBSD: src/sys/i386/isa/sound/pas2_mixer.c,v 1.22 1999/12/20 18:05:00 eivind Exp $
29  */
30
31 #define _PAS2_MIXER_C_
32
33 #include <i386/isa/sound/sound_config.h>
34
35 #if defined(CONFIG_PAS)
36
37 #include <i386/isa/sound/pas_hw.h>
38
39 #define TRACE(what)             /* (what) */
40
41 extern int      translat_code;
42 extern char     pas_model;
43 extern sound_os_info *pas_osp;
44
45 static int      rec_devices = (SOUND_MASK_MIC); /* Default recording source */
46 static int      mode_control = 0;
47
48 #define POSSIBLE_RECORDING_DEVICES      (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
49                                          SOUND_MASK_CD | SOUND_MASK_ALTPCM)
50
51 #define SUPPORTED_MIXER_DEVICES         (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
52                                          SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
53                                          SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \
54                                          SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD)
55
56 static u_short levels[SOUND_MIXER_NRDEVICES] =
57 {
58         0x3232,                 /* Master Volume */
59         0x3232,                 /* Bass */
60         0x3232,                 /* Treble */
61         0x5050,                 /* FM */
62         0x4b4b,                 /* PCM */
63         0x3232,                 /* PC Speaker */
64         0x4b4b,                 /* Ext Line */
65         0x4b4b,                 /* Mic */
66         0x4b4b,                 /* CD */
67         0x6464,                 /* Recording monitor */
68         0x4b4b,                 /* SB PCM */
69         0x6464                  /* Recording level */
70 };
71
72 void mix_write(u_char data, int ioaddr);
73
74 void
75 mix_write(u_char data, int ioaddr)
76 {
77         /*
78          * The Revision D cards have a problem with their MVA508 interface.
79          * The kludge-o-rama fix is to make a 16-bit quantity with identical
80          * LSB and MSBs out of the output byte and to do a 16-bit out to the
81          * mixer port - 1. We need to do this because it isn't timing problem
82          * but chip access sequence problem.
83          */
84
85         if (pas_model == PAS_16D) {
86                 outw((ioaddr ^ translat_code) - 1, data | (data << 8));
87                 outb(0, 0x80);
88         } else
89                 pas_write(data, ioaddr);
90 }
91
92 static int
93 mixer_output(int right_vol, int left_vol, int div, int bits,
94              int mixer)
95 {                               /* Input or output mixer */
96         int             left = left_vol * div / 100;
97         int             right = right_vol * div / 100;
98
99
100         if (bits & P_M_MV508_MIXER) {   /* Select input or output mixer */
101                 left |= mixer;
102                 right |= mixer;
103         }
104         if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE) {       /* Bass and treble are
105                                                                          * mono devices */
106                 mix_write(P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
107                 mix_write(left, PARALLEL_MIXER);
108                 right_vol = left_vol;
109         } else {
110                 mix_write(P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
111                 mix_write(left, PARALLEL_MIXER);
112                 mix_write(P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
113                 mix_write(right, PARALLEL_MIXER);
114         }
115
116         return (left_vol | (right_vol << 8));
117 }
118
119 static void
120 set_mode(int new_mode)
121 {
122         mix_write(P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
123         mix_write(new_mode, PARALLEL_MIXER);
124
125         mode_control = new_mode;
126 }
127
128 static int
129 pas_mixer_set(int whichDev, u_int level)
130 {
131         int             left, right, devmask, changed, i, mixer = 0;
132
133         TRACE(printf("static int pas_mixer_set(int whichDev = %d, u_int level = %X)\n", whichDev, level));
134
135         left = level & 0x7f;
136         right = (level & 0x7f00) >> 8;
137
138         if (whichDev < SOUND_MIXER_NRDEVICES) {
139                 if ((1 << whichDev) & rec_devices)
140                         mixer = P_M_MV508_INPUTMIX;
141                 else
142                         mixer = P_M_MV508_OUTPUTMIX;
143         }
144         switch (whichDev) {
145         case SOUND_MIXER_VOLUME:        /* Master volume (0-63) */
146                 levels[whichDev] = mixer_output(right, left, 63, P_M_MV508_MASTER_A, 0);
147                 break;
148
149                 /*
150                  * Note! Bass and Treble are mono devices. Will use just the
151                  * left channel.
152                  */
153         case SOUND_MIXER_BASS:  /* Bass (0-12) */
154                 levels[whichDev] = mixer_output(right, left, 12, P_M_MV508_BASS, 0);
155                 break;
156         case SOUND_MIXER_TREBLE:        /* Treble (0-12) */
157                 levels[whichDev] = mixer_output(right, left, 12, P_M_MV508_TREBLE, 0);
158                 break;
159
160         case SOUND_MIXER_SYNTH:/* Internal synthesizer (0-31) */
161                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer);
162                 break;
163         case SOUND_MIXER_PCM:   /* PAS PCM (0-31) */
164                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer);
165                 break;
166         case SOUND_MIXER_ALTPCM:        /* SB PCM (0-31) */
167                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer);
168                 break;
169         case SOUND_MIXER_SPEAKER:       /* PC speaker (0-31) */
170                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer);
171                 break;
172         case SOUND_MIXER_LINE:  /* External line (0-31) */
173                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer);
174                 break;
175         case SOUND_MIXER_CD:    /* CD (0-31) */
176                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer);
177                 break;
178         case SOUND_MIXER_MIC:   /* External microphone (0-31) */
179                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer);
180                 break;
181         case SOUND_MIXER_IMIX:  /* Recording monitor (0-31) (Output mixer
182                                  * only) */
183                 levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER,
184                                                 P_M_MV508_OUTPUTMIX);
185                 break;
186         case SOUND_MIXER_RECLEV:        /* Recording level (0-15) */
187                 levels[whichDev] = mixer_output(right, left, 15, P_M_MV508_MASTER_B, 0);
188                 break;
189
190         case SOUND_MIXER_MUTE:
191                 return 0;
192                 break;
193
194         case SOUND_MIXER_ENHANCE:
195                 i = 0;
196                 level &= 0x7f;
197                 if (level)
198                         i = (level / 20) - 1;
199
200                 mode_control &= ~P_M_MV508_ENHANCE_BITS;
201                 mode_control |= P_M_MV508_ENHANCE_BITS;
202                 set_mode(mode_control);
203
204                 if (i)
205                         i = (i + 1) * 20;
206                 return i;
207                 break;
208
209         case SOUND_MIXER_LOUD:
210                 mode_control &= ~P_M_MV508_LOUDNESS;
211                 if (level)
212                         mode_control |= P_M_MV508_LOUDNESS;
213                 set_mode(mode_control);
214                 return !!level; /* 0 or 1 */
215                 break;
216
217         case SOUND_MIXER_RECSRC:
218                 devmask = level & POSSIBLE_RECORDING_DEVICES;
219
220                 changed = devmask ^ rec_devices;
221                 rec_devices = devmask;
222
223                 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
224                         if (changed & (1 << i)) {
225                                 pas_mixer_set(i, levels[i]);
226                         }
227                 return rec_devices;
228                 break;
229
230         default:
231                 return -(EINVAL);
232         }
233
234         return (levels[whichDev]);
235 }
236
237 /*****/
238
239 static void
240 pas_mixer_reset(void)
241 {
242         int             foo;
243
244         TRACE(printf("pas2_mixer.c: void pas_mixer_reset(void)\n"));
245
246         for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
247                 pas_mixer_set(foo, levels[foo]);
248
249         set_mode(P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40);
250 }
251
252 static int
253 pas_mixer_ioctl(int dev, u_int cmd, ioctl_arg arg)
254 {
255         TRACE(printf("pas2_mixer.c: int pas_mixer_ioctl(u_int cmd = %X, u_int arg = %X)\n", cmd, arg));
256
257         if (((cmd >> 8) & 0xff) == 'M') {
258                 if (cmd & IOC_IN)
259                         return *(int *) arg = pas_mixer_set(cmd & 0xff, (*(int *) arg));
260                 else {          /* Read parameters */
261
262                         switch (cmd & 0xff) {
263
264                         case SOUND_MIXER_RECSRC:
265                                 return *(int *) arg = rec_devices;
266                                 break;
267
268                         case SOUND_MIXER_STEREODEVS:
269                                 return *(int *) arg = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE);
270                                 break;
271
272                         case SOUND_MIXER_DEVMASK:
273                                 return *(int *) arg = SUPPORTED_MIXER_DEVICES;
274                                 break;
275
276                         case SOUND_MIXER_RECMASK:
277                                 return *(int *) arg = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES;
278                                 break;
279
280                         case SOUND_MIXER_CAPS:
281                                 return *(int *) arg = 0;        /* No special
282                                                                  * capabilities */
283                                 break;
284
285                         case SOUND_MIXER_MUTE:
286                                 return *(int *) arg = 0;        /* No mute yet */
287                                 break;
288
289                         case SOUND_MIXER_ENHANCE:
290                                 if (!(mode_control & P_M_MV508_ENHANCE_BITS))
291                                         return *(int *) arg = 0;
292                                 return *(int *) arg = ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20;
293                                 break;
294
295                         case SOUND_MIXER_LOUD:
296                                 if (mode_control & P_M_MV508_LOUDNESS)
297                                         return *(int *) arg = 1;
298                                 return *(int *) arg = 0;
299                                 break;
300
301                         default:
302                                 return *(int *) arg = levels[cmd & 0xff];
303                         }
304                 }
305         }
306         return -(EINVAL);
307 }
308
309 static struct mixer_operations pas_mixer_operations =
310 {
311         "Pro Audio Spectrum 16",
312         pas_mixer_ioctl
313 };
314
315 int
316 pas_init_mixer(void)
317 {
318         pas_mixer_reset();
319
320         if (num_mixers < MAX_MIXER_DEV)
321                 mixer_devs[num_mixers++] = &pas_mixer_operations;
322         return 1;
323 }
324
325 #endif