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