diff options
Diffstat (limited to 'sound/oss/ics2101.c')
-rw-r--r-- | sound/oss/ics2101.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/sound/oss/ics2101.c b/sound/oss/ics2101.c new file mode 100644 index 000000000000..d5f3be8550f3 --- /dev/null +++ b/sound/oss/ics2101.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * sound/ics2101.c | ||
3 | * | ||
4 | * Driver for the ICS2101 mixer of GUS v3.7. | ||
5 | * | ||
6 | * | ||
7 | * Copyright (C) by Hannu Savolainen 1993-1997 | ||
8 | * | ||
9 | * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) | ||
10 | * Version 2 (June 1991). See the "COPYING" file distributed with this software | ||
11 | * for more info. | ||
12 | * | ||
13 | * | ||
14 | * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) | ||
15 | * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init() | ||
16 | */ | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | #include "sound_config.h" | ||
20 | |||
21 | #include <linux/ultrasound.h> | ||
22 | |||
23 | #include "gus.h" | ||
24 | #include "gus_hw.h" | ||
25 | |||
26 | #define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ | ||
27 | SOUND_MASK_SYNTH| \ | ||
28 | SOUND_MASK_CD | SOUND_MASK_VOLUME) | ||
29 | |||
30 | extern int *gus_osp; | ||
31 | extern int gus_base; | ||
32 | extern spinlock_t gus_lock; | ||
33 | static int volumes[ICS_MIXDEVS]; | ||
34 | static int left_fix[ICS_MIXDEVS] = | ||
35 | {1, 1, 1, 2, 1, 2}; | ||
36 | static int right_fix[ICS_MIXDEVS] = | ||
37 | {2, 2, 2, 1, 2, 1}; | ||
38 | |||
39 | static int scale_vol(int vol) | ||
40 | { | ||
41 | /* | ||
42 | * Experimental volume scaling by Risto Kankkunen. | ||
43 | * This should give smoother volume response than just | ||
44 | * a plain multiplication. | ||
45 | */ | ||
46 | |||
47 | int e; | ||
48 | |||
49 | if (vol < 0) | ||
50 | vol = 0; | ||
51 | if (vol > 100) | ||
52 | vol = 100; | ||
53 | vol = (31 * vol + 50) / 100; | ||
54 | e = 0; | ||
55 | if (vol) | ||
56 | { | ||
57 | while (vol < 16) | ||
58 | { | ||
59 | vol <<= 1; | ||
60 | e--; | ||
61 | } | ||
62 | vol -= 16; | ||
63 | e += 7; | ||
64 | } | ||
65 | return ((e << 4) + vol); | ||
66 | } | ||
67 | |||
68 | static void write_mix(int dev, int chn, int vol) | ||
69 | { | ||
70 | int *selector; | ||
71 | unsigned long flags; | ||
72 | int ctrl_addr = dev << 3; | ||
73 | int attn_addr = dev << 3; | ||
74 | |||
75 | vol = scale_vol(vol); | ||
76 | |||
77 | if (chn == CHN_LEFT) | ||
78 | { | ||
79 | selector = left_fix; | ||
80 | ctrl_addr |= 0x00; | ||
81 | attn_addr |= 0x02; | ||
82 | } | ||
83 | else | ||
84 | { | ||
85 | selector = right_fix; | ||
86 | ctrl_addr |= 0x01; | ||
87 | attn_addr |= 0x03; | ||
88 | } | ||
89 | |||
90 | spin_lock_irqsave(&gus_lock, flags); | ||
91 | outb((ctrl_addr), u_MixSelect); | ||
92 | outb((selector[dev]), u_MixData); | ||
93 | outb((attn_addr), u_MixSelect); | ||
94 | outb(((unsigned char) vol), u_MixData); | ||
95 | spin_unlock_irqrestore(&gus_lock,flags); | ||
96 | } | ||
97 | |||
98 | static int set_volumes(int dev, int vol) | ||
99 | { | ||
100 | int left = vol & 0x00ff; | ||
101 | int right = (vol >> 8) & 0x00ff; | ||
102 | |||
103 | if (left < 0) | ||
104 | left = 0; | ||
105 | if (left > 100) | ||
106 | left = 100; | ||
107 | if (right < 0) | ||
108 | right = 0; | ||
109 | if (right > 100) | ||
110 | right = 100; | ||
111 | |||
112 | write_mix(dev, CHN_LEFT, left); | ||
113 | write_mix(dev, CHN_RIGHT, right); | ||
114 | |||
115 | vol = left + (right << 8); | ||
116 | volumes[dev] = vol; | ||
117 | return vol; | ||
118 | } | ||
119 | |||
120 | static int ics2101_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) | ||
121 | { | ||
122 | int val; | ||
123 | |||
124 | if (((cmd >> 8) & 0xff) == 'M') { | ||
125 | if (_SIOC_DIR(cmd) & _SIOC_WRITE) { | ||
126 | |||
127 | if (get_user(val, (int __user *)arg)) | ||
128 | return -EFAULT; | ||
129 | switch (cmd & 0xff) { | ||
130 | case SOUND_MIXER_RECSRC: | ||
131 | return gus_default_mixer_ioctl(dev, cmd, arg); | ||
132 | |||
133 | case SOUND_MIXER_MIC: | ||
134 | val = set_volumes(DEV_MIC, val); | ||
135 | break; | ||
136 | |||
137 | case SOUND_MIXER_CD: | ||
138 | val = set_volumes(DEV_CD, val); | ||
139 | break; | ||
140 | |||
141 | case SOUND_MIXER_LINE: | ||
142 | val = set_volumes(DEV_LINE, val); | ||
143 | break; | ||
144 | |||
145 | case SOUND_MIXER_SYNTH: | ||
146 | val = set_volumes(DEV_GF1, val); | ||
147 | break; | ||
148 | |||
149 | case SOUND_MIXER_VOLUME: | ||
150 | val = set_volumes(DEV_VOL, val); | ||
151 | break; | ||
152 | |||
153 | default: | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | return put_user(val, (int __user *)arg); | ||
157 | } else { | ||
158 | switch (cmd & 0xff) { | ||
159 | /* | ||
160 | * Return parameters | ||
161 | */ | ||
162 | case SOUND_MIXER_RECSRC: | ||
163 | return gus_default_mixer_ioctl(dev, cmd, arg); | ||
164 | |||
165 | case SOUND_MIXER_DEVMASK: | ||
166 | val = MIX_DEVS; | ||
167 | break; | ||
168 | |||
169 | case SOUND_MIXER_STEREODEVS: | ||
170 | val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; | ||
171 | break; | ||
172 | |||
173 | case SOUND_MIXER_RECMASK: | ||
174 | val = SOUND_MASK_MIC | SOUND_MASK_LINE; | ||
175 | break; | ||
176 | |||
177 | case SOUND_MIXER_CAPS: | ||
178 | val = 0; | ||
179 | break; | ||
180 | |||
181 | case SOUND_MIXER_MIC: | ||
182 | val = volumes[DEV_MIC]; | ||
183 | break; | ||
184 | |||
185 | case SOUND_MIXER_LINE: | ||
186 | val = volumes[DEV_LINE]; | ||
187 | break; | ||
188 | |||
189 | case SOUND_MIXER_CD: | ||
190 | val = volumes[DEV_CD]; | ||
191 | break; | ||
192 | |||
193 | case SOUND_MIXER_VOLUME: | ||
194 | val = volumes[DEV_VOL]; | ||
195 | break; | ||
196 | |||
197 | case SOUND_MIXER_SYNTH: | ||
198 | val = volumes[DEV_GF1]; | ||
199 | break; | ||
200 | |||
201 | default: | ||
202 | return -EINVAL; | ||
203 | } | ||
204 | return put_user(val, (int __user *)arg); | ||
205 | } | ||
206 | } | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | |||
210 | static struct mixer_operations ics2101_mixer_operations = | ||
211 | { | ||
212 | .owner = THIS_MODULE, | ||
213 | .id = "ICS2101", | ||
214 | .name = "ICS2101 Multimedia Mixer", | ||
215 | .ioctl = ics2101_mixer_ioctl | ||
216 | }; | ||
217 | |||
218 | int __init ics2101_mixer_init(void) | ||
219 | { | ||
220 | int i; | ||
221 | int n; | ||
222 | |||
223 | if ((n = sound_alloc_mixerdev()) != -1) | ||
224 | { | ||
225 | mixer_devs[n] = &ics2101_mixer_operations; | ||
226 | |||
227 | /* | ||
228 | * Some GUS v3.7 cards had some channels flipped. Disable | ||
229 | * the flipping feature if the model id is other than 5. | ||
230 | */ | ||
231 | |||
232 | if (inb(u_MixSelect) != 5) | ||
233 | { | ||
234 | for (i = 0; i < ICS_MIXDEVS; i++) | ||
235 | left_fix[i] = 1; | ||
236 | for (i = 0; i < ICS_MIXDEVS; i++) | ||
237 | right_fix[i] = 2; | ||
238 | } | ||
239 | set_volumes(DEV_GF1, 0x5a5a); | ||
240 | set_volumes(DEV_CD, 0x5a5a); | ||
241 | set_volumes(DEV_MIC, 0x0000); | ||
242 | set_volumes(DEV_LINE, 0x5a5a); | ||
243 | set_volumes(DEV_VOL, 0x5a5a); | ||
244 | set_volumes(DEV_UNUSED, 0x0000); | ||
245 | } | ||
246 | return n; | ||
247 | } | ||