Linux Audio

Check our new training course

Loading...
  1/*
  2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  3 *  Routines for Sound Blaster mixer control
  4 *
  5 *
  6 *   This program is free software; you can redistribute it and/or modify
  7 *   it under the terms of the GNU General Public License as published by
  8 *   the Free Software Foundation; either version 2 of the License, or
  9 *   (at your option) any later version.
 10 *
 11 *   This program is distributed in the hope that it will be useful,
 12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 *   GNU General Public License for more details.
 15 *
 16 *   You should have received a copy of the GNU General Public License
 17 *   along with this program; if not, write to the Free Software
 18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 19 *
 20 */
 21
 22#include <asm/io.h>
 23#include <linux/delay.h>
 24#include <linux/time.h>
 25#include <sound/core.h>
 26#include <sound/sb.h>
 27#include <sound/control.h>
 28
 29#undef IO_DEBUG
 30
 31void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data)
 32{
 33	outb(reg, SBP(chip, MIXER_ADDR));
 34	udelay(10);
 35	outb(data, SBP(chip, MIXER_DATA));
 36	udelay(10);
 37#ifdef IO_DEBUG
 38	snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n", reg, data);
 39#endif
 40}
 41
 42unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
 43{
 44	unsigned char result;
 45
 46	outb(reg, SBP(chip, MIXER_ADDR));
 47	udelay(10);
 48	result = inb(SBP(chip, MIXER_DATA));
 49	udelay(10);
 50#ifdef IO_DEBUG
 51	snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n", reg, result);
 52#endif
 53	return result;
 54}
 55
 56/*
 57 * Single channel mixer element
 58 */
 59
 60static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 61{
 62	int mask = (kcontrol->private_value >> 24) & 0xff;
 63
 64	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
 65	uinfo->count = 1;
 66	uinfo->value.integer.min = 0;
 67	uinfo->value.integer.max = mask;
 68	return 0;
 69}
 70
 71static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 72{
 73	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
 74	unsigned long flags;
 75	int reg = kcontrol->private_value & 0xff;
 76	int shift = (kcontrol->private_value >> 16) & 0xff;
 77	int mask = (kcontrol->private_value >> 24) & 0xff;
 78	unsigned char val;
 79
 80	spin_lock_irqsave(&sb->mixer_lock, flags);
 81	val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
 82	spin_unlock_irqrestore(&sb->mixer_lock, flags);
 83	ucontrol->value.integer.value[0] = val;
 84	return 0;
 85}
 86
 87static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 88{
 89	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
 90	unsigned long flags;
 91	int reg = kcontrol->private_value & 0xff;
 92	int shift = (kcontrol->private_value >> 16) & 0x07;
 93	int mask = (kcontrol->private_value >> 24) & 0xff;
 94	int change;
 95	unsigned char val, oval;
 96
 97	val = (ucontrol->value.integer.value[0] & mask) << shift;
 98	spin_lock_irqsave(&sb->mixer_lock, flags);
 99	oval = snd_sbmixer_read(sb, reg);
100	val = (oval & ~(mask << shift)) | val;
101	change = val != oval;
102	if (change)
103		snd_sbmixer_write(sb, reg, val);
104	spin_unlock_irqrestore(&sb->mixer_lock, flags);
105	return change;
106}
107
108/*
109 * Double channel mixer element
110 */
111
112static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
113{
114	int mask = (kcontrol->private_value >> 24) & 0xff;
115
116	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
117	uinfo->count = 2;
118	uinfo->value.integer.min = 0;
119	uinfo->value.integer.max = mask;
120	return 0;
121}
122
123static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
124{
125	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
126	unsigned long flags;
127	int left_reg = kcontrol->private_value & 0xff;
128	int right_reg = (kcontrol->private_value >> 8) & 0xff;
129	int left_shift = (kcontrol->private_value >> 16) & 0x07;
130	int right_shift = (kcontrol->private_value >> 19) & 0x07;
131	int mask = (kcontrol->private_value >> 24) & 0xff;
132	unsigned char left, right;
133
134	spin_lock_irqsave(&sb->mixer_lock, flags);
135	left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
136	right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
137	spin_unlock_irqrestore(&sb->mixer_lock, flags);
138	ucontrol->value.integer.value[0] = left;
139	ucontrol->value.integer.value[1] = right;
140	return 0;
141}
142
143static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
144{
145	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
146	unsigned long flags;
147	int left_reg = kcontrol->private_value & 0xff;
148	int right_reg = (kcontrol->private_value >> 8) & 0xff;
149	int left_shift = (kcontrol->private_value >> 16) & 0x07;
150	int right_shift = (kcontrol->private_value >> 19) & 0x07;
151	int mask = (kcontrol->private_value >> 24) & 0xff;
152	int change;
153	unsigned char left, right, oleft, oright;
154
155	left = (ucontrol->value.integer.value[0] & mask) << left_shift;
156	right = (ucontrol->value.integer.value[1] & mask) << right_shift;
157	spin_lock_irqsave(&sb->mixer_lock, flags);
158	if (left_reg == right_reg) {
159		oleft = snd_sbmixer_read(sb, left_reg);
160		left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
161		change = left != oleft;
162		if (change)
163			snd_sbmixer_write(sb, left_reg, left);
164	} else {
165		oleft = snd_sbmixer_read(sb, left_reg);
166		oright = snd_sbmixer_read(sb, right_reg);
167		left = (oleft & ~(mask << left_shift)) | left;
168		right = (oright & ~(mask << right_shift)) | right;
169		change = left != oleft || right != oright;
170		if (change) {
171			snd_sbmixer_write(sb, left_reg, left);
172			snd_sbmixer_write(sb, right_reg, right);
173		}
174	}
175	spin_unlock_irqrestore(&sb->mixer_lock, flags);
176	return change;
177}
178
179/*
180 * DT-019x / ALS-007 capture/input switch
181 */
182
183static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
184{
185	static const char *texts[5] = {
186		"CD", "Mic", "Line", "Synth", "Master"
187	};
188
189	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
190	uinfo->count = 1;
191	uinfo->value.enumerated.items = 5;
192	if (uinfo->value.enumerated.item > 4)
193		uinfo->value.enumerated.item = 4;
194	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
195	return 0;
196}
197
198static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
199{
200	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
201	unsigned long flags;
202	unsigned char oval;
203	
204	spin_lock_irqsave(&sb->mixer_lock, flags);
205	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
206	spin_unlock_irqrestore(&sb->mixer_lock, flags);
207	switch (oval & 0x07) {
208	case SB_DT019X_CAP_CD:
209		ucontrol->value.enumerated.item[0] = 0;
210		break;
211	case SB_DT019X_CAP_MIC:
212		ucontrol->value.enumerated.item[0] = 1;
213		break;
214	case SB_DT019X_CAP_LINE:
215		ucontrol->value.enumerated.item[0] = 2;
216		break;
217	case SB_DT019X_CAP_MAIN:
218		ucontrol->value.enumerated.item[0] = 4;
219		break;
220	/* To record the synth on these cards you must record the main.   */
221	/* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */
222	/* duplicate case labels if left uncommented. */
223	/* case SB_DT019X_CAP_SYNTH:
224	 *	ucontrol->value.enumerated.item[0] = 3;
225	 *	break;
226	 */
227	default:
228		ucontrol->value.enumerated.item[0] = 4;
229		break;
230	}
231	return 0;
232}
233
234static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
235{
236	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
237	unsigned long flags;
238	int change;
239	unsigned char nval, oval;
240	
241	if (ucontrol->value.enumerated.item[0] > 4)
242		return -EINVAL;
243	switch (ucontrol->value.enumerated.item[0]) {
244	case 0:
245		nval = SB_DT019X_CAP_CD;
246		break;
247	case 1:
248		nval = SB_DT019X_CAP_MIC;
249		break;
250	case 2:
251		nval = SB_DT019X_CAP_LINE;
252		break;
253	case 3:
254		nval = SB_DT019X_CAP_SYNTH;
255		break;
256	case 4:
257		nval = SB_DT019X_CAP_MAIN;
258		break;
259	default:
260		nval = SB_DT019X_CAP_MAIN;
261	}
262	spin_lock_irqsave(&sb->mixer_lock, flags);
263	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
264	change = nval != oval;
265	if (change)
266		snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
267	spin_unlock_irqrestore(&sb->mixer_lock, flags);
268	return change;
269}
270
271/*
272 * ALS4000 mono recording control switch
273 */
274
275static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
276					     struct snd_ctl_elem_info *uinfo)
277{
278	static const char *texts[3] = {
279		"L chan only", "R chan only", "L ch/2 + R ch/2"
280	};
281
282	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
283	uinfo->count = 1;
284	uinfo->value.enumerated.items = 3;
285	if (uinfo->value.enumerated.item > 2)
286		uinfo->value.enumerated.item = 2;
287	strcpy(uinfo->value.enumerated.name,
288	       texts[uinfo->value.enumerated.item]);
289	return 0;
290}
291
292static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
293				struct snd_ctl_elem_value *ucontrol)
294{
295	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
296	unsigned long flags;
297	unsigned char oval;
298
299	spin_lock_irqsave(&sb->mixer_lock, flags);
300	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
301	spin_unlock_irqrestore(&sb->mixer_lock, flags);
302	oval >>= 6;
303	if (oval > 2)
304		oval = 2;
305
306	ucontrol->value.enumerated.item[0] = oval;
307	return 0;
308}
309
310static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
311				struct snd_ctl_elem_value *ucontrol)
312{
313	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
314	unsigned long flags;
315	int change;
316	unsigned char nval, oval;
317
318	if (ucontrol->value.enumerated.item[0] > 2)
319		return -EINVAL;
320	spin_lock_irqsave(&sb->mixer_lock, flags);
321	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
322
323	nval = (oval & ~(3 << 6))
324	     | (ucontrol->value.enumerated.item[0] << 6);
325	change = nval != oval;
326	if (change)
327		snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);
328	spin_unlock_irqrestore(&sb->mixer_lock, flags);
329	return change;
330}
331
332/*
333 * SBPRO input multiplexer
334 */
335
336static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
337{
338	static const char *texts[3] = {
339		"Mic", "CD", "Line"
340	};
341
342	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
343	uinfo->count = 1;
344	uinfo->value.enumerated.items = 3;
345	if (uinfo->value.enumerated.item > 2)
346		uinfo->value.enumerated.item = 2;
347	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
348	return 0;
349}
350
351
352static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
353{
354	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
355	unsigned long flags;
356	unsigned char oval;
357	
358	spin_lock_irqsave(&sb->mixer_lock, flags);
359	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
360	spin_unlock_irqrestore(&sb->mixer_lock, flags);
361	switch ((oval >> 0x01) & 0x03) {
362	case SB_DSP_MIXS_CD:
363		ucontrol->value.enumerated.item[0] = 1;
364		break;
365	case SB_DSP_MIXS_LINE:
366		ucontrol->value.enumerated.item[0] = 2;
367		break;
368	default:
369		ucontrol->value.enumerated.item[0] = 0;
370		break;
371	}
372	return 0;
373}
374
375static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
376{
377	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
378	unsigned long flags;
379	int change;
380	unsigned char nval, oval;
381	
382	if (ucontrol->value.enumerated.item[0] > 2)
383		return -EINVAL;
384	switch (ucontrol->value.enumerated.item[0]) {
385	case 1:
386		nval = SB_DSP_MIXS_CD;
387		break;
388	case 2:
389		nval = SB_DSP_MIXS_LINE;
390		break;
391	default:
392		nval = SB_DSP_MIXS_MIC;
393	}
394	nval <<= 1;
395	spin_lock_irqsave(&sb->mixer_lock, flags);
396	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
397	nval |= oval & ~0x06;
398	change = nval != oval;
399	if (change)
400		snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
401	spin_unlock_irqrestore(&sb->mixer_lock, flags);
402	return change;
403}
404
405/*
406 * SB16 input switch
407 */
408
409static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
410{
411	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
412	uinfo->count = 4;
413	uinfo->value.integer.min = 0;
414	uinfo->value.integer.max = 1;
415	return 0;
416}
417
418static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
419{
420	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
421	unsigned long flags;
422	int reg1 = kcontrol->private_value & 0xff;
423	int reg2 = (kcontrol->private_value >> 8) & 0xff;
424	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
425	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
426	unsigned char val1, val2;
427
428	spin_lock_irqsave(&sb->mixer_lock, flags);
429	val1 = snd_sbmixer_read(sb, reg1);
430	val2 = snd_sbmixer_read(sb, reg2);
431	spin_unlock_irqrestore(&sb->mixer_lock, flags);
432	ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
433	ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
434	ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
435	ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
436	return 0;
437}                                                                                                                   
438
439static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
440{
441	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
442	unsigned long flags;
443	int reg1 = kcontrol->private_value & 0xff;
444	int reg2 = (kcontrol->private_value >> 8) & 0xff;
445	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
446	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
447	int change;
448	unsigned char val1, val2, oval1, oval2;
449
450	spin_lock_irqsave(&sb->mixer_lock, flags);
451	oval1 = snd_sbmixer_read(sb, reg1);
452	oval2 = snd_sbmixer_read(sb, reg2);
453	val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
454	val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
455	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
456	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
457	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
458	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
459	change = val1 != oval1 || val2 != oval2;
460	if (change) {
461		snd_sbmixer_write(sb, reg1, val1);
462		snd_sbmixer_write(sb, reg2, val2);
463	}
464	spin_unlock_irqrestore(&sb->mixer_lock, flags);
465	return change;
466}
467
468
469/*
470 */
471/*
472 */
473int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
474{
475	static struct snd_kcontrol_new newctls[] = {
476		[SB_MIX_SINGLE] = {
477			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
478			.info = snd_sbmixer_info_single,
479			.get = snd_sbmixer_get_single,
480			.put = snd_sbmixer_put_single,
481		},
482		[SB_MIX_DOUBLE] = {
483			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
484			.info = snd_sbmixer_info_double,
485			.get = snd_sbmixer_get_double,
486			.put = snd_sbmixer_put_double,
487		},
488		[SB_MIX_INPUT_SW] = {
489			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
490			.info = snd_sb16mixer_info_input_sw,
491			.get = snd_sb16mixer_get_input_sw,
492			.put = snd_sb16mixer_put_input_sw,
493		},
494		[SB_MIX_CAPTURE_PRO] = {
495			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
496			.info = snd_sb8mixer_info_mux,
497			.get = snd_sb8mixer_get_mux,
498			.put = snd_sb8mixer_put_mux,
499		},
500		[SB_MIX_CAPTURE_DT019X] = {
501			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
502			.info = snd_dt019x_input_sw_info,
503			.get = snd_dt019x_input_sw_get,
504			.put = snd_dt019x_input_sw_put,
505		},
506		[SB_MIX_MONO_CAPTURE_ALS4K] = {
507			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
508			.info = snd_als4k_mono_capture_route_info,
509			.get = snd_als4k_mono_capture_route_get,
510			.put = snd_als4k_mono_capture_route_put,
511		},
512	};
513	struct snd_kcontrol *ctl;
514	int err;
515
516	ctl = snd_ctl_new1(&newctls[type], chip);
517	if (! ctl)
518		return -ENOMEM;
519	strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
520	ctl->id.index = index;
521	ctl->private_value = value;
522	if ((err = snd_ctl_add(chip->card, ctl)) < 0)
523		return err;
524	return 0;
525}
526
527/*
528 * SB 2.0 specific mixer elements
529 */
530
531static struct sbmix_elem snd_sb20_controls[] = {
532	SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
533	SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
534	SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
535	SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
536};
537
538static unsigned char snd_sb20_init_values[][2] = {
539	{ SB_DSP20_MASTER_DEV, 0 },
540	{ SB_DSP20_FM_DEV, 0 },
541};
542
543/*
544 * SB Pro specific mixer elements
545 */
546static struct sbmix_elem snd_sbpro_controls[] = {
547	SB_DOUBLE("Master Playback Volume",
548		  SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
549	SB_DOUBLE("PCM Playback Volume",
550		  SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),
551	SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),
552	SB_DOUBLE("Synth Playback Volume",
553		  SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),
554	SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),
555	SB_DOUBLE("Line Playback Volume",
556		  SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
557	SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
558	{
559		.name = "Capture Source",
560		.type = SB_MIX_CAPTURE_PRO
561	},
562	SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
563	SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
564};
565
566static unsigned char snd_sbpro_init_values[][2] = {
567	{ SB_DSP_MASTER_DEV, 0 },
568	{ SB_DSP_PCM_DEV, 0 },
569	{ SB_DSP_FM_DEV, 0 },
570};
571
572/*
573 * SB16 specific mixer elements
574 */
575static struct sbmix_elem snd_sb16_controls[] = {
576	SB_DOUBLE("Master Playback Volume",
577		  SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
578	SB_DOUBLE("PCM Playback Volume",
579		  SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),
580	SB16_INPUT_SW("Synth Capture Route",
581		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),
582	SB_DOUBLE("Synth Playback Volume",
583		  SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),
584	SB16_INPUT_SW("CD Capture Route",
585		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),
586	SB_DOUBLE("CD Playback Switch",
587		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
588	SB_DOUBLE("CD Playback Volume",
589		  SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),
590	SB16_INPUT_SW("Mic Capture Route",
591		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0),
592	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
593	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
594	SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
595	SB_DOUBLE("Capture Volume",
596		  SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
597	SB_DOUBLE("Playback Volume",
598		  SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
599	SB16_INPUT_SW("Line Capture Route",
600		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),
601	SB_DOUBLE("Line Playback Switch",
602		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
603	SB_DOUBLE("Line Playback Volume",
604		  SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
605	SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
606	SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),
607	SB_DOUBLE("Tone Control - Bass",
608		  SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
609	SB_DOUBLE("Tone Control - Treble",
610		  SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
611};
612
613static unsigned char snd_sb16_init_values[][2] = {
614	{ SB_DSP4_MASTER_DEV + 0, 0 },
615	{ SB_DSP4_MASTER_DEV + 1, 0 },
616	{ SB_DSP4_PCM_DEV + 0, 0 },
617	{ SB_DSP4_PCM_DEV + 1, 0 },
618	{ SB_DSP4_SYNTH_DEV + 0, 0 },
619	{ SB_DSP4_SYNTH_DEV + 1, 0 },
620	{ SB_DSP4_INPUT_LEFT, 0 },
621	{ SB_DSP4_INPUT_RIGHT, 0 },
622	{ SB_DSP4_OUTPUT_SW, 0 },
623	{ SB_DSP4_SPEAKER_DEV, 0 },
624};
625
626/*
627 * DT019x specific mixer elements
628 */
629static struct sbmix_elem snd_dt019x_controls[] = {
630	/* ALS4000 below has some parts which we might be lacking,
631	 * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
632	SB_DOUBLE("Master Playback Volume",
633		  SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15),
634	SB_DOUBLE("PCM Playback Switch",
635		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
636	SB_DOUBLE("PCM Playback Volume",
637		  SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15),
638	SB_DOUBLE("Synth Playback Switch",
639		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
640	SB_DOUBLE("Synth Playback Volume",
641		  SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15),
642	SB_DOUBLE("CD Playback Switch",
643		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
644	SB_DOUBLE("CD Playback Volume",
645		  SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15),
646	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
647	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7),
648	SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7),
649	SB_DOUBLE("Line Playback Switch",
650		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
651	SB_DOUBLE("Line Playback Volume",
652		  SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15),
653	{
654		.name = "Capture Source",
655		.type = SB_MIX_CAPTURE_DT019X
656	}
657};
658
659static unsigned char snd_dt019x_init_values[][2] = {
660        { SB_DT019X_MASTER_DEV, 0 },
661        { SB_DT019X_PCM_DEV, 0 },
662        { SB_DT019X_SYNTH_DEV, 0 },
663        { SB_DT019X_CD_DEV, 0 },
664        { SB_DT019X_MIC_DEV, 0 },	/* Includes PC-speaker in high nibble */
665        { SB_DT019X_LINE_DEV, 0 },
666        { SB_DSP4_OUTPUT_SW, 0 },
667        { SB_DT019X_OUTPUT_SW2, 0 },
668        { SB_DT019X_CAPTURE_SW, 0x06 },
669};
670
671/*
672 * ALS4000 specific mixer elements
673 */
674static struct sbmix_elem snd_als4000_controls[] = {
675	SB_DOUBLE("PCM Playback Switch",
676		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
677	SB_DOUBLE("Synth Playback Switch",
678		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
679	SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03),
680	SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1),
681	{
682		.name = "Master Mono Capture Route",
683		.type = SB_MIX_MONO_CAPTURE_ALS4K
684	},
685	SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1),
686	SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01),
687	SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01),
688	SB_SINGLE("Digital Loopback Switch",
689		  SB_ALS4000_CR3_CONFIGURATION, 7, 0x01),
690	/* FIXME: functionality of 3D controls might be swapped, I didn't find
691	 * a description of how to identify what is supposed to be what */
692	SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07),
693	/* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
694	SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03),
695	/* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
696	 * but what ALSA 3D attribute is that actually? "Center", "Depth",
697	 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
698	SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f),
699	SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01),
700	SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",
701		  SB_ALS4000_FMDAC, 5, 0x01),
702#ifdef NOT_AVAILABLE
703	SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01),
704	SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f),
705#endif
706};
707
708static unsigned char snd_als4000_init_values[][2] = {
709	{ SB_DSP4_MASTER_DEV + 0, 0 },
710	{ SB_DSP4_MASTER_DEV + 1, 0 },
711	{ SB_DSP4_PCM_DEV + 0, 0 },
712	{ SB_DSP4_PCM_DEV + 1, 0 },
713	{ SB_DSP4_SYNTH_DEV + 0, 0 },
714	{ SB_DSP4_SYNTH_DEV + 1, 0 },
715	{ SB_DSP4_SPEAKER_DEV, 0 },
716	{ SB_DSP4_OUTPUT_SW, 0 },
717	{ SB_DSP4_INPUT_LEFT, 0 },
718	{ SB_DSP4_INPUT_RIGHT, 0 },
719	{ SB_DT019X_OUTPUT_SW2, 0 },
720	{ SB_ALS4000_MIC_IN_GAIN, 0 },
721};
722
723/*
724 */
725static int snd_sbmixer_init(struct snd_sb *chip,
726			    struct sbmix_elem *controls,
727			    int controls_count,
728			    unsigned char map[][2],
729			    int map_count,
730			    char *name)
731{
732	unsigned long flags;
733	struct snd_card *card = chip->card;
734	int idx, err;
735
736	/* mixer reset */
737	spin_lock_irqsave(&chip->mixer_lock, flags);
738	snd_sbmixer_write(chip, 0x00, 0x00);
739	spin_unlock_irqrestore(&chip->mixer_lock, flags);
740
741	/* mute and zero volume channels */
742	for (idx = 0; idx < map_count; idx++) {
743		spin_lock_irqsave(&chip->mixer_lock, flags);
744		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
745		spin_unlock_irqrestore(&chip->mixer_lock, flags);
746	}
747
748	for (idx = 0; idx < controls_count; idx++) {
749		err = snd_sbmixer_add_ctl_elem(chip, &controls[idx]);
750		if (err < 0)
751			return err;
752	}
753	snd_component_add(card, name);
754	strcpy(card->mixername, name);
755	return 0;
756}
757
758int snd_sbmixer_new(struct snd_sb *chip)
759{
760	struct snd_card *card;
761	int err;
762
763	if (snd_BUG_ON(!chip || !chip->card))
764		return -EINVAL;
765
766	card = chip->card;
767
768	switch (chip->hardware) {
769	case SB_HW_10:
770		return 0; /* no mixer chip on SB1.x */
771	case SB_HW_20:
772	case SB_HW_201:
773		if ((err = snd_sbmixer_init(chip,
774					    snd_sb20_controls,
775					    ARRAY_SIZE(snd_sb20_controls),
776					    snd_sb20_init_values,
777					    ARRAY_SIZE(snd_sb20_init_values),
778					    "CTL1335")) < 0)
779			return err;
780		break;
781	case SB_HW_PRO:
782	case SB_HW_JAZZ16:
783		if ((err = snd_sbmixer_init(chip,
784					    snd_sbpro_controls,
785					    ARRAY_SIZE(snd_sbpro_controls),
786					    snd_sbpro_init_values,
787					    ARRAY_SIZE(snd_sbpro_init_values),
788					    "CTL1345")) < 0)
789			return err;
790		break;
791	case SB_HW_16:
792	case SB_HW_ALS100:
793	case SB_HW_CS5530:
794		if ((err = snd_sbmixer_init(chip,
795					    snd_sb16_controls,
796					    ARRAY_SIZE(snd_sb16_controls),
797					    snd_sb16_init_values,
798					    ARRAY_SIZE(snd_sb16_init_values),
799					    "CTL1745")) < 0)
800			return err;
801		break;
802	case SB_HW_ALS4000:
803		/* use only the first 16 controls from SB16 */
804		err = snd_sbmixer_init(chip,
805					snd_sb16_controls,
806					16,
807					snd_sb16_init_values,
808					ARRAY_SIZE(snd_sb16_init_values),
809					"ALS4000");
810		if (err < 0)
811			return err;
812		if ((err = snd_sbmixer_init(chip,
813					    snd_als4000_controls,
814					    ARRAY_SIZE(snd_als4000_controls),
815					    snd_als4000_init_values,
816					    ARRAY_SIZE(snd_als4000_init_values),
817					    "ALS4000")) < 0)
818			return err;
819		break;
820	case SB_HW_DT019X:
821		if ((err = snd_sbmixer_init(chip,
822					    snd_dt019x_controls,
823					    ARRAY_SIZE(snd_dt019x_controls),
824					    snd_dt019x_init_values,
825					    ARRAY_SIZE(snd_dt019x_init_values),
826					    "DT019X")) < 0)
827		break;
828	default:
829		strcpy(card->mixername, "???");
830	}
831	return 0;
832}
833
834#ifdef CONFIG_PM
835static unsigned char sb20_saved_regs[] = {
836	SB_DSP20_MASTER_DEV,
837	SB_DSP20_PCM_DEV,
838	SB_DSP20_FM_DEV,
839	SB_DSP20_CD_DEV,
840};
841
842static unsigned char sbpro_saved_regs[] = {
843	SB_DSP_MASTER_DEV,
844	SB_DSP_PCM_DEV,
845	SB_DSP_PLAYBACK_FILT,
846	SB_DSP_FM_DEV,
847	SB_DSP_CD_DEV,
848	SB_DSP_LINE_DEV,
849	SB_DSP_MIC_DEV,
850	SB_DSP_CAPTURE_SOURCE,
851	SB_DSP_CAPTURE_FILT,
852};
853
854static unsigned char sb16_saved_regs[] = {
855	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
856	SB_DSP4_3DSE,
857	SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
858	SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,
859	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
860	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
861	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
862	SB_DSP4_OUTPUT_SW,
863	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
864	SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
865	SB_DSP4_MIC_DEV,
866	SB_DSP4_SPEAKER_DEV,
867	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
868	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
869	SB_DSP4_MIC_AGC
870};
871
872static unsigned char dt019x_saved_regs[] = {
873	SB_DT019X_MASTER_DEV,
874	SB_DT019X_PCM_DEV,
875	SB_DT019X_SYNTH_DEV,
876	SB_DT019X_CD_DEV,
877	SB_DT019X_MIC_DEV,
878	SB_DT019X_SPKR_DEV,
879	SB_DT019X_LINE_DEV,
880	SB_DSP4_OUTPUT_SW,
881	SB_DT019X_OUTPUT_SW2,
882	SB_DT019X_CAPTURE_SW,
883};
884
885static unsigned char als4000_saved_regs[] = {
886	/* please verify in dsheet whether regs to be added
887	   are actually real H/W or just dummy */
888	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
889	SB_DSP4_OUTPUT_SW,
890	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
891	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
892	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
893	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
894	SB_DSP4_MIC_DEV,
895	SB_DSP4_SPEAKER_DEV,
896	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
897	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
898	SB_DT019X_OUTPUT_SW2,
899	SB_ALS4000_MONO_IO_CTRL,
900	SB_ALS4000_MIC_IN_GAIN,
901	SB_ALS4000_FMDAC,
902	SB_ALS4000_3D_SND_FX,
903	SB_ALS4000_3D_TIME_DELAY,
904	SB_ALS4000_CR3_CONFIGURATION,
905};
906
907static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
908{
909	unsigned char *val = chip->saved_regs;
910	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
911		return;
912	for (; num_regs; num_regs--)
913		*val++ = snd_sbmixer_read(chip, *regs++);
914}
915
916static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
917{
918	unsigned char *val = chip->saved_regs;
919	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
920		return;
921	for (; num_regs; num_regs--)
922		snd_sbmixer_write(chip, *regs++, *val++);
923}
924
925void snd_sbmixer_suspend(struct snd_sb *chip)
926{
927	switch (chip->hardware) {
928	case SB_HW_20:
929	case SB_HW_201:
930		save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
931		break;
932	case SB_HW_PRO:
933	case SB_HW_JAZZ16:
934		save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
935		break;
936	case SB_HW_16:
937	case SB_HW_ALS100:
938	case SB_HW_CS5530:
939		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
940		break;
941	case SB_HW_ALS4000:
942		save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
943		break;
944	case SB_HW_DT019X:
945		save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
946		break;
947	default:
948		break;
949	}
950}
951
952void snd_sbmixer_resume(struct snd_sb *chip)
953{
954	switch (chip->hardware) {
955	case SB_HW_20:
956	case SB_HW_201:
957		restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
958		break;
959	case SB_HW_PRO:
960	case SB_HW_JAZZ16:
961		restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
962		break;
963	case SB_HW_16:
964	case SB_HW_ALS100:
965	case SB_HW_CS5530:
966		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
967		break;
968	case SB_HW_ALS4000:
969		restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
970		break;
971	case SB_HW_DT019X:
972		restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
973		break;
974	default:
975		break;
976	}
977}
978#endif