diff options
Diffstat (limited to 'sound/oss/sb_mixer.c')
-rw-r--r-- | sound/oss/sb_mixer.c | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c new file mode 100644 index 000000000000..f56898c3981e --- /dev/null +++ b/sound/oss/sb_mixer.c | |||
@@ -0,0 +1,768 @@ | |||
1 | /* | ||
2 | * sound/sb_mixer.c | ||
3 | * | ||
4 | * The low level mixer driver for the Sound Blaster compatible cards. | ||
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 | * Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch] | ||
16 | * Stanislav Voronyi <stas@esc.kharkov.com> : Support for AWE 3DSE device (Jun 7 1999) | ||
17 | */ | ||
18 | |||
19 | #include "sound_config.h" | ||
20 | |||
21 | #define __SB_MIXER_C__ | ||
22 | |||
23 | #include "sb.h" | ||
24 | #include "sb_mixer.h" | ||
25 | |||
26 | #include "sb_ess.h" | ||
27 | |||
28 | #define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) | ||
29 | |||
30 | /* Same as SB Pro, unless I find otherwise */ | ||
31 | #define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES | ||
32 | |||
33 | #define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ | ||
34 | SOUND_MASK_CD | SOUND_MASK_VOLUME) | ||
35 | |||
36 | /* SG NX Pro has treble and bass settings on the mixer. The 'speaker' | ||
37 | * channel is the COVOX/DisneySoundSource emulation volume control | ||
38 | * on the mixer. It does NOT control speaker volume. Should have own | ||
39 | * mask eventually? | ||
40 | */ | ||
41 | #define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ | ||
42 | SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) | ||
43 | |||
44 | #define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ | ||
45 | SOUND_MASK_CD) | ||
46 | |||
47 | #define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ | ||
48 | SOUND_MASK_CD) | ||
49 | |||
50 | #define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ | ||
51 | SOUND_MASK_CD | \ | ||
52 | SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \ | ||
53 | SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \ | ||
54 | SOUND_MASK_IMIX) | ||
55 | |||
56 | /* These are the only devices that are working at the moment. Others could | ||
57 | * be added once they are identified and a method is found to control them. | ||
58 | */ | ||
59 | #define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \ | ||
60 | SOUND_MASK_PCM | SOUND_MASK_MIC | \ | ||
61 | SOUND_MASK_CD | \ | ||
62 | SOUND_MASK_VOLUME) | ||
63 | |||
64 | static mixer_tab sbpro_mix = { | ||
65 | MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), | ||
66 | MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), | ||
67 | MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), | ||
68 | MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), | ||
69 | MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), | ||
70 | MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), | ||
71 | MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), | ||
72 | MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), | ||
73 | MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), | ||
74 | MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), | ||
75 | MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), | ||
76 | MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) | ||
77 | }; | ||
78 | |||
79 | static mixer_tab sb16_mix = { | ||
80 | MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), | ||
81 | MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), | ||
82 | MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), | ||
83 | MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), | ||
84 | MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), | ||
85 | MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), | ||
86 | MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), | ||
87 | MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), | ||
88 | MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), | ||
89 | MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0), | ||
90 | MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), | ||
91 | MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */ | ||
92 | MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2), | ||
93 | MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2) | ||
94 | }; | ||
95 | |||
96 | static mixer_tab als007_mix = | ||
97 | { | ||
98 | MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4), | ||
99 | MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), | ||
100 | MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), | ||
101 | MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4), | ||
102 | MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4), | ||
103 | MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), | ||
104 | MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4), | ||
105 | MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0), | ||
106 | MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4), | ||
107 | MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), | ||
108 | MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), | ||
109 | MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */ | ||
110 | MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), | ||
111 | MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0) | ||
112 | }; | ||
113 | |||
114 | |||
115 | /* SM_GAMES Master volume is lower and PCM & FM volumes | ||
116 | higher than with SB Pro. This improves the | ||
117 | sound quality */ | ||
118 | |||
119 | static int smg_default_levels[32] = | ||
120 | { | ||
121 | 0x2020, /* Master Volume */ | ||
122 | 0x4b4b, /* Bass */ | ||
123 | 0x4b4b, /* Treble */ | ||
124 | 0x6464, /* FM */ | ||
125 | 0x6464, /* PCM */ | ||
126 | 0x4b4b, /* PC Speaker */ | ||
127 | 0x4b4b, /* Ext Line */ | ||
128 | 0x0000, /* Mic */ | ||
129 | 0x4b4b, /* CD */ | ||
130 | 0x4b4b, /* Recording monitor */ | ||
131 | 0x4b4b, /* SB PCM */ | ||
132 | 0x4b4b, /* Recording level */ | ||
133 | 0x4b4b, /* Input gain */ | ||
134 | 0x4b4b, /* Output gain */ | ||
135 | 0x4040, /* Line1 */ | ||
136 | 0x4040, /* Line2 */ | ||
137 | 0x1515 /* Line3 */ | ||
138 | }; | ||
139 | |||
140 | static int sb_default_levels[32] = | ||
141 | { | ||
142 | 0x5a5a, /* Master Volume */ | ||
143 | 0x4b4b, /* Bass */ | ||
144 | 0x4b4b, /* Treble */ | ||
145 | 0x4b4b, /* FM */ | ||
146 | 0x4b4b, /* PCM */ | ||
147 | 0x4b4b, /* PC Speaker */ | ||
148 | 0x4b4b, /* Ext Line */ | ||
149 | 0x1010, /* Mic */ | ||
150 | 0x4b4b, /* CD */ | ||
151 | 0x0000, /* Recording monitor */ | ||
152 | 0x4b4b, /* SB PCM */ | ||
153 | 0x4b4b, /* Recording level */ | ||
154 | 0x4b4b, /* Input gain */ | ||
155 | 0x4b4b, /* Output gain */ | ||
156 | 0x4040, /* Line1 */ | ||
157 | 0x4040, /* Line2 */ | ||
158 | 0x1515 /* Line3 */ | ||
159 | }; | ||
160 | |||
161 | static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = | ||
162 | { | ||
163 | 0x00, /* SOUND_MIXER_VOLUME */ | ||
164 | 0x00, /* SOUND_MIXER_BASS */ | ||
165 | 0x00, /* SOUND_MIXER_TREBLE */ | ||
166 | 0x40, /* SOUND_MIXER_SYNTH */ | ||
167 | 0x00, /* SOUND_MIXER_PCM */ | ||
168 | 0x00, /* SOUND_MIXER_SPEAKER */ | ||
169 | 0x10, /* SOUND_MIXER_LINE */ | ||
170 | 0x01, /* SOUND_MIXER_MIC */ | ||
171 | 0x04, /* SOUND_MIXER_CD */ | ||
172 | 0x00, /* SOUND_MIXER_IMIX */ | ||
173 | 0x00, /* SOUND_MIXER_ALTPCM */ | ||
174 | 0x00, /* SOUND_MIXER_RECLEV */ | ||
175 | 0x00, /* SOUND_MIXER_IGAIN */ | ||
176 | 0x00 /* SOUND_MIXER_OGAIN */ | ||
177 | }; | ||
178 | |||
179 | static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = | ||
180 | { | ||
181 | 0x00, /* SOUND_MIXER_VOLUME */ | ||
182 | 0x00, /* SOUND_MIXER_BASS */ | ||
183 | 0x00, /* SOUND_MIXER_TREBLE */ | ||
184 | 0x20, /* SOUND_MIXER_SYNTH */ | ||
185 | 0x00, /* SOUND_MIXER_PCM */ | ||
186 | 0x00, /* SOUND_MIXER_SPEAKER */ | ||
187 | 0x08, /* SOUND_MIXER_LINE */ | ||
188 | 0x01, /* SOUND_MIXER_MIC */ | ||
189 | 0x02, /* SOUND_MIXER_CD */ | ||
190 | 0x00, /* SOUND_MIXER_IMIX */ | ||
191 | 0x00, /* SOUND_MIXER_ALTPCM */ | ||
192 | 0x00, /* SOUND_MIXER_RECLEV */ | ||
193 | 0x00, /* SOUND_MIXER_IGAIN */ | ||
194 | 0x00 /* SOUND_MIXER_OGAIN */ | ||
195 | }; | ||
196 | |||
197 | static char smw_mix_regs[] = /* Left mixer registers */ | ||
198 | { | ||
199 | 0x0b, /* SOUND_MIXER_VOLUME */ | ||
200 | 0x0d, /* SOUND_MIXER_BASS */ | ||
201 | 0x0d, /* SOUND_MIXER_TREBLE */ | ||
202 | 0x05, /* SOUND_MIXER_SYNTH */ | ||
203 | 0x09, /* SOUND_MIXER_PCM */ | ||
204 | 0x00, /* SOUND_MIXER_SPEAKER */ | ||
205 | 0x03, /* SOUND_MIXER_LINE */ | ||
206 | 0x01, /* SOUND_MIXER_MIC */ | ||
207 | 0x07, /* SOUND_MIXER_CD */ | ||
208 | 0x00, /* SOUND_MIXER_IMIX */ | ||
209 | 0x00, /* SOUND_MIXER_ALTPCM */ | ||
210 | 0x00, /* SOUND_MIXER_RECLEV */ | ||
211 | 0x00, /* SOUND_MIXER_IGAIN */ | ||
212 | 0x00, /* SOUND_MIXER_OGAIN */ | ||
213 | 0x00, /* SOUND_MIXER_LINE1 */ | ||
214 | 0x00, /* SOUND_MIXER_LINE2 */ | ||
215 | 0x00 /* SOUND_MIXER_LINE3 */ | ||
216 | }; | ||
217 | |||
218 | static int sbmixnum = 1; | ||
219 | |||
220 | static void sb_mixer_reset(sb_devc * devc); | ||
221 | |||
222 | void sb_mixer_set_stereo(sb_devc * devc, int mode) | ||
223 | { | ||
224 | sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC)); | ||
225 | } | ||
226 | |||
227 | static int detect_mixer(sb_devc * devc) | ||
228 | { | ||
229 | /* Just trust the mixer is there */ | ||
230 | return 1; | ||
231 | } | ||
232 | |||
233 | static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval) | ||
234 | { | ||
235 | unsigned char mask; | ||
236 | int shift; | ||
237 | |||
238 | mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1; | ||
239 | newval = (int) ((newval * mask) + 50) / 100; /* Scale */ | ||
240 | |||
241 | shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1; | ||
242 | |||
243 | *regval &= ~(mask << shift); /* Mask out previous value */ | ||
244 | *regval |= (newval & mask) << shift; /* Set the new value */ | ||
245 | } | ||
246 | |||
247 | static int sb_mixer_get(sb_devc * devc, int dev) | ||
248 | { | ||
249 | if (!((1 << dev) & devc->supported_devices)) | ||
250 | return -EINVAL; | ||
251 | return devc->levels[dev]; | ||
252 | } | ||
253 | |||
254 | void smw_mixer_init(sb_devc * devc) | ||
255 | { | ||
256 | int i; | ||
257 | |||
258 | sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */ | ||
259 | sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */ | ||
260 | |||
261 | devc->supported_devices = 0; | ||
262 | for (i = 0; i < sizeof(smw_mix_regs); i++) | ||
263 | if (smw_mix_regs[i] != 0) | ||
264 | devc->supported_devices |= (1 << i); | ||
265 | |||
266 | devc->supported_rec_devices = devc->supported_devices & | ||
267 | ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME); | ||
268 | sb_mixer_reset(devc); | ||
269 | } | ||
270 | |||
271 | int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right) | ||
272 | { | ||
273 | int regoffs; | ||
274 | unsigned char val; | ||
275 | |||
276 | regoffs = (*devc->iomap)[dev][LEFT_CHN].regno; | ||
277 | |||
278 | if (regoffs == 0) | ||
279 | return -EINVAL; | ||
280 | |||
281 | if ((dev < 0) || (dev >= devc->iomap_sz)) | ||
282 | return -EINVAL; | ||
283 | |||
284 | val = sb_getmixer(devc, regoffs); | ||
285 | change_bits(devc, &val, dev, LEFT_CHN, left); | ||
286 | |||
287 | if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* | ||
288 | * Change register | ||
289 | */ | ||
290 | { | ||
291 | sb_setmixer(devc, regoffs, val); /* | ||
292 | * Save the old one | ||
293 | */ | ||
294 | regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno; | ||
295 | |||
296 | if (regoffs == 0) | ||
297 | return left | (left << 8); /* | ||
298 | * Just left channel present | ||
299 | */ | ||
300 | |||
301 | val = sb_getmixer(devc, regoffs); /* | ||
302 | * Read the new one | ||
303 | */ | ||
304 | } | ||
305 | change_bits(devc, &val, dev, RIGHT_CHN, right); | ||
306 | |||
307 | sb_setmixer(devc, regoffs, val); | ||
308 | |||
309 | return left | (right << 8); | ||
310 | } | ||
311 | |||
312 | static int smw_mixer_set(sb_devc * devc, int dev, int left, int right) | ||
313 | { | ||
314 | int reg, val; | ||
315 | |||
316 | switch (dev) | ||
317 | { | ||
318 | case SOUND_MIXER_VOLUME: | ||
319 | sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */ | ||
320 | sb_setmixer(devc, 0x0c, 96 - (96 * right / 100)); | ||
321 | break; | ||
322 | |||
323 | case SOUND_MIXER_BASS: | ||
324 | case SOUND_MIXER_TREBLE: | ||
325 | devc->levels[dev] = left | (right << 8); | ||
326 | /* Set left bass and treble values */ | ||
327 | val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4; | ||
328 | val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f; | ||
329 | sb_setmixer(devc, 0x0d, val); | ||
330 | |||
331 | /* Set right bass and treble values */ | ||
332 | val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4; | ||
333 | val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f; | ||
334 | sb_setmixer(devc, 0x0e, val); | ||
335 | |||
336 | break; | ||
337 | |||
338 | default: | ||
339 | /* bounds check */ | ||
340 | if (dev < 0 || dev >= ARRAY_SIZE(smw_mix_regs)) | ||
341 | return -EINVAL; | ||
342 | reg = smw_mix_regs[dev]; | ||
343 | if (reg == 0) | ||
344 | return -EINVAL; | ||
345 | sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */ | ||
346 | sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40); | ||
347 | } | ||
348 | |||
349 | devc->levels[dev] = left | (right << 8); | ||
350 | return left | (right << 8); | ||
351 | } | ||
352 | |||
353 | static int sb_mixer_set(sb_devc * devc, int dev, int value) | ||
354 | { | ||
355 | int left = value & 0x000000ff; | ||
356 | int right = (value & 0x0000ff00) >> 8; | ||
357 | int retval; | ||
358 | |||
359 | if (left > 100) | ||
360 | left = 100; | ||
361 | if (right > 100) | ||
362 | right = 100; | ||
363 | |||
364 | if ((dev < 0) || (dev > 31)) | ||
365 | return -EINVAL; | ||
366 | |||
367 | if (!(devc->supported_devices & (1 << dev))) /* | ||
368 | * Not supported | ||
369 | */ | ||
370 | return -EINVAL; | ||
371 | |||
372 | /* Differentiate depending on the chipsets */ | ||
373 | switch (devc->model) { | ||
374 | case MDL_SMW: | ||
375 | retval = smw_mixer_set(devc, dev, left, right); | ||
376 | break; | ||
377 | case MDL_ESS: | ||
378 | retval = ess_mixer_set(devc, dev, left, right); | ||
379 | break; | ||
380 | default: | ||
381 | retval = sb_common_mixer_set(devc, dev, left, right); | ||
382 | } | ||
383 | if (retval >= 0) devc->levels[dev] = retval; | ||
384 | |||
385 | return retval; | ||
386 | } | ||
387 | |||
388 | /* | ||
389 | * set_recsrc doesn't apply to ES188x | ||
390 | */ | ||
391 | static void set_recsrc(sb_devc * devc, int src) | ||
392 | { | ||
393 | sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7)); | ||
394 | } | ||
395 | |||
396 | static int set_recmask(sb_devc * devc, int mask) | ||
397 | { | ||
398 | int devmask, i; | ||
399 | unsigned char regimageL, regimageR; | ||
400 | |||
401 | devmask = mask & devc->supported_rec_devices; | ||
402 | |||
403 | switch (devc->model) | ||
404 | { | ||
405 | case MDL_SBPRO: | ||
406 | case MDL_ESS: | ||
407 | case MDL_JAZZ: | ||
408 | case MDL_SMW: | ||
409 | if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) { | ||
410 | break; | ||
411 | }; | ||
412 | if (devmask != SOUND_MASK_MIC && | ||
413 | devmask != SOUND_MASK_LINE && | ||
414 | devmask != SOUND_MASK_CD) | ||
415 | { | ||
416 | /* | ||
417 | * More than one device selected. Drop the | ||
418 | * previous selection | ||
419 | */ | ||
420 | devmask &= ~devc->recmask; | ||
421 | } | ||
422 | if (devmask != SOUND_MASK_MIC && | ||
423 | devmask != SOUND_MASK_LINE && | ||
424 | devmask != SOUND_MASK_CD) | ||
425 | { | ||
426 | /* | ||
427 | * More than one device selected. Default to | ||
428 | * mic | ||
429 | */ | ||
430 | devmask = SOUND_MASK_MIC; | ||
431 | } | ||
432 | if (devmask ^ devc->recmask) /* | ||
433 | * Input source changed | ||
434 | */ | ||
435 | { | ||
436 | switch (devmask) | ||
437 | { | ||
438 | case SOUND_MASK_MIC: | ||
439 | set_recsrc(devc, SRC__MIC); | ||
440 | break; | ||
441 | |||
442 | case SOUND_MASK_LINE: | ||
443 | set_recsrc(devc, SRC__LINE); | ||
444 | break; | ||
445 | |||
446 | case SOUND_MASK_CD: | ||
447 | set_recsrc(devc, SRC__CD); | ||
448 | break; | ||
449 | |||
450 | default: | ||
451 | set_recsrc(devc, SRC__MIC); | ||
452 | } | ||
453 | } | ||
454 | break; | ||
455 | |||
456 | case MDL_SB16: | ||
457 | if (!devmask) | ||
458 | devmask = SOUND_MASK_MIC; | ||
459 | |||
460 | if (devc->submodel == SUBMDL_ALS007) | ||
461 | { | ||
462 | switch (devmask) | ||
463 | { | ||
464 | case SOUND_MASK_LINE: | ||
465 | sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE); | ||
466 | break; | ||
467 | case SOUND_MASK_CD: | ||
468 | sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD); | ||
469 | break; | ||
470 | case SOUND_MASK_SYNTH: | ||
471 | sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH); | ||
472 | break; | ||
473 | default: /* Also takes care of SOUND_MASK_MIC case */ | ||
474 | sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC); | ||
475 | break; | ||
476 | } | ||
477 | } | ||
478 | else | ||
479 | { | ||
480 | regimageL = regimageR = 0; | ||
481 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | ||
482 | { | ||
483 | if ((1 << i) & devmask) | ||
484 | { | ||
485 | regimageL |= sb16_recmasks_L[i]; | ||
486 | regimageR |= sb16_recmasks_R[i]; | ||
487 | } | ||
488 | sb_setmixer (devc, SB16_IMASK_L, regimageL); | ||
489 | sb_setmixer (devc, SB16_IMASK_R, regimageR); | ||
490 | } | ||
491 | } | ||
492 | break; | ||
493 | } | ||
494 | devc->recmask = devmask; | ||
495 | return devc->recmask; | ||
496 | } | ||
497 | |||
498 | static int set_outmask(sb_devc * devc, int mask) | ||
499 | { | ||
500 | int devmask, i; | ||
501 | unsigned char regimage; | ||
502 | |||
503 | devmask = mask & devc->supported_out_devices; | ||
504 | |||
505 | switch (devc->model) | ||
506 | { | ||
507 | case MDL_SB16: | ||
508 | if (devc->submodel == SUBMDL_ALS007) | ||
509 | break; | ||
510 | else | ||
511 | { | ||
512 | regimage = 0; | ||
513 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | ||
514 | { | ||
515 | if ((1 << i) & devmask) | ||
516 | { | ||
517 | regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]); | ||
518 | } | ||
519 | sb_setmixer (devc, SB16_OMASK, regimage); | ||
520 | } | ||
521 | } | ||
522 | break; | ||
523 | default: | ||
524 | break; | ||
525 | } | ||
526 | |||
527 | devc->outmask = devmask; | ||
528 | return devc->outmask; | ||
529 | } | ||
530 | |||
531 | static int sb_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) | ||
532 | { | ||
533 | sb_devc *devc = mixer_devs[dev]->devc; | ||
534 | int val, ret; | ||
535 | int __user *p = arg; | ||
536 | |||
537 | /* | ||
538 | * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1). | ||
539 | * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1) | ||
540 | * or mode==2 put 3DSE state to mode. | ||
541 | */ | ||
542 | if (devc->model == MDL_SB16) { | ||
543 | if (cmd == SOUND_MIXER_AGC) | ||
544 | { | ||
545 | if (get_user(val, p)) | ||
546 | return -EFAULT; | ||
547 | sb_setmixer(devc, 0x43, (~val) & 0x01); | ||
548 | return 0; | ||
549 | } | ||
550 | if (cmd == SOUND_MIXER_3DSE) | ||
551 | { | ||
552 | /* I put here 15, but I don't know the exact version. | ||
553 | At least my 4.13 havn't 3DSE, 4.16 has it. */ | ||
554 | if (devc->minor < 15) | ||
555 | return -EINVAL; | ||
556 | if (get_user(val, p)) | ||
557 | return -EFAULT; | ||
558 | if (val == 0 || val == 1) | ||
559 | sb_chgmixer(devc, AWE_3DSE, 0x01, val); | ||
560 | else if (val == 2) | ||
561 | { | ||
562 | ret = sb_getmixer(devc, AWE_3DSE)&0x01; | ||
563 | return put_user(ret, p); | ||
564 | } | ||
565 | else | ||
566 | return -EINVAL; | ||
567 | return 0; | ||
568 | } | ||
569 | } | ||
570 | if (((cmd >> 8) & 0xff) == 'M') | ||
571 | { | ||
572 | if (_SIOC_DIR(cmd) & _SIOC_WRITE) | ||
573 | { | ||
574 | if (get_user(val, p)) | ||
575 | return -EFAULT; | ||
576 | switch (cmd & 0xff) | ||
577 | { | ||
578 | case SOUND_MIXER_RECSRC: | ||
579 | ret = set_recmask(devc, val); | ||
580 | break; | ||
581 | |||
582 | case SOUND_MIXER_OUTSRC: | ||
583 | ret = set_outmask(devc, val); | ||
584 | break; | ||
585 | |||
586 | default: | ||
587 | ret = sb_mixer_set(devc, cmd & 0xff, val); | ||
588 | } | ||
589 | } | ||
590 | else switch (cmd & 0xff) | ||
591 | { | ||
592 | case SOUND_MIXER_RECSRC: | ||
593 | ret = devc->recmask; | ||
594 | break; | ||
595 | |||
596 | case SOUND_MIXER_OUTSRC: | ||
597 | ret = devc->outmask; | ||
598 | break; | ||
599 | |||
600 | case SOUND_MIXER_DEVMASK: | ||
601 | ret = devc->supported_devices; | ||
602 | break; | ||
603 | |||
604 | case SOUND_MIXER_STEREODEVS: | ||
605 | ret = devc->supported_devices; | ||
606 | /* The ESS seems to have stereo mic controls */ | ||
607 | if (devc->model == MDL_ESS) | ||
608 | ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX); | ||
609 | else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW) | ||
610 | ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); | ||
611 | break; | ||
612 | |||
613 | case SOUND_MIXER_RECMASK: | ||
614 | ret = devc->supported_rec_devices; | ||
615 | break; | ||
616 | |||
617 | case SOUND_MIXER_OUTMASK: | ||
618 | ret = devc->supported_out_devices; | ||
619 | break; | ||
620 | |||
621 | case SOUND_MIXER_CAPS: | ||
622 | ret = devc->mixer_caps; | ||
623 | break; | ||
624 | |||
625 | default: | ||
626 | ret = sb_mixer_get(devc, cmd & 0xff); | ||
627 | break; | ||
628 | } | ||
629 | return put_user(ret, p); | ||
630 | } else | ||
631 | return -EINVAL; | ||
632 | } | ||
633 | |||
634 | static struct mixer_operations sb_mixer_operations = | ||
635 | { | ||
636 | .owner = THIS_MODULE, | ||
637 | .id = "SB", | ||
638 | .name = "Sound Blaster", | ||
639 | .ioctl = sb_mixer_ioctl | ||
640 | }; | ||
641 | |||
642 | static struct mixer_operations als007_mixer_operations = | ||
643 | { | ||
644 | .owner = THIS_MODULE, | ||
645 | .id = "ALS007", | ||
646 | .name = "Avance ALS-007", | ||
647 | .ioctl = sb_mixer_ioctl | ||
648 | }; | ||
649 | |||
650 | static void sb_mixer_reset(sb_devc * devc) | ||
651 | { | ||
652 | char name[32]; | ||
653 | int i; | ||
654 | |||
655 | sprintf(name, "SB_%d", devc->sbmixnum); | ||
656 | |||
657 | if (devc->sbmo.sm_games) | ||
658 | devc->levels = load_mixer_volumes(name, smg_default_levels, 1); | ||
659 | else | ||
660 | devc->levels = load_mixer_volumes(name, sb_default_levels, 1); | ||
661 | |||
662 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | ||
663 | sb_mixer_set(devc, i, devc->levels[i]); | ||
664 | |||
665 | if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) { | ||
666 | set_recmask(devc, SOUND_MASK_MIC); | ||
667 | }; | ||
668 | } | ||
669 | |||
670 | int sb_mixer_init(sb_devc * devc, struct module *owner) | ||
671 | { | ||
672 | int mixer_type = 0; | ||
673 | int m; | ||
674 | |||
675 | devc->sbmixnum = sbmixnum++; | ||
676 | devc->levels = NULL; | ||
677 | |||
678 | sb_setmixer(devc, 0x00, 0); /* Reset mixer */ | ||
679 | |||
680 | if (!(mixer_type = detect_mixer(devc))) | ||
681 | return 0; /* No mixer. Why? */ | ||
682 | |||
683 | switch (devc->model) | ||
684 | { | ||
685 | case MDL_ESSPCI: | ||
686 | case MDL_YMPCI: | ||
687 | case MDL_SBPRO: | ||
688 | case MDL_AZTECH: | ||
689 | case MDL_JAZZ: | ||
690 | devc->mixer_caps = SOUND_CAP_EXCL_INPUT; | ||
691 | devc->supported_devices = SBPRO_MIXER_DEVICES; | ||
692 | devc->supported_rec_devices = SBPRO_RECORDING_DEVICES; | ||
693 | devc->iomap = &sbpro_mix; | ||
694 | devc->iomap_sz = ARRAY_SIZE(sbpro_mix); | ||
695 | break; | ||
696 | |||
697 | case MDL_ESS: | ||
698 | ess_mixer_init (devc); | ||
699 | break; | ||
700 | |||
701 | case MDL_SMW: | ||
702 | devc->mixer_caps = SOUND_CAP_EXCL_INPUT; | ||
703 | devc->supported_devices = 0; | ||
704 | devc->supported_rec_devices = 0; | ||
705 | devc->iomap = &sbpro_mix; | ||
706 | devc->iomap_sz = ARRAY_SIZE(sbpro_mix); | ||
707 | smw_mixer_init(devc); | ||
708 | break; | ||
709 | |||
710 | case MDL_SB16: | ||
711 | devc->mixer_caps = 0; | ||
712 | devc->supported_rec_devices = SB16_RECORDING_DEVICES; | ||
713 | devc->supported_out_devices = SB16_OUTFILTER_DEVICES; | ||
714 | if (devc->submodel != SUBMDL_ALS007) | ||
715 | { | ||
716 | devc->supported_devices = SB16_MIXER_DEVICES; | ||
717 | devc->iomap = &sb16_mix; | ||
718 | devc->iomap_sz = ARRAY_SIZE(sb16_mix); | ||
719 | } | ||
720 | else | ||
721 | { | ||
722 | devc->supported_devices = ALS007_MIXER_DEVICES; | ||
723 | devc->iomap = &als007_mix; | ||
724 | devc->iomap_sz = ARRAY_SIZE(als007_mix); | ||
725 | } | ||
726 | break; | ||
727 | |||
728 | default: | ||
729 | printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model); | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | m = sound_alloc_mixerdev(); | ||
734 | if (m == -1) | ||
735 | return 0; | ||
736 | |||
737 | mixer_devs[m] = (struct mixer_operations *)kmalloc(sizeof(struct mixer_operations), GFP_KERNEL); | ||
738 | if (mixer_devs[m] == NULL) | ||
739 | { | ||
740 | printk(KERN_ERR "sb_mixer: Can't allocate memory\n"); | ||
741 | sound_unload_mixerdev(m); | ||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | if (devc->submodel != SUBMDL_ALS007) | ||
746 | memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations)); | ||
747 | else | ||
748 | memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations)); | ||
749 | |||
750 | mixer_devs[m]->devc = devc; | ||
751 | |||
752 | if (owner) | ||
753 | mixer_devs[m]->owner = owner; | ||
754 | |||
755 | devc->my_mixerdev = m; | ||
756 | sb_mixer_reset(devc); | ||
757 | return 1; | ||
758 | } | ||
759 | |||
760 | void sb_mixer_unload(sb_devc *devc) | ||
761 | { | ||
762 | if (devc->my_mixerdev == -1) | ||
763 | return; | ||
764 | |||
765 | kfree(mixer_devs[devc->my_mixerdev]); | ||
766 | sound_unload_mixerdev(devc->my_mixerdev); | ||
767 | sbmixnum--; | ||
768 | } | ||