Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / i386 / isa / sound / ics2101.c
1 /*
2  * sound/ics2101.c
3  * 
4  * Driver for the ICS2101 mixer of GUS v3.7.
5  * 
6  * Copyright by Hannu Savolainen 1994
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  */
29
30 #include <i386/isa/sound/sound_config.h>
31
32 #if defined(CONFIG_GUS)
33
34 #include <i386/isa/sound/ultrasound.h>
35 #include <i386/isa/sound/gus_hw.h>
36
37 #define MIX_DEVS        (SOUND_MASK_MIC|SOUND_MASK_LINE| \
38                          SOUND_MASK_SYNTH| \
39                          SOUND_MASK_CD | SOUND_MASK_VOLUME)
40
41 extern sound_os_info *gus_osp;
42 extern int      gus_base;
43 static int      volumes[ICS_MIXDEVS];
44 static int      left_fix[ICS_MIXDEVS] = {1, 1, 1, 2, 1, 2};
45 static int      right_fix[ICS_MIXDEVS] = {2, 2, 2, 1, 2, 1};
46
47 static int
48 scale_vol(int vol)
49 {
50     /*
51      * Experimental volume scaling by Risto Kankkunen. This should give
52      * smoother volume response than just a plain multiplication.
53      */
54     int             e;
55
56     RANGE(vol, 0, 100);
57     vol = (31 * vol + 50) / 100;
58     e = 0;
59     if (vol) {
60         while (vol < 16) {
61             vol <<= 1;
62             e--;
63         }
64         vol -= 16;
65         e += 7;
66     }
67     return ((e << 4) + vol);
68 }
69
70 static void
71 write_mix(int dev, int chn, int vol)
72 {
73     int            *selector;
74     unsigned long   flags;
75     int             ctrl_addr = dev << 3;
76     int             attn_addr = dev << 3;
77
78     vol = scale_vol(vol);
79
80     if (chn == CHN_LEFT) {
81         selector = left_fix;
82         ctrl_addr |= 0x00;
83         attn_addr |= 0x02;
84     } else {
85         selector = right_fix;
86         ctrl_addr |= 0x01;
87         attn_addr |= 0x03;
88     }
89
90     flags = splhigh();
91     outb(u_MixSelect, ctrl_addr);
92     outb(u_MixData, selector[dev]);
93     outb(u_MixSelect, attn_addr);
94     outb(u_MixData, (unsigned char) vol);
95     splx(flags);
96 }
97
98 static int
99 set_volumes(int dev, int vol)
100 {
101     int             left = vol & 0x00ff;
102     int             right = (vol >> 8) & 0x00ff;
103
104     RANGE (left, 0, 100);
105     RANGE (right, 0, 100);
106
107     write_mix(dev, CHN_LEFT, left);
108     write_mix(dev, CHN_RIGHT, right);
109
110     vol = left + (right << 8);
111     volumes[dev] = vol;
112     return vol;
113 }
114
115 static int
116 ics2101_mixer_ioctl(int dev, unsigned int cmd, ioctl_arg arg)
117 {
118     if (((cmd >> 8) & 0xff) == 'M') {
119         if (cmd & IOC_IN)
120             switch (cmd & 0xff) {
121             case SOUND_MIXER_RECSRC:
122                 return gus_default_mixer_ioctl(dev, cmd, arg);
123                 break;
124
125             case SOUND_MIXER_MIC:
126                 return *(int *) arg = set_volumes(DEV_MIC, (*(int *) arg));
127                 break;
128
129             case SOUND_MIXER_CD:
130                 return *(int *) arg = set_volumes(DEV_CD, (*(int *) arg));
131                 break;
132
133             case SOUND_MIXER_LINE:
134                 return *(int *) arg = set_volumes(DEV_LINE, (*(int *) arg));
135                 break;
136
137             case SOUND_MIXER_SYNTH:
138                 return *(int *) arg = set_volumes(DEV_GF1, (*(int *) arg));
139                 break;
140
141             case SOUND_MIXER_VOLUME:
142                 return *(int *) arg = set_volumes(DEV_VOL, (*(int *) arg));
143                 break;
144
145             default:
146                 return -(EINVAL);
147             }
148         else
149             switch (cmd & 0xff) {       /* Return parameters */
150
151             case SOUND_MIXER_RECSRC:
152                 return gus_default_mixer_ioctl(dev, cmd, arg);
153                 break;
154
155             case SOUND_MIXER_DEVMASK:
156                 return *(int *) arg = MIX_DEVS;
157                 break;
158
159             case SOUND_MIXER_STEREODEVS:
160                 return *(int *) arg = SOUND_MASK_LINE | SOUND_MASK_CD |
161                         SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC;
162                 break;
163
164             case SOUND_MIXER_RECMASK:
165                 return *(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE;
166                 break;
167
168             case SOUND_MIXER_CAPS:
169                 return *(int *) arg = 0;
170                 break;
171
172             case SOUND_MIXER_MIC:
173                 return *(int *) arg = volumes[DEV_MIC];
174                 break;
175
176             case SOUND_MIXER_LINE:
177                 return *(int *) arg = volumes[DEV_LINE];
178                 break;
179
180             case SOUND_MIXER_CD:
181                 return *(int *) arg = volumes[DEV_CD];
182                 break;
183
184             case SOUND_MIXER_VOLUME:
185                 return *(int *) arg = volumes[DEV_VOL];
186                 break;
187
188             case SOUND_MIXER_SYNTH:
189                 return *(int *) arg = volumes[DEV_GF1];
190                 break;
191
192             default:
193                 return -(EINVAL);
194             }
195     }
196     return -(EINVAL);
197 }
198
199 static struct mixer_operations ics2101_mixer_operations =
200 {
201         "ICS2101 Multimedia Mixer",
202         ics2101_mixer_ioctl
203 };
204
205 void
206 ics2101_mixer_init()
207 {
208     int             i;
209
210     if (num_mixers < MAX_MIXER_DEV) {
211         mixer_devs[num_mixers++] = &ics2101_mixer_operations;
212
213         /*
214          * Some GUS v3.7 cards had some channels flipped. Disable the
215          * flipping feature if the model id is other than 5.
216          */
217
218         if (inb(u_MixSelect) != 5) {
219             for (i = 0; i < ICS_MIXDEVS; i++)
220                 left_fix[i] = 1;
221             for (i = 0; i < ICS_MIXDEVS; i++)
222                 right_fix[i] = 2;
223         }
224         set_volumes(DEV_GF1, 0x5a5a);
225         set_volumes(DEV_CD, 0x5a5a);
226         set_volumes(DEV_MIC, 0x0000);
227         set_volumes(DEV_LINE, 0x5a5a);
228         set_volumes(DEV_VOL, 0x5a5a);
229         set_volumes(DEV_UNUSED, 0x0000);
230     }
231 }
232
233 #endif