xref: /openbmc/linux/sound/isa/sb/sb_mixer.c (revision 6b9e1288)
11da177e4SLinus Torvalds /*
2c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
31da177e4SLinus Torvalds  *  Routines for Sound Blaster mixer control
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
71da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
81da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
91da177e4SLinus Torvalds  *   (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
121da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
131da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141da177e4SLinus Torvalds  *   GNU General Public License for more details.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
171da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
181da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include <asm/io.h>
231da177e4SLinus Torvalds #include <linux/delay.h>
241da177e4SLinus Torvalds #include <linux/time.h>
251da177e4SLinus Torvalds #include <sound/core.h>
261da177e4SLinus Torvalds #include <sound/sb.h>
271da177e4SLinus Torvalds #include <sound/control.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #undef IO_DEBUG
301da177e4SLinus Torvalds 
31029d64b0STakashi Iwai void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data)
321da177e4SLinus Torvalds {
331da177e4SLinus Torvalds 	outb(reg, SBP(chip, MIXER_ADDR));
341da177e4SLinus Torvalds 	udelay(10);
351da177e4SLinus Torvalds 	outb(data, SBP(chip, MIXER_DATA));
361da177e4SLinus Torvalds 	udelay(10);
371da177e4SLinus Torvalds #ifdef IO_DEBUG
3899b359baSTakashi Iwai 	snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n", reg, data);
391da177e4SLinus Torvalds #endif
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds 
42029d64b0STakashi Iwai unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
431da177e4SLinus Torvalds {
441da177e4SLinus Torvalds 	unsigned char result;
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	outb(reg, SBP(chip, MIXER_ADDR));
471da177e4SLinus Torvalds 	udelay(10);
481da177e4SLinus Torvalds 	result = inb(SBP(chip, MIXER_DATA));
491da177e4SLinus Torvalds 	udelay(10);
501da177e4SLinus Torvalds #ifdef IO_DEBUG
5199b359baSTakashi Iwai 	snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n", reg, result);
521da177e4SLinus Torvalds #endif
531da177e4SLinus Torvalds 	return result;
541da177e4SLinus Torvalds }
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /*
571da177e4SLinus Torvalds  * Single channel mixer element
581da177e4SLinus Torvalds  */
591da177e4SLinus Torvalds 
60029d64b0STakashi Iwai static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
611da177e4SLinus Torvalds {
621da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
651da177e4SLinus Torvalds 	uinfo->count = 1;
661da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
671da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
681da177e4SLinus Torvalds 	return 0;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
71029d64b0STakashi Iwai static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
721da177e4SLinus Torvalds {
73029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
741da177e4SLinus Torvalds 	unsigned long flags;
751da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
761da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 16) & 0xff;
771da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
781da177e4SLinus Torvalds 	unsigned char val;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
811da177e4SLinus Torvalds 	val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
821da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
831da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
841da177e4SLinus Torvalds 	return 0;
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds 
87029d64b0STakashi Iwai static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
881da177e4SLinus Torvalds {
89029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
901da177e4SLinus Torvalds 	unsigned long flags;
911da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
921da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 16) & 0x07;
931da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
941da177e4SLinus Torvalds 	int change;
951da177e4SLinus Torvalds 	unsigned char val, oval;
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	val = (ucontrol->value.integer.value[0] & mask) << shift;
981da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
991da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, reg);
1001da177e4SLinus Torvalds 	val = (oval & ~(mask << shift)) | val;
1011da177e4SLinus Torvalds 	change = val != oval;
1021da177e4SLinus Torvalds 	if (change)
1031da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg, val);
1041da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1051da177e4SLinus Torvalds 	return change;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds /*
1091da177e4SLinus Torvalds  * Double channel mixer element
1101da177e4SLinus Torvalds  */
1111da177e4SLinus Torvalds 
112029d64b0STakashi Iwai static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1171da177e4SLinus Torvalds 	uinfo->count = 2;
1181da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
1191da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
1201da177e4SLinus Torvalds 	return 0;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
123029d64b0STakashi Iwai static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1241da177e4SLinus Torvalds {
125029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1261da177e4SLinus Torvalds 	unsigned long flags;
1271da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
1281da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
1291da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x07;
1301da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 19) & 0x07;
1311da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1321da177e4SLinus Torvalds 	unsigned char left, right;
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1351da177e4SLinus Torvalds 	left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
1361da177e4SLinus Torvalds 	right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
1371da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1381da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = left;
1391da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = right;
1401da177e4SLinus Torvalds 	return 0;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143029d64b0STakashi Iwai static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1441da177e4SLinus Torvalds {
145029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1461da177e4SLinus Torvalds 	unsigned long flags;
1471da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
1481da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
1491da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x07;
1501da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 19) & 0x07;
1511da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1521da177e4SLinus Torvalds 	int change;
1531da177e4SLinus Torvalds 	unsigned char left, right, oleft, oright;
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	left = (ucontrol->value.integer.value[0] & mask) << left_shift;
1561da177e4SLinus Torvalds 	right = (ucontrol->value.integer.value[1] & mask) << right_shift;
1571da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1581da177e4SLinus Torvalds 	if (left_reg == right_reg) {
1591da177e4SLinus Torvalds 		oleft = snd_sbmixer_read(sb, left_reg);
1601da177e4SLinus Torvalds 		left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
1611da177e4SLinus Torvalds 		change = left != oleft;
1621da177e4SLinus Torvalds 		if (change)
1631da177e4SLinus Torvalds 			snd_sbmixer_write(sb, left_reg, left);
1641da177e4SLinus Torvalds 	} else {
1651da177e4SLinus Torvalds 		oleft = snd_sbmixer_read(sb, left_reg);
1661da177e4SLinus Torvalds 		oright = snd_sbmixer_read(sb, right_reg);
1671da177e4SLinus Torvalds 		left = (oleft & ~(mask << left_shift)) | left;
1681da177e4SLinus Torvalds 		right = (oright & ~(mask << right_shift)) | right;
1691da177e4SLinus Torvalds 		change = left != oleft || right != oright;
1701da177e4SLinus Torvalds 		if (change) {
1711da177e4SLinus Torvalds 			snd_sbmixer_write(sb, left_reg, left);
1721da177e4SLinus Torvalds 			snd_sbmixer_write(sb, right_reg, right);
1731da177e4SLinus Torvalds 		}
1741da177e4SLinus Torvalds 	}
1751da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1761da177e4SLinus Torvalds 	return change;
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds /*
1801da177e4SLinus Torvalds  * DT-019x / ALS-007 capture/input switch
1811da177e4SLinus Torvalds  */
1821da177e4SLinus Torvalds 
183029d64b0STakashi Iwai static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1841da177e4SLinus Torvalds {
1856b9e1288STakashi Iwai 	static const char * const texts[5] = {
1861da177e4SLinus Torvalds 		"CD", "Mic", "Line", "Synth", "Master"
1871da177e4SLinus Torvalds 	};
1881da177e4SLinus Torvalds 
1896b9e1288STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 5, texts);
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
192029d64b0STakashi Iwai static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1931da177e4SLinus Torvalds {
194029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1951da177e4SLinus Torvalds 	unsigned long flags;
1961da177e4SLinus Torvalds 	unsigned char oval;
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1991da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
2001da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
2011da177e4SLinus Torvalds 	switch (oval & 0x07) {
2021da177e4SLinus Torvalds 	case SB_DT019X_CAP_CD:
2031da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 0;
2041da177e4SLinus Torvalds 		break;
2051da177e4SLinus Torvalds 	case SB_DT019X_CAP_MIC:
2061da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
2071da177e4SLinus Torvalds 		break;
2081da177e4SLinus Torvalds 	case SB_DT019X_CAP_LINE:
2091da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 2;
2101da177e4SLinus Torvalds 		break;
2111da177e4SLinus Torvalds 	case SB_DT019X_CAP_MAIN:
2121da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 4;
2131da177e4SLinus Torvalds 		break;
2141da177e4SLinus Torvalds 	/* To record the synth on these cards you must record the main.   */
2151da177e4SLinus Torvalds 	/* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */
2161da177e4SLinus Torvalds 	/* duplicate case labels if left uncommented. */
2171da177e4SLinus Torvalds 	/* case SB_DT019X_CAP_SYNTH:
2181da177e4SLinus Torvalds 	 *	ucontrol->value.enumerated.item[0] = 3;
2191da177e4SLinus Torvalds 	 *	break;
2201da177e4SLinus Torvalds 	 */
2211da177e4SLinus Torvalds 	default:
2221da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 4;
2231da177e4SLinus Torvalds 		break;
2241da177e4SLinus Torvalds 	}
2251da177e4SLinus Torvalds 	return 0;
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
228029d64b0STakashi Iwai static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2291da177e4SLinus Torvalds {
230029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
2311da177e4SLinus Torvalds 	unsigned long flags;
2321da177e4SLinus Torvalds 	int change;
2331da177e4SLinus Torvalds 	unsigned char nval, oval;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	if (ucontrol->value.enumerated.item[0] > 4)
2361da177e4SLinus Torvalds 		return -EINVAL;
2371da177e4SLinus Torvalds 	switch (ucontrol->value.enumerated.item[0]) {
2381da177e4SLinus Torvalds 	case 0:
2391da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_CD;
2401da177e4SLinus Torvalds 		break;
2411da177e4SLinus Torvalds 	case 1:
2421da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MIC;
2431da177e4SLinus Torvalds 		break;
2441da177e4SLinus Torvalds 	case 2:
2451da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_LINE;
2461da177e4SLinus Torvalds 		break;
2471da177e4SLinus Torvalds 	case 3:
2481da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_SYNTH;
2491da177e4SLinus Torvalds 		break;
2501da177e4SLinus Torvalds 	case 4:
2511da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MAIN;
2521da177e4SLinus Torvalds 		break;
2531da177e4SLinus Torvalds 	default:
2541da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MAIN;
2551da177e4SLinus Torvalds 	}
2561da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
2571da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
2581da177e4SLinus Torvalds 	change = nval != oval;
2591da177e4SLinus Torvalds 	if (change)
2601da177e4SLinus Torvalds 		snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
2611da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
2621da177e4SLinus Torvalds 	return change;
2631da177e4SLinus Torvalds }
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds /*
266ce71bfd1SAndreas Mohr  * ALS4000 mono recording control switch
267ce71bfd1SAndreas Mohr  */
268ce71bfd1SAndreas Mohr 
269ce71bfd1SAndreas Mohr static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
270ce71bfd1SAndreas Mohr 					     struct snd_ctl_elem_info *uinfo)
271ce71bfd1SAndreas Mohr {
2726b9e1288STakashi Iwai 	static const char * const texts[3] = {
273ce71bfd1SAndreas Mohr 		"L chan only", "R chan only", "L ch/2 + R ch/2"
274ce71bfd1SAndreas Mohr 	};
275ce71bfd1SAndreas Mohr 
2766b9e1288STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 3, texts);
277ce71bfd1SAndreas Mohr }
278ce71bfd1SAndreas Mohr 
279ce71bfd1SAndreas Mohr static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
280ce71bfd1SAndreas Mohr 				struct snd_ctl_elem_value *ucontrol)
281ce71bfd1SAndreas Mohr {
282ce71bfd1SAndreas Mohr 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
283ce71bfd1SAndreas Mohr 	unsigned long flags;
284ce71bfd1SAndreas Mohr 	unsigned char oval;
285ce71bfd1SAndreas Mohr 
286ce71bfd1SAndreas Mohr 	spin_lock_irqsave(&sb->mixer_lock, flags);
287ce71bfd1SAndreas Mohr 	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
288ce71bfd1SAndreas Mohr 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
289ce71bfd1SAndreas Mohr 	oval >>= 6;
290ce71bfd1SAndreas Mohr 	if (oval > 2)
291ce71bfd1SAndreas Mohr 		oval = 2;
292ce71bfd1SAndreas Mohr 
293ce71bfd1SAndreas Mohr 	ucontrol->value.enumerated.item[0] = oval;
294ce71bfd1SAndreas Mohr 	return 0;
295ce71bfd1SAndreas Mohr }
296ce71bfd1SAndreas Mohr 
297ce71bfd1SAndreas Mohr static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
298ce71bfd1SAndreas Mohr 				struct snd_ctl_elem_value *ucontrol)
299ce71bfd1SAndreas Mohr {
300ce71bfd1SAndreas Mohr 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
301ce71bfd1SAndreas Mohr 	unsigned long flags;
302ce71bfd1SAndreas Mohr 	int change;
303ce71bfd1SAndreas Mohr 	unsigned char nval, oval;
304ce71bfd1SAndreas Mohr 
305ce71bfd1SAndreas Mohr 	if (ucontrol->value.enumerated.item[0] > 2)
306ce71bfd1SAndreas Mohr 		return -EINVAL;
307ce71bfd1SAndreas Mohr 	spin_lock_irqsave(&sb->mixer_lock, flags);
308ce71bfd1SAndreas Mohr 	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
309ce71bfd1SAndreas Mohr 
310ce71bfd1SAndreas Mohr 	nval = (oval & ~(3 << 6))
311ce71bfd1SAndreas Mohr 	     | (ucontrol->value.enumerated.item[0] << 6);
312ce71bfd1SAndreas Mohr 	change = nval != oval;
313ce71bfd1SAndreas Mohr 	if (change)
314ce71bfd1SAndreas Mohr 		snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);
315ce71bfd1SAndreas Mohr 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
316ce71bfd1SAndreas Mohr 	return change;
317ce71bfd1SAndreas Mohr }
318ce71bfd1SAndreas Mohr 
319ce71bfd1SAndreas Mohr /*
3201da177e4SLinus Torvalds  * SBPRO input multiplexer
3211da177e4SLinus Torvalds  */
3221da177e4SLinus Torvalds 
323029d64b0STakashi Iwai static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
3241da177e4SLinus Torvalds {
3256b9e1288STakashi Iwai 	static const char * const texts[3] = {
3261da177e4SLinus Torvalds 		"Mic", "CD", "Line"
3271da177e4SLinus Torvalds 	};
3281da177e4SLinus Torvalds 
3296b9e1288STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 3, texts);
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 
333029d64b0STakashi Iwai static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3341da177e4SLinus Torvalds {
335029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3361da177e4SLinus Torvalds 	unsigned long flags;
3371da177e4SLinus Torvalds 	unsigned char oval;
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3401da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
3411da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3421da177e4SLinus Torvalds 	switch ((oval >> 0x01) & 0x03) {
3431da177e4SLinus Torvalds 	case SB_DSP_MIXS_CD:
3441da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
3451da177e4SLinus Torvalds 		break;
3461da177e4SLinus Torvalds 	case SB_DSP_MIXS_LINE:
3471da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 2;
3481da177e4SLinus Torvalds 		break;
3491da177e4SLinus Torvalds 	default:
3501da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 0;
3511da177e4SLinus Torvalds 		break;
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds 	return 0;
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds 
356029d64b0STakashi Iwai static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3571da177e4SLinus Torvalds {
358029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3591da177e4SLinus Torvalds 	unsigned long flags;
3601da177e4SLinus Torvalds 	int change;
3611da177e4SLinus Torvalds 	unsigned char nval, oval;
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 	if (ucontrol->value.enumerated.item[0] > 2)
3641da177e4SLinus Torvalds 		return -EINVAL;
3651da177e4SLinus Torvalds 	switch (ucontrol->value.enumerated.item[0]) {
3661da177e4SLinus Torvalds 	case 1:
3671da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_CD;
3681da177e4SLinus Torvalds 		break;
3691da177e4SLinus Torvalds 	case 2:
3701da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_LINE;
3711da177e4SLinus Torvalds 		break;
3721da177e4SLinus Torvalds 	default:
3731da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_MIC;
3741da177e4SLinus Torvalds 	}
3751da177e4SLinus Torvalds 	nval <<= 1;
3761da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3771da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
3781da177e4SLinus Torvalds 	nval |= oval & ~0x06;
3791da177e4SLinus Torvalds 	change = nval != oval;
3801da177e4SLinus Torvalds 	if (change)
3811da177e4SLinus Torvalds 		snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
3821da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3831da177e4SLinus Torvalds 	return change;
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds /*
3871da177e4SLinus Torvalds  * SB16 input switch
3881da177e4SLinus Torvalds  */
3891da177e4SLinus Torvalds 
390029d64b0STakashi Iwai static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
3911da177e4SLinus Torvalds {
3921da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3931da177e4SLinus Torvalds 	uinfo->count = 4;
3941da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3951da177e4SLinus Torvalds 	uinfo->value.integer.max = 1;
3961da177e4SLinus Torvalds 	return 0;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds 
399029d64b0STakashi Iwai static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4001da177e4SLinus Torvalds {
401029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
4021da177e4SLinus Torvalds 	unsigned long flags;
4031da177e4SLinus Torvalds 	int reg1 = kcontrol->private_value & 0xff;
4041da177e4SLinus Torvalds 	int reg2 = (kcontrol->private_value >> 8) & 0xff;
4051da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
4061da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
4071da177e4SLinus Torvalds 	unsigned char val1, val2;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
4101da177e4SLinus Torvalds 	val1 = snd_sbmixer_read(sb, reg1);
4111da177e4SLinus Torvalds 	val2 = snd_sbmixer_read(sb, reg2);
4121da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
4131da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
4141da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
4151da177e4SLinus Torvalds 	ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
4161da177e4SLinus Torvalds 	ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
4171da177e4SLinus Torvalds 	return 0;
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
420029d64b0STakashi Iwai static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4211da177e4SLinus Torvalds {
422029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
4231da177e4SLinus Torvalds 	unsigned long flags;
4241da177e4SLinus Torvalds 	int reg1 = kcontrol->private_value & 0xff;
4251da177e4SLinus Torvalds 	int reg2 = (kcontrol->private_value >> 8) & 0xff;
4261da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
4271da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
4281da177e4SLinus Torvalds 	int change;
4291da177e4SLinus Torvalds 	unsigned char val1, val2, oval1, oval2;
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
4321da177e4SLinus Torvalds 	oval1 = snd_sbmixer_read(sb, reg1);
4331da177e4SLinus Torvalds 	oval2 = snd_sbmixer_read(sb, reg2);
4341da177e4SLinus Torvalds 	val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
4351da177e4SLinus Torvalds 	val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
4361da177e4SLinus Torvalds 	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
4371da177e4SLinus Torvalds 	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
4381da177e4SLinus Torvalds 	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
4391da177e4SLinus Torvalds 	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
4401da177e4SLinus Torvalds 	change = val1 != oval1 || val2 != oval2;
4411da177e4SLinus Torvalds 	if (change) {
4421da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg1, val1);
4431da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg2, val2);
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
4461da177e4SLinus Torvalds 	return change;
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds /*
4511da177e4SLinus Torvalds  */
4521da177e4SLinus Torvalds /*
4531da177e4SLinus Torvalds  */
454029d64b0STakashi Iwai int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
4551da177e4SLinus Torvalds {
456029d64b0STakashi Iwai 	static struct snd_kcontrol_new newctls[] = {
4571da177e4SLinus Torvalds 		[SB_MIX_SINGLE] = {
4581da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4591da177e4SLinus Torvalds 			.info = snd_sbmixer_info_single,
4601da177e4SLinus Torvalds 			.get = snd_sbmixer_get_single,
4611da177e4SLinus Torvalds 			.put = snd_sbmixer_put_single,
4621da177e4SLinus Torvalds 		},
4631da177e4SLinus Torvalds 		[SB_MIX_DOUBLE] = {
4641da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4651da177e4SLinus Torvalds 			.info = snd_sbmixer_info_double,
4661da177e4SLinus Torvalds 			.get = snd_sbmixer_get_double,
4671da177e4SLinus Torvalds 			.put = snd_sbmixer_put_double,
4681da177e4SLinus Torvalds 		},
4691da177e4SLinus Torvalds 		[SB_MIX_INPUT_SW] = {
4701da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4711da177e4SLinus Torvalds 			.info = snd_sb16mixer_info_input_sw,
4721da177e4SLinus Torvalds 			.get = snd_sb16mixer_get_input_sw,
4731da177e4SLinus Torvalds 			.put = snd_sb16mixer_put_input_sw,
4741da177e4SLinus Torvalds 		},
4751da177e4SLinus Torvalds 		[SB_MIX_CAPTURE_PRO] = {
4761da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4771da177e4SLinus Torvalds 			.info = snd_sb8mixer_info_mux,
4781da177e4SLinus Torvalds 			.get = snd_sb8mixer_get_mux,
4791da177e4SLinus Torvalds 			.put = snd_sb8mixer_put_mux,
4801da177e4SLinus Torvalds 		},
4811da177e4SLinus Torvalds 		[SB_MIX_CAPTURE_DT019X] = {
4821da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4831da177e4SLinus Torvalds 			.info = snd_dt019x_input_sw_info,
4841da177e4SLinus Torvalds 			.get = snd_dt019x_input_sw_get,
4851da177e4SLinus Torvalds 			.put = snd_dt019x_input_sw_put,
4861da177e4SLinus Torvalds 		},
487ce71bfd1SAndreas Mohr 		[SB_MIX_MONO_CAPTURE_ALS4K] = {
488ce71bfd1SAndreas Mohr 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
489ce71bfd1SAndreas Mohr 			.info = snd_als4k_mono_capture_route_info,
490ce71bfd1SAndreas Mohr 			.get = snd_als4k_mono_capture_route_get,
491ce71bfd1SAndreas Mohr 			.put = snd_als4k_mono_capture_route_put,
492ce71bfd1SAndreas Mohr 		},
4931da177e4SLinus Torvalds 	};
494029d64b0STakashi Iwai 	struct snd_kcontrol *ctl;
4951da177e4SLinus Torvalds 	int err;
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	ctl = snd_ctl_new1(&newctls[type], chip);
4981da177e4SLinus Torvalds 	if (! ctl)
4991da177e4SLinus Torvalds 		return -ENOMEM;
5001da177e4SLinus Torvalds 	strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
5011da177e4SLinus Torvalds 	ctl->id.index = index;
5021da177e4SLinus Torvalds 	ctl->private_value = value;
503bcc54f9aSDave Jones 	if ((err = snd_ctl_add(chip->card, ctl)) < 0)
5041da177e4SLinus Torvalds 		return err;
5051da177e4SLinus Torvalds 	return 0;
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds /*
5091da177e4SLinus Torvalds  * SB 2.0 specific mixer elements
5101da177e4SLinus Torvalds  */
5111da177e4SLinus Torvalds 
51274c2b45bSKrzysztof Helt static struct sbmix_elem snd_sb20_controls[] = {
51374c2b45bSKrzysztof Helt 	SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
51474c2b45bSKrzysztof Helt 	SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
51574c2b45bSKrzysztof Helt 	SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
51674c2b45bSKrzysztof Helt 	SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
5171da177e4SLinus Torvalds };
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds static unsigned char snd_sb20_init_values[][2] = {
5201da177e4SLinus Torvalds 	{ SB_DSP20_MASTER_DEV, 0 },
5211da177e4SLinus Torvalds 	{ SB_DSP20_FM_DEV, 0 },
5221da177e4SLinus Torvalds };
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds /*
5251da177e4SLinus Torvalds  * SB Pro specific mixer elements
5261da177e4SLinus Torvalds  */
52774c2b45bSKrzysztof Helt static struct sbmix_elem snd_sbpro_controls[] = {
52874c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
52974c2b45bSKrzysztof Helt 		  SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
53074c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
53174c2b45bSKrzysztof Helt 		  SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),
53274c2b45bSKrzysztof Helt 	SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),
53374c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
53474c2b45bSKrzysztof Helt 		  SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),
53574c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),
53674c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
53774c2b45bSKrzysztof Helt 		  SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
53874c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
5391da177e4SLinus Torvalds 	{
5401da177e4SLinus Torvalds 		.name = "Capture Source",
5411da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_PRO
54274c2b45bSKrzysztof Helt 	},
54374c2b45bSKrzysztof Helt 	SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
54474c2b45bSKrzysztof Helt 	SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
5451da177e4SLinus Torvalds };
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds static unsigned char snd_sbpro_init_values[][2] = {
5481da177e4SLinus Torvalds 	{ SB_DSP_MASTER_DEV, 0 },
5491da177e4SLinus Torvalds 	{ SB_DSP_PCM_DEV, 0 },
5501da177e4SLinus Torvalds 	{ SB_DSP_FM_DEV, 0 },
5511da177e4SLinus Torvalds };
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds /*
5541da177e4SLinus Torvalds  * SB16 specific mixer elements
5551da177e4SLinus Torvalds  */
55674c2b45bSKrzysztof Helt static struct sbmix_elem snd_sb16_controls[] = {
55774c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
55874c2b45bSKrzysztof Helt 		  SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
55974c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
56074c2b45bSKrzysztof Helt 		  SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),
56174c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Synth Capture Route",
56274c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),
56374c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
56474c2b45bSKrzysztof Helt 		  SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),
56574c2b45bSKrzysztof Helt 	SB16_INPUT_SW("CD Capture Route",
56674c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),
56774c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Switch",
56874c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
56974c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume",
57074c2b45bSKrzysztof Helt 		  SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),
57174c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Mic Capture Route",
57274c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0),
57374c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
57474c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
57574c2b45bSKrzysztof Helt 	SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
57674c2b45bSKrzysztof Helt 	SB_DOUBLE("Capture Volume",
57774c2b45bSKrzysztof Helt 		  SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
57874c2b45bSKrzysztof Helt 	SB_DOUBLE("Playback Volume",
57974c2b45bSKrzysztof Helt 		  SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
58074c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Line Capture Route",
58174c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),
58274c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Switch",
58374c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
58474c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
58574c2b45bSKrzysztof Helt 		  SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
58674c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
58774c2b45bSKrzysztof Helt 	SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),
58874c2b45bSKrzysztof Helt 	SB_DOUBLE("Tone Control - Bass",
58974c2b45bSKrzysztof Helt 		  SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
59074c2b45bSKrzysztof Helt 	SB_DOUBLE("Tone Control - Treble",
59174c2b45bSKrzysztof Helt 		  SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
5921da177e4SLinus Torvalds };
5931da177e4SLinus Torvalds 
5941da177e4SLinus Torvalds static unsigned char snd_sb16_init_values[][2] = {
5951da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
5961da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
5971da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
5981da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
5991da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
6001da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
6011da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
6021da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
6031da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
6041da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
6051da177e4SLinus Torvalds };
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds /*
6081da177e4SLinus Torvalds  * DT019x specific mixer elements
6091da177e4SLinus Torvalds  */
61074c2b45bSKrzysztof Helt static struct sbmix_elem snd_dt019x_controls[] = {
61174c2b45bSKrzysztof Helt 	/* ALS4000 below has some parts which we might be lacking,
61274c2b45bSKrzysztof Helt 	 * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
61374c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
61474c2b45bSKrzysztof Helt 		  SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15),
61574c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Switch",
61674c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
61774c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
61874c2b45bSKrzysztof Helt 		  SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15),
61974c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Switch",
62074c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
62174c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
62274c2b45bSKrzysztof Helt 		  SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15),
62374c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Switch",
62474c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
62574c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume",
62674c2b45bSKrzysztof Helt 		  SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15),
62774c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
62874c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7),
62974c2b45bSKrzysztof Helt 	SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7),
63074c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Switch",
63174c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
63274c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
63374c2b45bSKrzysztof Helt 		  SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15),
6341da177e4SLinus Torvalds 	{
6351da177e4SLinus Torvalds 		.name = "Capture Source",
6361da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_DT019X
63774c2b45bSKrzysztof Helt 	}
6381da177e4SLinus Torvalds };
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds static unsigned char snd_dt019x_init_values[][2] = {
6411da177e4SLinus Torvalds         { SB_DT019X_MASTER_DEV, 0 },
6421da177e4SLinus Torvalds         { SB_DT019X_PCM_DEV, 0 },
6431da177e4SLinus Torvalds         { SB_DT019X_SYNTH_DEV, 0 },
6441da177e4SLinus Torvalds         { SB_DT019X_CD_DEV, 0 },
6451da177e4SLinus Torvalds         { SB_DT019X_MIC_DEV, 0 },	/* Includes PC-speaker in high nibble */
6461da177e4SLinus Torvalds         { SB_DT019X_LINE_DEV, 0 },
6471da177e4SLinus Torvalds         { SB_DSP4_OUTPUT_SW, 0 },
6481da177e4SLinus Torvalds         { SB_DT019X_OUTPUT_SW2, 0 },
6491da177e4SLinus Torvalds         { SB_DT019X_CAPTURE_SW, 0x06 },
6501da177e4SLinus Torvalds };
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds /*
6531da177e4SLinus Torvalds  * ALS4000 specific mixer elements
6541da177e4SLinus Torvalds  */
65574c2b45bSKrzysztof Helt static struct sbmix_elem snd_als4000_controls[] = {
65674c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Switch",
65774c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
65874c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Switch",
65974c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
66074c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03),
66174c2b45bSKrzysztof Helt 	SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1),
66274c2b45bSKrzysztof Helt 	{
663ce71bfd1SAndreas Mohr 		.name = "Master Mono Capture Route",
664ce71bfd1SAndreas Mohr 		.type = SB_MIX_MONO_CAPTURE_ALS4K
66574c2b45bSKrzysztof Helt 	},
66674c2b45bSKrzysztof Helt 	SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1),
66774c2b45bSKrzysztof Helt 	SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01),
66874c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01),
669ce71bfd1SAndreas Mohr 	SB_SINGLE("Digital Loopback Switch",
67074c2b45bSKrzysztof Helt 		  SB_ALS4000_CR3_CONFIGURATION, 7, 0x01),
671ba7301c7SAndreas Mohr 	/* FIXME: functionality of 3D controls might be swapped, I didn't find
672ba7301c7SAndreas Mohr 	 * a description of how to identify what is supposed to be what */
67374c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07),
674ba7301c7SAndreas Mohr 	/* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
67574c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03),
676ba7301c7SAndreas Mohr 	/* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
677ba7301c7SAndreas Mohr 	 * but what ALSA 3D attribute is that actually? "Center", "Depth",
678ba7301c7SAndreas Mohr 	 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
67974c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f),
68074c2b45bSKrzysztof Helt 	SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01),
681ce71bfd1SAndreas Mohr 	SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",
68274c2b45bSKrzysztof Helt 		  SB_ALS4000_FMDAC, 5, 0x01),
68344456d37SOlaf Hering #ifdef NOT_AVAILABLE
68474c2b45bSKrzysztof Helt 	SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01),
68574c2b45bSKrzysztof Helt 	SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f),
6861da177e4SLinus Torvalds #endif
6871da177e4SLinus Torvalds };
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds static unsigned char snd_als4000_init_values[][2] = {
6901da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
6911da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
6921da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
6931da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
6941da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
6951da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
6961da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
6971da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
6981da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
6991da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
7001da177e4SLinus Torvalds 	{ SB_DT019X_OUTPUT_SW2, 0 },
7011da177e4SLinus Torvalds 	{ SB_ALS4000_MIC_IN_GAIN, 0 },
7021da177e4SLinus Torvalds };
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds /*
7051da177e4SLinus Torvalds  */
706029d64b0STakashi Iwai static int snd_sbmixer_init(struct snd_sb *chip,
70774c2b45bSKrzysztof Helt 			    struct sbmix_elem *controls,
7081da177e4SLinus Torvalds 			    int controls_count,
7091da177e4SLinus Torvalds 			    unsigned char map[][2],
7101da177e4SLinus Torvalds 			    int map_count,
7111da177e4SLinus Torvalds 			    char *name)
7121da177e4SLinus Torvalds {
7131da177e4SLinus Torvalds 	unsigned long flags;
714029d64b0STakashi Iwai 	struct snd_card *card = chip->card;
7151da177e4SLinus Torvalds 	int idx, err;
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 	/* mixer reset */
7181da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->mixer_lock, flags);
7191da177e4SLinus Torvalds 	snd_sbmixer_write(chip, 0x00, 0x00);
7201da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->mixer_lock, flags);
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds 	/* mute and zero volume channels */
7231da177e4SLinus Torvalds 	for (idx = 0; idx < map_count; idx++) {
7241da177e4SLinus Torvalds 		spin_lock_irqsave(&chip->mixer_lock, flags);
7251da177e4SLinus Torvalds 		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
7261da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->mixer_lock, flags);
7271da177e4SLinus Torvalds 	}
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds 	for (idx = 0; idx < controls_count; idx++) {
73074c2b45bSKrzysztof Helt 		err = snd_sbmixer_add_ctl_elem(chip, &controls[idx]);
73174c2b45bSKrzysztof Helt 		if (err < 0)
7321da177e4SLinus Torvalds 			return err;
7331da177e4SLinus Torvalds 	}
7341da177e4SLinus Torvalds 	snd_component_add(card, name);
7351da177e4SLinus Torvalds 	strcpy(card->mixername, name);
7361da177e4SLinus Torvalds 	return 0;
7371da177e4SLinus Torvalds }
7381da177e4SLinus Torvalds 
739029d64b0STakashi Iwai int snd_sbmixer_new(struct snd_sb *chip)
7401da177e4SLinus Torvalds {
741029d64b0STakashi Iwai 	struct snd_card *card;
7421da177e4SLinus Torvalds 	int err;
7431da177e4SLinus Torvalds 
744622207dcSTakashi Iwai 	if (snd_BUG_ON(!chip || !chip->card))
745622207dcSTakashi Iwai 		return -EINVAL;
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds 	card = chip->card;
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds 	switch (chip->hardware) {
7501da177e4SLinus Torvalds 	case SB_HW_10:
7511da177e4SLinus Torvalds 		return 0; /* no mixer chip on SB1.x */
7521da177e4SLinus Torvalds 	case SB_HW_20:
7531da177e4SLinus Torvalds 	case SB_HW_201:
7541da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7551da177e4SLinus Torvalds 					    snd_sb20_controls,
7561da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb20_controls),
7571da177e4SLinus Torvalds 					    snd_sb20_init_values,
7581da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb20_init_values),
7591da177e4SLinus Torvalds 					    "CTL1335")) < 0)
7601da177e4SLinus Torvalds 			return err;
7611da177e4SLinus Torvalds 		break;
7621da177e4SLinus Torvalds 	case SB_HW_PRO:
763ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
7641da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7651da177e4SLinus Torvalds 					    snd_sbpro_controls,
7661da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sbpro_controls),
7671da177e4SLinus Torvalds 					    snd_sbpro_init_values,
7681da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sbpro_init_values),
7691da177e4SLinus Torvalds 					    "CTL1345")) < 0)
7701da177e4SLinus Torvalds 			return err;
7711da177e4SLinus Torvalds 		break;
7721da177e4SLinus Torvalds 	case SB_HW_16:
7731da177e4SLinus Torvalds 	case SB_HW_ALS100:
774621887aeSTakashi Iwai 	case SB_HW_CS5530:
7751da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7761da177e4SLinus Torvalds 					    snd_sb16_controls,
7771da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb16_controls),
7781da177e4SLinus Torvalds 					    snd_sb16_init_values,
7791da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb16_init_values),
7801da177e4SLinus Torvalds 					    "CTL1745")) < 0)
7811da177e4SLinus Torvalds 			return err;
7821da177e4SLinus Torvalds 		break;
7831da177e4SLinus Torvalds 	case SB_HW_ALS4000:
78474c2b45bSKrzysztof Helt 		/* use only the first 16 controls from SB16 */
78574c2b45bSKrzysztof Helt 		err = snd_sbmixer_init(chip,
78674c2b45bSKrzysztof Helt 					snd_sb16_controls,
78774c2b45bSKrzysztof Helt 					16,
78874c2b45bSKrzysztof Helt 					snd_sb16_init_values,
78974c2b45bSKrzysztof Helt 					ARRAY_SIZE(snd_sb16_init_values),
79074c2b45bSKrzysztof Helt 					"ALS4000");
79174c2b45bSKrzysztof Helt 		if (err < 0)
79274c2b45bSKrzysztof Helt 			return err;
7931da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7941da177e4SLinus Torvalds 					    snd_als4000_controls,
7951da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_als4000_controls),
7961da177e4SLinus Torvalds 					    snd_als4000_init_values,
7971da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_als4000_init_values),
7981da177e4SLinus Torvalds 					    "ALS4000")) < 0)
7991da177e4SLinus Torvalds 			return err;
8001da177e4SLinus Torvalds 		break;
8011da177e4SLinus Torvalds 	case SB_HW_DT019X:
802665ebe92SDan Carpenter 		err = snd_sbmixer_init(chip,
8031da177e4SLinus Torvalds 				       snd_dt019x_controls,
8041da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_dt019x_controls),
8051da177e4SLinus Torvalds 				       snd_dt019x_init_values,
8061da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_dt019x_init_values),
807665ebe92SDan Carpenter 				       "DT019X");
808665ebe92SDan Carpenter 		if (err < 0)
809665ebe92SDan Carpenter 			return err;
8101da177e4SLinus Torvalds 		break;
8111da177e4SLinus Torvalds 	default:
8121da177e4SLinus Torvalds 		strcpy(card->mixername, "???");
8131da177e4SLinus Torvalds 	}
8141da177e4SLinus Torvalds 	return 0;
8151da177e4SLinus Torvalds }
8165bdb6a16STakashi Iwai 
8175bdb6a16STakashi Iwai #ifdef CONFIG_PM
8185bdb6a16STakashi Iwai static unsigned char sb20_saved_regs[] = {
8195bdb6a16STakashi Iwai 	SB_DSP20_MASTER_DEV,
8205bdb6a16STakashi Iwai 	SB_DSP20_PCM_DEV,
8215bdb6a16STakashi Iwai 	SB_DSP20_FM_DEV,
8225bdb6a16STakashi Iwai 	SB_DSP20_CD_DEV,
8235bdb6a16STakashi Iwai };
8245bdb6a16STakashi Iwai 
8255bdb6a16STakashi Iwai static unsigned char sbpro_saved_regs[] = {
8265bdb6a16STakashi Iwai 	SB_DSP_MASTER_DEV,
8275bdb6a16STakashi Iwai 	SB_DSP_PCM_DEV,
8285bdb6a16STakashi Iwai 	SB_DSP_PLAYBACK_FILT,
8295bdb6a16STakashi Iwai 	SB_DSP_FM_DEV,
8305bdb6a16STakashi Iwai 	SB_DSP_CD_DEV,
8315bdb6a16STakashi Iwai 	SB_DSP_LINE_DEV,
8325bdb6a16STakashi Iwai 	SB_DSP_MIC_DEV,
8335bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_SOURCE,
8345bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_FILT,
8355bdb6a16STakashi Iwai };
8365bdb6a16STakashi Iwai 
8375bdb6a16STakashi Iwai static unsigned char sb16_saved_regs[] = {
8385bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
8395bdb6a16STakashi Iwai 	SB_DSP4_3DSE,
8405bdb6a16STakashi Iwai 	SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
8415bdb6a16STakashi Iwai 	SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,
8425bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
8435bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
8445bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
8455bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8465bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
8475bdb6a16STakashi Iwai 	SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
8485bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
8495bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
8505bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
8515bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
8525bdb6a16STakashi Iwai 	SB_DSP4_MIC_AGC
8535bdb6a16STakashi Iwai };
8545bdb6a16STakashi Iwai 
8555bdb6a16STakashi Iwai static unsigned char dt019x_saved_regs[] = {
8565bdb6a16STakashi Iwai 	SB_DT019X_MASTER_DEV,
8575bdb6a16STakashi Iwai 	SB_DT019X_PCM_DEV,
8585bdb6a16STakashi Iwai 	SB_DT019X_SYNTH_DEV,
8595bdb6a16STakashi Iwai 	SB_DT019X_CD_DEV,
8605bdb6a16STakashi Iwai 	SB_DT019X_MIC_DEV,
8615bdb6a16STakashi Iwai 	SB_DT019X_SPKR_DEV,
8625bdb6a16STakashi Iwai 	SB_DT019X_LINE_DEV,
8635bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8645bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
8655bdb6a16STakashi Iwai 	SB_DT019X_CAPTURE_SW,
8665bdb6a16STakashi Iwai };
8675bdb6a16STakashi Iwai 
8685bdb6a16STakashi Iwai static unsigned char als4000_saved_regs[] = {
869ce71bfd1SAndreas Mohr 	/* please verify in dsheet whether regs to be added
870ce71bfd1SAndreas Mohr 	   are actually real H/W or just dummy */
8715bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
8725bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8735bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
8745bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
8755bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
8765bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
8775bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
8785bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
8795bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
8805bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
8815bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
8825bdb6a16STakashi Iwai 	SB_ALS4000_MONO_IO_CTRL,
8835bdb6a16STakashi Iwai 	SB_ALS4000_MIC_IN_GAIN,
884ce71bfd1SAndreas Mohr 	SB_ALS4000_FMDAC,
8855bdb6a16STakashi Iwai 	SB_ALS4000_3D_SND_FX,
8865bdb6a16STakashi Iwai 	SB_ALS4000_3D_TIME_DELAY,
887ce71bfd1SAndreas Mohr 	SB_ALS4000_CR3_CONFIGURATION,
8885bdb6a16STakashi Iwai };
8895bdb6a16STakashi Iwai 
8905bdb6a16STakashi Iwai static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
8915bdb6a16STakashi Iwai {
8925bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
893622207dcSTakashi Iwai 	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
894622207dcSTakashi Iwai 		return;
8955bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
8965bdb6a16STakashi Iwai 		*val++ = snd_sbmixer_read(chip, *regs++);
8975bdb6a16STakashi Iwai }
8985bdb6a16STakashi Iwai 
8995bdb6a16STakashi Iwai static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
9005bdb6a16STakashi Iwai {
9015bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
902622207dcSTakashi Iwai 	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
903622207dcSTakashi Iwai 		return;
9045bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
9055bdb6a16STakashi Iwai 		snd_sbmixer_write(chip, *regs++, *val++);
9065bdb6a16STakashi Iwai }
9075bdb6a16STakashi Iwai 
9085bdb6a16STakashi Iwai void snd_sbmixer_suspend(struct snd_sb *chip)
9095bdb6a16STakashi Iwai {
9105bdb6a16STakashi Iwai 	switch (chip->hardware) {
9115bdb6a16STakashi Iwai 	case SB_HW_20:
9125bdb6a16STakashi Iwai 	case SB_HW_201:
9135bdb6a16STakashi Iwai 		save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
9145bdb6a16STakashi Iwai 		break;
9155bdb6a16STakashi Iwai 	case SB_HW_PRO:
916ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
9175bdb6a16STakashi Iwai 		save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9185bdb6a16STakashi Iwai 		break;
9195bdb6a16STakashi Iwai 	case SB_HW_16:
9205bdb6a16STakashi Iwai 	case SB_HW_ALS100:
921621887aeSTakashi Iwai 	case SB_HW_CS5530:
9225bdb6a16STakashi Iwai 		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9235bdb6a16STakashi Iwai 		break;
9245bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9255bdb6a16STakashi Iwai 		save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9265bdb6a16STakashi Iwai 		break;
9275bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9285bdb6a16STakashi Iwai 		save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9295bdb6a16STakashi Iwai 		break;
9305bdb6a16STakashi Iwai 	default:
9315bdb6a16STakashi Iwai 		break;
9325bdb6a16STakashi Iwai 	}
9335bdb6a16STakashi Iwai }
9345bdb6a16STakashi Iwai 
9355bdb6a16STakashi Iwai void snd_sbmixer_resume(struct snd_sb *chip)
9365bdb6a16STakashi Iwai {
9375bdb6a16STakashi Iwai 	switch (chip->hardware) {
9385bdb6a16STakashi Iwai 	case SB_HW_20:
9395bdb6a16STakashi Iwai 	case SB_HW_201:
9405bdb6a16STakashi Iwai 		restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
9415bdb6a16STakashi Iwai 		break;
9425bdb6a16STakashi Iwai 	case SB_HW_PRO:
943ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
9445bdb6a16STakashi Iwai 		restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9455bdb6a16STakashi Iwai 		break;
9465bdb6a16STakashi Iwai 	case SB_HW_16:
9475bdb6a16STakashi Iwai 	case SB_HW_ALS100:
948621887aeSTakashi Iwai 	case SB_HW_CS5530:
9495bdb6a16STakashi Iwai 		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9505bdb6a16STakashi Iwai 		break;
9515bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9525bdb6a16STakashi Iwai 		restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9535bdb6a16STakashi Iwai 		break;
9545bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9555bdb6a16STakashi Iwai 		restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9565bdb6a16STakashi Iwai 		break;
9575bdb6a16STakashi Iwai 	default:
9585bdb6a16STakashi Iwai 		break;
9595bdb6a16STakashi Iwai 	}
9605bdb6a16STakashi Iwai }
9615bdb6a16STakashi Iwai #endif
962