]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
f30c2269 | 2 | * sound/oss/ics2101.c |
1da177e4 LT |
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 | } |