xref: /openbmc/linux/sound/isa/sb/sb_mixer.c (revision 5bdb6a16)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Copyright (c) by Jaroslav Kysela <perex@suse.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 <sound/driver.h>
231da177e4SLinus Torvalds #include <asm/io.h>
241da177e4SLinus Torvalds #include <linux/delay.h>
251da177e4SLinus Torvalds #include <linux/time.h>
261da177e4SLinus Torvalds #include <sound/core.h>
271da177e4SLinus Torvalds #include <sound/sb.h>
281da177e4SLinus Torvalds #include <sound/control.h>
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #undef IO_DEBUG
311da177e4SLinus Torvalds 
32029d64b0STakashi Iwai void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data)
331da177e4SLinus Torvalds {
341da177e4SLinus Torvalds 	outb(reg, SBP(chip, MIXER_ADDR));
351da177e4SLinus Torvalds 	udelay(10);
361da177e4SLinus Torvalds 	outb(data, SBP(chip, MIXER_DATA));
371da177e4SLinus Torvalds 	udelay(10);
381da177e4SLinus Torvalds #ifdef IO_DEBUG
3999b359baSTakashi Iwai 	snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n", reg, data);
401da177e4SLinus Torvalds #endif
411da177e4SLinus Torvalds }
421da177e4SLinus Torvalds 
43029d64b0STakashi Iwai unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
441da177e4SLinus Torvalds {
451da177e4SLinus Torvalds 	unsigned char result;
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 	outb(reg, SBP(chip, MIXER_ADDR));
481da177e4SLinus Torvalds 	udelay(10);
491da177e4SLinus Torvalds 	result = inb(SBP(chip, MIXER_DATA));
501da177e4SLinus Torvalds 	udelay(10);
511da177e4SLinus Torvalds #ifdef IO_DEBUG
5299b359baSTakashi Iwai 	snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n", reg, result);
531da177e4SLinus Torvalds #endif
541da177e4SLinus Torvalds 	return result;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds /*
581da177e4SLinus Torvalds  * Single channel mixer element
591da177e4SLinus Torvalds  */
601da177e4SLinus Torvalds 
61029d64b0STakashi Iwai static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
661da177e4SLinus Torvalds 	uinfo->count = 1;
671da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
681da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
691da177e4SLinus Torvalds 	return 0;
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds 
72029d64b0STakashi Iwai static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
731da177e4SLinus Torvalds {
74029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
751da177e4SLinus Torvalds 	unsigned long flags;
761da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
771da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 16) & 0xff;
781da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
791da177e4SLinus Torvalds 	unsigned char val;
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
821da177e4SLinus Torvalds 	val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
831da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
841da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
851da177e4SLinus Torvalds 	return 0;
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds 
88029d64b0STakashi Iwai static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
891da177e4SLinus Torvalds {
90029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
911da177e4SLinus Torvalds 	unsigned long flags;
921da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
931da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 16) & 0x07;
941da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
951da177e4SLinus Torvalds 	int change;
961da177e4SLinus Torvalds 	unsigned char val, oval;
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	val = (ucontrol->value.integer.value[0] & mask) << shift;
991da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1001da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, reg);
1011da177e4SLinus Torvalds 	val = (oval & ~(mask << shift)) | val;
1021da177e4SLinus Torvalds 	change = val != oval;
1031da177e4SLinus Torvalds 	if (change)
1041da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg, val);
1051da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1061da177e4SLinus Torvalds 	return change;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds /*
1101da177e4SLinus Torvalds  * Double channel mixer element
1111da177e4SLinus Torvalds  */
1121da177e4SLinus Torvalds 
113029d64b0STakashi Iwai static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1141da177e4SLinus Torvalds {
1151da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1181da177e4SLinus Torvalds 	uinfo->count = 2;
1191da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
1201da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
1211da177e4SLinus Torvalds 	return 0;
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
124029d64b0STakashi Iwai static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1251da177e4SLinus Torvalds {
126029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1271da177e4SLinus Torvalds 	unsigned long flags;
1281da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
1291da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
1301da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x07;
1311da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 19) & 0x07;
1321da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1331da177e4SLinus Torvalds 	unsigned char left, right;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1361da177e4SLinus Torvalds 	left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
1371da177e4SLinus Torvalds 	right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
1381da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1391da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = left;
1401da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = right;
1411da177e4SLinus Torvalds 	return 0;
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds 
144029d64b0STakashi Iwai static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1451da177e4SLinus Torvalds {
146029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1471da177e4SLinus Torvalds 	unsigned long flags;
1481da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
1491da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
1501da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x07;
1511da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 19) & 0x07;
1521da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1531da177e4SLinus Torvalds 	int change;
1541da177e4SLinus Torvalds 	unsigned char left, right, oleft, oright;
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	left = (ucontrol->value.integer.value[0] & mask) << left_shift;
1571da177e4SLinus Torvalds 	right = (ucontrol->value.integer.value[1] & mask) << right_shift;
1581da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1591da177e4SLinus Torvalds 	if (left_reg == right_reg) {
1601da177e4SLinus Torvalds 		oleft = snd_sbmixer_read(sb, left_reg);
1611da177e4SLinus Torvalds 		left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
1621da177e4SLinus Torvalds 		change = left != oleft;
1631da177e4SLinus Torvalds 		if (change)
1641da177e4SLinus Torvalds 			snd_sbmixer_write(sb, left_reg, left);
1651da177e4SLinus Torvalds 	} else {
1661da177e4SLinus Torvalds 		oleft = snd_sbmixer_read(sb, left_reg);
1671da177e4SLinus Torvalds 		oright = snd_sbmixer_read(sb, right_reg);
1681da177e4SLinus Torvalds 		left = (oleft & ~(mask << left_shift)) | left;
1691da177e4SLinus Torvalds 		right = (oright & ~(mask << right_shift)) | right;
1701da177e4SLinus Torvalds 		change = left != oleft || right != oright;
1711da177e4SLinus Torvalds 		if (change) {
1721da177e4SLinus Torvalds 			snd_sbmixer_write(sb, left_reg, left);
1731da177e4SLinus Torvalds 			snd_sbmixer_write(sb, right_reg, right);
1741da177e4SLinus Torvalds 		}
1751da177e4SLinus Torvalds 	}
1761da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1771da177e4SLinus Torvalds 	return change;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds /*
1811da177e4SLinus Torvalds  * DT-019x / ALS-007 capture/input switch
1821da177e4SLinus Torvalds  */
1831da177e4SLinus Torvalds 
184029d64b0STakashi Iwai static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1851da177e4SLinus Torvalds {
1861da177e4SLinus Torvalds 	static char *texts[5] = {
1871da177e4SLinus Torvalds 		"CD", "Mic", "Line", "Synth", "Master"
1881da177e4SLinus Torvalds 	};
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1911da177e4SLinus Torvalds 	uinfo->count = 1;
1921da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 5;
1931da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 4)
1941da177e4SLinus Torvalds 		uinfo->value.enumerated.item = 4;
1951da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1961da177e4SLinus Torvalds 	return 0;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds 
199029d64b0STakashi Iwai static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2001da177e4SLinus Torvalds {
201029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
2021da177e4SLinus Torvalds 	unsigned long flags;
2031da177e4SLinus Torvalds 	unsigned char oval;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
2061da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
2071da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
2081da177e4SLinus Torvalds 	switch (oval & 0x07) {
2091da177e4SLinus Torvalds 	case SB_DT019X_CAP_CD:
2101da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 0;
2111da177e4SLinus Torvalds 		break;
2121da177e4SLinus Torvalds 	case SB_DT019X_CAP_MIC:
2131da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
2141da177e4SLinus Torvalds 		break;
2151da177e4SLinus Torvalds 	case SB_DT019X_CAP_LINE:
2161da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 2;
2171da177e4SLinus Torvalds 		break;
2181da177e4SLinus Torvalds 	case SB_DT019X_CAP_MAIN:
2191da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 4;
2201da177e4SLinus Torvalds 		break;
2211da177e4SLinus Torvalds 	/* To record the synth on these cards you must record the main.   */
2221da177e4SLinus Torvalds 	/* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */
2231da177e4SLinus Torvalds 	/* duplicate case labels if left uncommented. */
2241da177e4SLinus Torvalds 	/* case SB_DT019X_CAP_SYNTH:
2251da177e4SLinus Torvalds 	 *	ucontrol->value.enumerated.item[0] = 3;
2261da177e4SLinus Torvalds 	 *	break;
2271da177e4SLinus Torvalds 	 */
2281da177e4SLinus Torvalds 	default:
2291da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 4;
2301da177e4SLinus Torvalds 		break;
2311da177e4SLinus Torvalds 	}
2321da177e4SLinus Torvalds 	return 0;
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds 
235029d64b0STakashi Iwai static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2361da177e4SLinus Torvalds {
237029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
2381da177e4SLinus Torvalds 	unsigned long flags;
2391da177e4SLinus Torvalds 	int change;
2401da177e4SLinus Torvalds 	unsigned char nval, oval;
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 	if (ucontrol->value.enumerated.item[0] > 4)
2431da177e4SLinus Torvalds 		return -EINVAL;
2441da177e4SLinus Torvalds 	switch (ucontrol->value.enumerated.item[0]) {
2451da177e4SLinus Torvalds 	case 0:
2461da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_CD;
2471da177e4SLinus Torvalds 		break;
2481da177e4SLinus Torvalds 	case 1:
2491da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MIC;
2501da177e4SLinus Torvalds 		break;
2511da177e4SLinus Torvalds 	case 2:
2521da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_LINE;
2531da177e4SLinus Torvalds 		break;
2541da177e4SLinus Torvalds 	case 3:
2551da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_SYNTH;
2561da177e4SLinus Torvalds 		break;
2571da177e4SLinus Torvalds 	case 4:
2581da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MAIN;
2591da177e4SLinus Torvalds 		break;
2601da177e4SLinus Torvalds 	default:
2611da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MAIN;
2621da177e4SLinus Torvalds 	}
2631da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
2641da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
2651da177e4SLinus Torvalds 	change = nval != oval;
2661da177e4SLinus Torvalds 	if (change)
2671da177e4SLinus Torvalds 		snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
2681da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
2691da177e4SLinus Torvalds 	return change;
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds /*
2731da177e4SLinus Torvalds  * SBPRO input multiplexer
2741da177e4SLinus Torvalds  */
2751da177e4SLinus Torvalds 
276029d64b0STakashi Iwai static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
2771da177e4SLinus Torvalds {
2781da177e4SLinus Torvalds 	static char *texts[3] = {
2791da177e4SLinus Torvalds 		"Mic", "CD", "Line"
2801da177e4SLinus Torvalds 	};
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2831da177e4SLinus Torvalds 	uinfo->count = 1;
2841da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 3;
2851da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 2)
2861da177e4SLinus Torvalds 		uinfo->value.enumerated.item = 2;
2871da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2881da177e4SLinus Torvalds 	return 0;
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 
292029d64b0STakashi Iwai static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2931da177e4SLinus Torvalds {
294029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
2951da177e4SLinus Torvalds 	unsigned long flags;
2961da177e4SLinus Torvalds 	unsigned char oval;
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
2991da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
3001da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3011da177e4SLinus Torvalds 	switch ((oval >> 0x01) & 0x03) {
3021da177e4SLinus Torvalds 	case SB_DSP_MIXS_CD:
3031da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
3041da177e4SLinus Torvalds 		break;
3051da177e4SLinus Torvalds 	case SB_DSP_MIXS_LINE:
3061da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 2;
3071da177e4SLinus Torvalds 		break;
3081da177e4SLinus Torvalds 	default:
3091da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 0;
3101da177e4SLinus Torvalds 		break;
3111da177e4SLinus Torvalds 	}
3121da177e4SLinus Torvalds 	return 0;
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
315029d64b0STakashi Iwai static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3161da177e4SLinus Torvalds {
317029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3181da177e4SLinus Torvalds 	unsigned long flags;
3191da177e4SLinus Torvalds 	int change;
3201da177e4SLinus Torvalds 	unsigned char nval, oval;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	if (ucontrol->value.enumerated.item[0] > 2)
3231da177e4SLinus Torvalds 		return -EINVAL;
3241da177e4SLinus Torvalds 	switch (ucontrol->value.enumerated.item[0]) {
3251da177e4SLinus Torvalds 	case 1:
3261da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_CD;
3271da177e4SLinus Torvalds 		break;
3281da177e4SLinus Torvalds 	case 2:
3291da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_LINE;
3301da177e4SLinus Torvalds 		break;
3311da177e4SLinus Torvalds 	default:
3321da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_MIC;
3331da177e4SLinus Torvalds 	}
3341da177e4SLinus Torvalds 	nval <<= 1;
3351da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3361da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
3371da177e4SLinus Torvalds 	nval |= oval & ~0x06;
3381da177e4SLinus Torvalds 	change = nval != oval;
3391da177e4SLinus Torvalds 	if (change)
3401da177e4SLinus Torvalds 		snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
3411da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3421da177e4SLinus Torvalds 	return change;
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds /*
3461da177e4SLinus Torvalds  * SB16 input switch
3471da177e4SLinus Torvalds  */
3481da177e4SLinus Torvalds 
349029d64b0STakashi Iwai static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
3501da177e4SLinus Torvalds {
3511da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3521da177e4SLinus Torvalds 	uinfo->count = 4;
3531da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3541da177e4SLinus Torvalds 	uinfo->value.integer.max = 1;
3551da177e4SLinus Torvalds 	return 0;
3561da177e4SLinus Torvalds }
3571da177e4SLinus Torvalds 
358029d64b0STakashi Iwai static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3591da177e4SLinus Torvalds {
360029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3611da177e4SLinus Torvalds 	unsigned long flags;
3621da177e4SLinus Torvalds 	int reg1 = kcontrol->private_value & 0xff;
3631da177e4SLinus Torvalds 	int reg2 = (kcontrol->private_value >> 8) & 0xff;
3641da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
3651da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
3661da177e4SLinus Torvalds 	unsigned char val1, val2;
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3691da177e4SLinus Torvalds 	val1 = snd_sbmixer_read(sb, reg1);
3701da177e4SLinus Torvalds 	val2 = snd_sbmixer_read(sb, reg2);
3711da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3721da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
3731da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
3741da177e4SLinus Torvalds 	ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
3751da177e4SLinus Torvalds 	ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
3761da177e4SLinus Torvalds 	return 0;
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds 
379029d64b0STakashi Iwai static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3801da177e4SLinus Torvalds {
381029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3821da177e4SLinus Torvalds 	unsigned long flags;
3831da177e4SLinus Torvalds 	int reg1 = kcontrol->private_value & 0xff;
3841da177e4SLinus Torvalds 	int reg2 = (kcontrol->private_value >> 8) & 0xff;
3851da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
3861da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
3871da177e4SLinus Torvalds 	int change;
3881da177e4SLinus Torvalds 	unsigned char val1, val2, oval1, oval2;
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3911da177e4SLinus Torvalds 	oval1 = snd_sbmixer_read(sb, reg1);
3921da177e4SLinus Torvalds 	oval2 = snd_sbmixer_read(sb, reg2);
3931da177e4SLinus Torvalds 	val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
3941da177e4SLinus Torvalds 	val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
3951da177e4SLinus Torvalds 	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
3961da177e4SLinus Torvalds 	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
3971da177e4SLinus Torvalds 	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
3981da177e4SLinus Torvalds 	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
3991da177e4SLinus Torvalds 	change = val1 != oval1 || val2 != oval2;
4001da177e4SLinus Torvalds 	if (change) {
4011da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg1, val1);
4021da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg2, val2);
4031da177e4SLinus Torvalds 	}
4041da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
4051da177e4SLinus Torvalds 	return change;
4061da177e4SLinus Torvalds }
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds /*
4101da177e4SLinus Torvalds  */
4111da177e4SLinus Torvalds /*
4121da177e4SLinus Torvalds  */
413029d64b0STakashi Iwai int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
4141da177e4SLinus Torvalds {
415029d64b0STakashi Iwai 	static struct snd_kcontrol_new newctls[] = {
4161da177e4SLinus Torvalds 		[SB_MIX_SINGLE] = {
4171da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4181da177e4SLinus Torvalds 			.info = snd_sbmixer_info_single,
4191da177e4SLinus Torvalds 			.get = snd_sbmixer_get_single,
4201da177e4SLinus Torvalds 			.put = snd_sbmixer_put_single,
4211da177e4SLinus Torvalds 		},
4221da177e4SLinus Torvalds 		[SB_MIX_DOUBLE] = {
4231da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4241da177e4SLinus Torvalds 			.info = snd_sbmixer_info_double,
4251da177e4SLinus Torvalds 			.get = snd_sbmixer_get_double,
4261da177e4SLinus Torvalds 			.put = snd_sbmixer_put_double,
4271da177e4SLinus Torvalds 		},
4281da177e4SLinus Torvalds 		[SB_MIX_INPUT_SW] = {
4291da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4301da177e4SLinus Torvalds 			.info = snd_sb16mixer_info_input_sw,
4311da177e4SLinus Torvalds 			.get = snd_sb16mixer_get_input_sw,
4321da177e4SLinus Torvalds 			.put = snd_sb16mixer_put_input_sw,
4331da177e4SLinus Torvalds 		},
4341da177e4SLinus Torvalds 		[SB_MIX_CAPTURE_PRO] = {
4351da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4361da177e4SLinus Torvalds 			.info = snd_sb8mixer_info_mux,
4371da177e4SLinus Torvalds 			.get = snd_sb8mixer_get_mux,
4381da177e4SLinus Torvalds 			.put = snd_sb8mixer_put_mux,
4391da177e4SLinus Torvalds 		},
4401da177e4SLinus Torvalds 		[SB_MIX_CAPTURE_DT019X] = {
4411da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4421da177e4SLinus Torvalds 			.info = snd_dt019x_input_sw_info,
4431da177e4SLinus Torvalds 			.get = snd_dt019x_input_sw_get,
4441da177e4SLinus Torvalds 			.put = snd_dt019x_input_sw_put,
4451da177e4SLinus Torvalds 		},
4461da177e4SLinus Torvalds 	};
447029d64b0STakashi Iwai 	struct snd_kcontrol *ctl;
4481da177e4SLinus Torvalds 	int err;
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	ctl = snd_ctl_new1(&newctls[type], chip);
4511da177e4SLinus Torvalds 	if (! ctl)
4521da177e4SLinus Torvalds 		return -ENOMEM;
4531da177e4SLinus Torvalds 	strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
4541da177e4SLinus Torvalds 	ctl->id.index = index;
4551da177e4SLinus Torvalds 	ctl->private_value = value;
4561da177e4SLinus Torvalds 	if ((err = snd_ctl_add(chip->card, ctl)) < 0) {
4571da177e4SLinus Torvalds 		snd_ctl_free_one(ctl);
4581da177e4SLinus Torvalds 		return err;
4591da177e4SLinus Torvalds 	}
4601da177e4SLinus Torvalds 	return 0;
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds /*
4641da177e4SLinus Torvalds  * SB 2.0 specific mixer elements
4651da177e4SLinus Torvalds  */
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds static struct sbmix_elem snd_sb20_ctl_master_play_vol =
4681da177e4SLinus Torvalds 	SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7);
4691da177e4SLinus Torvalds static struct sbmix_elem snd_sb20_ctl_pcm_play_vol =
4701da177e4SLinus Torvalds 	SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3);
4711da177e4SLinus Torvalds static struct sbmix_elem snd_sb20_ctl_synth_play_vol =
4721da177e4SLinus Torvalds 	SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7);
4731da177e4SLinus Torvalds static struct sbmix_elem snd_sb20_ctl_cd_play_vol =
4741da177e4SLinus Torvalds 	SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7);
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds static struct sbmix_elem *snd_sb20_controls[] = {
4771da177e4SLinus Torvalds 	&snd_sb20_ctl_master_play_vol,
4781da177e4SLinus Torvalds 	&snd_sb20_ctl_pcm_play_vol,
4791da177e4SLinus Torvalds 	&snd_sb20_ctl_synth_play_vol,
4801da177e4SLinus Torvalds 	&snd_sb20_ctl_cd_play_vol
4811da177e4SLinus Torvalds };
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds static unsigned char snd_sb20_init_values[][2] = {
4841da177e4SLinus Torvalds 	{ SB_DSP20_MASTER_DEV, 0 },
4851da177e4SLinus Torvalds 	{ SB_DSP20_FM_DEV, 0 },
4861da177e4SLinus Torvalds };
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds /*
4891da177e4SLinus Torvalds  * SB Pro specific mixer elements
4901da177e4SLinus Torvalds  */
4911da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_master_play_vol =
4921da177e4SLinus Torvalds 	SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7);
4931da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_pcm_play_vol =
4941da177e4SLinus Torvalds 	SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7);
4951da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_pcm_play_filter =
4961da177e4SLinus Torvalds 	SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1);
4971da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_synth_play_vol =
4981da177e4SLinus Torvalds 	SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7);
4991da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_cd_play_vol =
5001da177e4SLinus Torvalds 	SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7);
5011da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_line_play_vol =
5021da177e4SLinus Torvalds 	SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7);
5031da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_mic_play_vol =
5041da177e4SLinus Torvalds 	SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3);
5051da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_capture_source =
5061da177e4SLinus Torvalds 	{
5071da177e4SLinus Torvalds 		.name = "Capture Source",
5081da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_PRO
5091da177e4SLinus Torvalds 	};
5101da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_capture_filter =
5111da177e4SLinus Torvalds 	SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1);
5121da177e4SLinus Torvalds static struct sbmix_elem snd_sbpro_ctl_capture_low_filter =
5131da177e4SLinus Torvalds 	SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1);
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds static struct sbmix_elem *snd_sbpro_controls[] = {
5161da177e4SLinus Torvalds 	&snd_sbpro_ctl_master_play_vol,
5171da177e4SLinus Torvalds 	&snd_sbpro_ctl_pcm_play_vol,
5181da177e4SLinus Torvalds 	&snd_sbpro_ctl_pcm_play_filter,
5191da177e4SLinus Torvalds 	&snd_sbpro_ctl_synth_play_vol,
5201da177e4SLinus Torvalds 	&snd_sbpro_ctl_cd_play_vol,
5211da177e4SLinus Torvalds 	&snd_sbpro_ctl_line_play_vol,
5221da177e4SLinus Torvalds 	&snd_sbpro_ctl_mic_play_vol,
5231da177e4SLinus Torvalds 	&snd_sbpro_ctl_capture_source,
5241da177e4SLinus Torvalds 	&snd_sbpro_ctl_capture_filter,
5251da177e4SLinus Torvalds 	&snd_sbpro_ctl_capture_low_filter
5261da177e4SLinus Torvalds };
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds static unsigned char snd_sbpro_init_values[][2] = {
5291da177e4SLinus Torvalds 	{ SB_DSP_MASTER_DEV, 0 },
5301da177e4SLinus Torvalds 	{ SB_DSP_PCM_DEV, 0 },
5311da177e4SLinus Torvalds 	{ SB_DSP_FM_DEV, 0 },
5321da177e4SLinus Torvalds };
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds /*
5351da177e4SLinus Torvalds  * SB16 specific mixer elements
5361da177e4SLinus Torvalds  */
5371da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_master_play_vol =
5381da177e4SLinus Torvalds 	SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31);
5391da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_3d_enhance_switch =
5401da177e4SLinus Torvalds 	SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1);
5411da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_tone_bass =
5421da177e4SLinus Torvalds 	SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15);
5431da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_tone_treble =
5441da177e4SLinus Torvalds 	SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15);
5451da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_pcm_play_vol =
5461da177e4SLinus Torvalds 	SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31);
5471da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_synth_capture_route =
5481da177e4SLinus Torvalds 	SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5);
5491da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_synth_play_vol =
5501da177e4SLinus Torvalds 	SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31);
5511da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_cd_capture_route =
5521da177e4SLinus Torvalds 	SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1);
5531da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_cd_play_switch =
5541da177e4SLinus Torvalds 	SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1);
5551da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_cd_play_vol =
5561da177e4SLinus Torvalds 	SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31);
5571da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_line_capture_route =
5581da177e4SLinus Torvalds 	SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3);
5591da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_line_play_switch =
5601da177e4SLinus Torvalds 	SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1);
5611da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_line_play_vol =
5621da177e4SLinus Torvalds 	SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31);
5631da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_mic_capture_route =
5641da177e4SLinus Torvalds 	SB16_INPUT_SW("Mic Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0);
5651da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_mic_play_switch =
5661da177e4SLinus Torvalds 	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1);
5671da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
5681da177e4SLinus Torvalds 	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
5691da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
5701da177e4SLinus Torvalds 	SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
5711da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_capture_vol =
5721da177e4SLinus Torvalds 	SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
5731da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_play_vol =
5741da177e4SLinus Torvalds 	SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3);
5751da177e4SLinus Torvalds static struct sbmix_elem snd_sb16_ctl_auto_mic_gain =
5761da177e4SLinus Torvalds 	SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1);
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds static struct sbmix_elem *snd_sb16_controls[] = {
5791da177e4SLinus Torvalds 	&snd_sb16_ctl_master_play_vol,
5801da177e4SLinus Torvalds 	&snd_sb16_ctl_3d_enhance_switch,
5811da177e4SLinus Torvalds 	&snd_sb16_ctl_tone_bass,
5821da177e4SLinus Torvalds 	&snd_sb16_ctl_tone_treble,
5831da177e4SLinus Torvalds 	&snd_sb16_ctl_pcm_play_vol,
5841da177e4SLinus Torvalds 	&snd_sb16_ctl_synth_capture_route,
5851da177e4SLinus Torvalds 	&snd_sb16_ctl_synth_play_vol,
5861da177e4SLinus Torvalds 	&snd_sb16_ctl_cd_capture_route,
5871da177e4SLinus Torvalds 	&snd_sb16_ctl_cd_play_switch,
5881da177e4SLinus Torvalds 	&snd_sb16_ctl_cd_play_vol,
5891da177e4SLinus Torvalds 	&snd_sb16_ctl_line_capture_route,
5901da177e4SLinus Torvalds 	&snd_sb16_ctl_line_play_switch,
5911da177e4SLinus Torvalds 	&snd_sb16_ctl_line_play_vol,
5921da177e4SLinus Torvalds 	&snd_sb16_ctl_mic_capture_route,
5931da177e4SLinus Torvalds 	&snd_sb16_ctl_mic_play_switch,
5941da177e4SLinus Torvalds 	&snd_sb16_ctl_mic_play_vol,
5951da177e4SLinus Torvalds 	&snd_sb16_ctl_pc_speaker_vol,
5961da177e4SLinus Torvalds 	&snd_sb16_ctl_capture_vol,
5971da177e4SLinus Torvalds 	&snd_sb16_ctl_play_vol,
5981da177e4SLinus Torvalds 	&snd_sb16_ctl_auto_mic_gain
5991da177e4SLinus Torvalds };
6001da177e4SLinus Torvalds 
6011da177e4SLinus Torvalds static unsigned char snd_sb16_init_values[][2] = {
6021da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
6031da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
6041da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
6051da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
6061da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
6071da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
6081da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
6091da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
6101da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
6111da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
6121da177e4SLinus Torvalds };
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds /*
6151da177e4SLinus Torvalds  * DT019x specific mixer elements
6161da177e4SLinus Torvalds  */
6171da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_master_play_vol =
6181da177e4SLinus Torvalds 	SB_DOUBLE("Master Playback Volume", SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4,0, 15);
6191da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_pcm_play_vol =
6201da177e4SLinus Torvalds 	SB_DOUBLE("PCM Playback Volume", SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4,0, 15);
6211da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_synth_play_vol =
6221da177e4SLinus Torvalds 	SB_DOUBLE("Synth Playback Volume", SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4,0, 15);
6231da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_cd_play_vol =
6241da177e4SLinus Torvalds 	SB_DOUBLE("CD Playback Volume", SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4,0, 15);
6251da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
6261da177e4SLinus Torvalds 	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
6271da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
6281da177e4SLinus Torvalds 	SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0,  7);
6291da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
6301da177e4SLinus Torvalds 	SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
6311da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =
6321da177e4SLinus Torvalds 	SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1);
6331da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_synth_play_switch =
6341da177e4SLinus Torvalds 	SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1);
6351da177e4SLinus Torvalds static struct sbmix_elem snd_dt019x_ctl_capture_source =
6361da177e4SLinus Torvalds 	{
6371da177e4SLinus Torvalds 		.name = "Capture Source",
6381da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_DT019X
6391da177e4SLinus Torvalds 	};
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds static struct sbmix_elem *snd_dt019x_controls[] = {
6421da177e4SLinus Torvalds 	&snd_dt019x_ctl_master_play_vol,
6431da177e4SLinus Torvalds 	&snd_dt019x_ctl_pcm_play_vol,
6441da177e4SLinus Torvalds 	&snd_dt019x_ctl_synth_play_vol,
6451da177e4SLinus Torvalds 	&snd_dt019x_ctl_cd_play_vol,
6461da177e4SLinus Torvalds 	&snd_dt019x_ctl_mic_play_vol,
6471da177e4SLinus Torvalds 	&snd_dt019x_ctl_pc_speaker_vol,
6481da177e4SLinus Torvalds 	&snd_dt019x_ctl_line_play_vol,
6491da177e4SLinus Torvalds 	&snd_sb16_ctl_mic_play_switch,
6501da177e4SLinus Torvalds 	&snd_sb16_ctl_cd_play_switch,
6511da177e4SLinus Torvalds 	&snd_sb16_ctl_line_play_switch,
6521da177e4SLinus Torvalds 	&snd_dt019x_ctl_pcm_play_switch,
6531da177e4SLinus Torvalds 	&snd_dt019x_ctl_synth_play_switch,
6541da177e4SLinus Torvalds 	&snd_dt019x_ctl_capture_source
6551da177e4SLinus Torvalds };
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds static unsigned char snd_dt019x_init_values[][2] = {
6581da177e4SLinus Torvalds         { SB_DT019X_MASTER_DEV, 0 },
6591da177e4SLinus Torvalds         { SB_DT019X_PCM_DEV, 0 },
6601da177e4SLinus Torvalds         { SB_DT019X_SYNTH_DEV, 0 },
6611da177e4SLinus Torvalds         { SB_DT019X_CD_DEV, 0 },
6621da177e4SLinus Torvalds         { SB_DT019X_MIC_DEV, 0 },	/* Includes PC-speaker in high nibble */
6631da177e4SLinus Torvalds         { SB_DT019X_LINE_DEV, 0 },
6641da177e4SLinus Torvalds         { SB_DSP4_OUTPUT_SW, 0 },
6651da177e4SLinus Torvalds         { SB_DT019X_OUTPUT_SW2, 0 },
6661da177e4SLinus Torvalds         { SB_DT019X_CAPTURE_SW, 0x06 },
6671da177e4SLinus Torvalds };
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds /*
6701da177e4SLinus Torvalds  * ALS4000 specific mixer elements
6711da177e4SLinus Torvalds  */
6721da177e4SLinus Torvalds /* FIXME: SB_ALS4000_MONO_IO_CTRL needs output select ctrl! */
673ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_ctl_master_mono_playback_switch =
674ba7301c7SAndreas Mohr 	SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1);
675ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_ctl_master_mono_capture_route =
676ba7301c7SAndreas Mohr 	SB_SINGLE("Master Mono Capture Route", SB_ALS4000_MONO_IO_CTRL, 6, 0x03);
677ba7301c7SAndreas Mohr /* FIXME: mono playback switch also available on DT019X? */
678ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_ctl_mono_playback_switch =
679ba7301c7SAndreas Mohr 	SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1);
6801da177e4SLinus Torvalds static struct sbmix_elem snd_als4000_ctl_mic_20db_boost =
6811da177e4SLinus Torvalds 	SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03);
682ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_ctl_mixer_loopback =
683ba7301c7SAndreas Mohr 	SB_SINGLE("Analog Loopback", SB_ALS4000_MIC_IN_GAIN, 7, 0x01);
684ba7301c7SAndreas Mohr /* FIXME: functionality of 3D controls might be swapped, I didn't find
685ba7301c7SAndreas Mohr  * a description of how to identify what is supposed to be what */
686ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_3d_control_switch =
687ba7301c7SAndreas Mohr 	SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01);
688ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_3d_control_ratio =
689ba7301c7SAndreas Mohr 	SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07);
690ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_3d_control_freq =
691ba7301c7SAndreas Mohr 	/* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
692ba7301c7SAndreas Mohr 	SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03);
693ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_3d_control_delay =
694ba7301c7SAndreas Mohr 	/* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
695ba7301c7SAndreas Mohr 	 * but what ALSA 3D attribute is that actually? "Center", "Depth",
696ba7301c7SAndreas Mohr 	 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
697ba7301c7SAndreas Mohr 	SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f);
698ba7301c7SAndreas Mohr static struct sbmix_elem snd_als4000_3d_control_poweroff_switch =
6991da177e4SLinus Torvalds 	SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01);
70044456d37SOlaf Hering #ifdef NOT_AVAILABLE
7011da177e4SLinus Torvalds static struct sbmix_elem snd_als4000_ctl_fmdac =
7021da177e4SLinus Torvalds 	SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01);
7031da177e4SLinus Torvalds static struct sbmix_elem snd_als4000_ctl_qsound =
7041da177e4SLinus Torvalds 	SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f);
7051da177e4SLinus Torvalds #endif
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds static struct sbmix_elem *snd_als4000_controls[] = {
7081da177e4SLinus Torvalds 	&snd_sb16_ctl_master_play_vol,
7091da177e4SLinus Torvalds 	&snd_dt019x_ctl_pcm_play_switch,
7101da177e4SLinus Torvalds 	&snd_sb16_ctl_pcm_play_vol,
7111da177e4SLinus Torvalds 	&snd_sb16_ctl_synth_capture_route,
7121da177e4SLinus Torvalds 	&snd_dt019x_ctl_synth_play_switch,
7131da177e4SLinus Torvalds 	&snd_sb16_ctl_synth_play_vol,
7141da177e4SLinus Torvalds 	&snd_sb16_ctl_cd_capture_route,
7151da177e4SLinus Torvalds 	&snd_sb16_ctl_cd_play_switch,
7161da177e4SLinus Torvalds 	&snd_sb16_ctl_cd_play_vol,
7171da177e4SLinus Torvalds 	&snd_sb16_ctl_line_capture_route,
7181da177e4SLinus Torvalds 	&snd_sb16_ctl_line_play_switch,
7191da177e4SLinus Torvalds 	&snd_sb16_ctl_line_play_vol,
7201da177e4SLinus Torvalds 	&snd_sb16_ctl_mic_capture_route,
7211da177e4SLinus Torvalds 	&snd_als4000_ctl_mic_20db_boost,
7221da177e4SLinus Torvalds 	&snd_sb16_ctl_auto_mic_gain,
7231da177e4SLinus Torvalds 	&snd_sb16_ctl_mic_play_switch,
7241da177e4SLinus Torvalds 	&snd_sb16_ctl_mic_play_vol,
7251da177e4SLinus Torvalds 	&snd_sb16_ctl_pc_speaker_vol,
7261da177e4SLinus Torvalds 	&snd_sb16_ctl_capture_vol,
7271da177e4SLinus Torvalds 	&snd_sb16_ctl_play_vol,
728ba7301c7SAndreas Mohr 	&snd_als4000_ctl_master_mono_playback_switch,
729ba7301c7SAndreas Mohr 	&snd_als4000_ctl_master_mono_capture_route,
730ba7301c7SAndreas Mohr 	&snd_als4000_ctl_mono_playback_switch,
731ba7301c7SAndreas Mohr 	&snd_als4000_ctl_mixer_loopback,
732ba7301c7SAndreas Mohr 	&snd_als4000_3d_control_switch,
733ba7301c7SAndreas Mohr 	&snd_als4000_3d_control_ratio,
734ba7301c7SAndreas Mohr 	&snd_als4000_3d_control_freq,
735ba7301c7SAndreas Mohr 	&snd_als4000_3d_control_delay,
736ba7301c7SAndreas Mohr 	&snd_als4000_3d_control_poweroff_switch,
73744456d37SOlaf Hering #ifdef NOT_AVAILABLE
7381da177e4SLinus Torvalds 	&snd_als4000_ctl_fmdac,
7391da177e4SLinus Torvalds 	&snd_als4000_ctl_qsound,
7401da177e4SLinus Torvalds #endif
7411da177e4SLinus Torvalds };
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds static unsigned char snd_als4000_init_values[][2] = {
7441da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
7451da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
7461da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
7471da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
7481da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
7491da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
7501da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
7511da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
7521da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
7531da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
7541da177e4SLinus Torvalds 	{ SB_DT019X_OUTPUT_SW2, 0 },
7551da177e4SLinus Torvalds 	{ SB_ALS4000_MIC_IN_GAIN, 0 },
7561da177e4SLinus Torvalds };
7571da177e4SLinus Torvalds 
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds /*
7601da177e4SLinus Torvalds  */
761029d64b0STakashi Iwai static int snd_sbmixer_init(struct snd_sb *chip,
7621da177e4SLinus Torvalds 			    struct sbmix_elem **controls,
7631da177e4SLinus Torvalds 			    int controls_count,
7641da177e4SLinus Torvalds 			    unsigned char map[][2],
7651da177e4SLinus Torvalds 			    int map_count,
7661da177e4SLinus Torvalds 			    char *name)
7671da177e4SLinus Torvalds {
7681da177e4SLinus Torvalds 	unsigned long flags;
769029d64b0STakashi Iwai 	struct snd_card *card = chip->card;
7701da177e4SLinus Torvalds 	int idx, err;
7711da177e4SLinus Torvalds 
7721da177e4SLinus Torvalds 	/* mixer reset */
7731da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->mixer_lock, flags);
7741da177e4SLinus Torvalds 	snd_sbmixer_write(chip, 0x00, 0x00);
7751da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->mixer_lock, flags);
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds 	/* mute and zero volume channels */
7781da177e4SLinus Torvalds 	for (idx = 0; idx < map_count; idx++) {
7791da177e4SLinus Torvalds 		spin_lock_irqsave(&chip->mixer_lock, flags);
7801da177e4SLinus Torvalds 		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
7811da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->mixer_lock, flags);
7821da177e4SLinus Torvalds 	}
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds 	for (idx = 0; idx < controls_count; idx++) {
7851da177e4SLinus Torvalds 		if ((err = snd_sbmixer_add_ctl_elem(chip, controls[idx])) < 0)
7861da177e4SLinus Torvalds 			return err;
7871da177e4SLinus Torvalds 	}
7881da177e4SLinus Torvalds 	snd_component_add(card, name);
7891da177e4SLinus Torvalds 	strcpy(card->mixername, name);
7901da177e4SLinus Torvalds 	return 0;
7911da177e4SLinus Torvalds }
7921da177e4SLinus Torvalds 
793029d64b0STakashi Iwai int snd_sbmixer_new(struct snd_sb *chip)
7941da177e4SLinus Torvalds {
795029d64b0STakashi Iwai 	struct snd_card *card;
7961da177e4SLinus Torvalds 	int err;
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds 	card = chip->card;
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds 	switch (chip->hardware) {
8031da177e4SLinus Torvalds 	case SB_HW_10:
8041da177e4SLinus Torvalds 		return 0; /* no mixer chip on SB1.x */
8051da177e4SLinus Torvalds 	case SB_HW_20:
8061da177e4SLinus Torvalds 	case SB_HW_201:
8071da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
8081da177e4SLinus Torvalds 					    snd_sb20_controls,
8091da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb20_controls),
8101da177e4SLinus Torvalds 					    snd_sb20_init_values,
8111da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb20_init_values),
8121da177e4SLinus Torvalds 					    "CTL1335")) < 0)
8131da177e4SLinus Torvalds 			return err;
8141da177e4SLinus Torvalds 		break;
8151da177e4SLinus Torvalds 	case SB_HW_PRO:
8161da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
8171da177e4SLinus Torvalds 					    snd_sbpro_controls,
8181da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sbpro_controls),
8191da177e4SLinus Torvalds 					    snd_sbpro_init_values,
8201da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sbpro_init_values),
8211da177e4SLinus Torvalds 					    "CTL1345")) < 0)
8221da177e4SLinus Torvalds 			return err;
8231da177e4SLinus Torvalds 		break;
8241da177e4SLinus Torvalds 	case SB_HW_16:
8251da177e4SLinus Torvalds 	case SB_HW_ALS100:
8261da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
8271da177e4SLinus Torvalds 					    snd_sb16_controls,
8281da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb16_controls),
8291da177e4SLinus Torvalds 					    snd_sb16_init_values,
8301da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb16_init_values),
8311da177e4SLinus Torvalds 					    "CTL1745")) < 0)
8321da177e4SLinus Torvalds 			return err;
8331da177e4SLinus Torvalds 		break;
8341da177e4SLinus Torvalds 	case SB_HW_ALS4000:
8351da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
8361da177e4SLinus Torvalds 					    snd_als4000_controls,
8371da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_als4000_controls),
8381da177e4SLinus Torvalds 					    snd_als4000_init_values,
8391da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_als4000_init_values),
8401da177e4SLinus Torvalds 					    "ALS4000")) < 0)
8411da177e4SLinus Torvalds 			return err;
8421da177e4SLinus Torvalds 		break;
8431da177e4SLinus Torvalds 	case SB_HW_DT019X:
8441da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
8451da177e4SLinus Torvalds 					    snd_dt019x_controls,
8461da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_dt019x_controls),
8471da177e4SLinus Torvalds 					    snd_dt019x_init_values,
8481da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_dt019x_init_values),
8491da177e4SLinus Torvalds 					    "DT019X")) < 0)
8501da177e4SLinus Torvalds 		break;
8511da177e4SLinus Torvalds 	default:
8521da177e4SLinus Torvalds 		strcpy(card->mixername, "???");
8531da177e4SLinus Torvalds 	}
8541da177e4SLinus Torvalds 	return 0;
8551da177e4SLinus Torvalds }
8565bdb6a16STakashi Iwai 
8575bdb6a16STakashi Iwai #ifdef CONFIG_PM
8585bdb6a16STakashi Iwai static unsigned char sb20_saved_regs[] = {
8595bdb6a16STakashi Iwai 	SB_DSP20_MASTER_DEV,
8605bdb6a16STakashi Iwai 	SB_DSP20_PCM_DEV,
8615bdb6a16STakashi Iwai 	SB_DSP20_FM_DEV,
8625bdb6a16STakashi Iwai 	SB_DSP20_CD_DEV,
8635bdb6a16STakashi Iwai };
8645bdb6a16STakashi Iwai 
8655bdb6a16STakashi Iwai static unsigned char sbpro_saved_regs[] = {
8665bdb6a16STakashi Iwai 	SB_DSP_MASTER_DEV,
8675bdb6a16STakashi Iwai 	SB_DSP_PCM_DEV,
8685bdb6a16STakashi Iwai 	SB_DSP_PLAYBACK_FILT,
8695bdb6a16STakashi Iwai 	SB_DSP_FM_DEV,
8705bdb6a16STakashi Iwai 	SB_DSP_CD_DEV,
8715bdb6a16STakashi Iwai 	SB_DSP_LINE_DEV,
8725bdb6a16STakashi Iwai 	SB_DSP_MIC_DEV,
8735bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_SOURCE,
8745bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_FILT,
8755bdb6a16STakashi Iwai };
8765bdb6a16STakashi Iwai 
8775bdb6a16STakashi Iwai static unsigned char sb16_saved_regs[] = {
8785bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
8795bdb6a16STakashi Iwai 	SB_DSP4_3DSE,
8805bdb6a16STakashi Iwai 	SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
8815bdb6a16STakashi Iwai 	SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,
8825bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
8835bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
8845bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
8855bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8865bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
8875bdb6a16STakashi Iwai 	SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
8885bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
8895bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
8905bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
8915bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
8925bdb6a16STakashi Iwai 	SB_DSP4_MIC_AGC
8935bdb6a16STakashi Iwai };
8945bdb6a16STakashi Iwai 
8955bdb6a16STakashi Iwai static unsigned char dt019x_saved_regs[] = {
8965bdb6a16STakashi Iwai 	SB_DT019X_MASTER_DEV,
8975bdb6a16STakashi Iwai 	SB_DT019X_PCM_DEV,
8985bdb6a16STakashi Iwai 	SB_DT019X_SYNTH_DEV,
8995bdb6a16STakashi Iwai 	SB_DT019X_CD_DEV,
9005bdb6a16STakashi Iwai 	SB_DT019X_MIC_DEV,
9015bdb6a16STakashi Iwai 	SB_DT019X_SPKR_DEV,
9025bdb6a16STakashi Iwai 	SB_DT019X_LINE_DEV,
9035bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
9045bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
9055bdb6a16STakashi Iwai 	SB_DT019X_CAPTURE_SW,
9065bdb6a16STakashi Iwai };
9075bdb6a16STakashi Iwai 
9085bdb6a16STakashi Iwai static unsigned char als4000_saved_regs[] = {
9095bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
9105bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
9115bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
9125bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
9135bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
9145bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
9155bdb6a16STakashi Iwai 	SB_DSP4_MIC_AGC,
9165bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
9175bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
9185bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
9195bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
9205bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
9215bdb6a16STakashi Iwai 	SB_ALS4000_MONO_IO_CTRL,
9225bdb6a16STakashi Iwai 	SB_ALS4000_MIC_IN_GAIN,
9235bdb6a16STakashi Iwai 	SB_ALS4000_3D_SND_FX,
9245bdb6a16STakashi Iwai 	SB_ALS4000_3D_TIME_DELAY,
9255bdb6a16STakashi Iwai };
9265bdb6a16STakashi Iwai 
9275bdb6a16STakashi Iwai static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
9285bdb6a16STakashi Iwai {
9295bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
9305bdb6a16STakashi Iwai 	snd_assert(num_regs > ARRAY_SIZE(chip->saved_regs), return);
9315bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
9325bdb6a16STakashi Iwai 		*val++ = snd_sbmixer_read(chip, *regs++);
9335bdb6a16STakashi Iwai }
9345bdb6a16STakashi Iwai 
9355bdb6a16STakashi Iwai static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
9365bdb6a16STakashi Iwai {
9375bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
9385bdb6a16STakashi Iwai 	snd_assert(num_regs > ARRAY_SIZE(chip->saved_regs), return);
9395bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
9405bdb6a16STakashi Iwai 		snd_sbmixer_write(chip, *regs++, *val++);
9415bdb6a16STakashi Iwai }
9425bdb6a16STakashi Iwai 
9435bdb6a16STakashi Iwai void snd_sbmixer_suspend(struct snd_sb *chip)
9445bdb6a16STakashi Iwai {
9455bdb6a16STakashi Iwai 	switch (chip->hardware) {
9465bdb6a16STakashi Iwai 	case SB_HW_20:
9475bdb6a16STakashi Iwai 	case SB_HW_201:
9485bdb6a16STakashi Iwai 		save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
9495bdb6a16STakashi Iwai 		break;
9505bdb6a16STakashi Iwai 	case SB_HW_PRO:
9515bdb6a16STakashi Iwai 		save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9525bdb6a16STakashi Iwai 		break;
9535bdb6a16STakashi Iwai 	case SB_HW_16:
9545bdb6a16STakashi Iwai 	case SB_HW_ALS100:
9555bdb6a16STakashi Iwai 		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9565bdb6a16STakashi Iwai 		break;
9575bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9585bdb6a16STakashi Iwai 		save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9595bdb6a16STakashi Iwai 		break;
9605bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9615bdb6a16STakashi Iwai 		save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9625bdb6a16STakashi Iwai 		break;
9635bdb6a16STakashi Iwai 	default:
9645bdb6a16STakashi Iwai 		break;
9655bdb6a16STakashi Iwai 	}
9665bdb6a16STakashi Iwai }
9675bdb6a16STakashi Iwai 
9685bdb6a16STakashi Iwai void snd_sbmixer_resume(struct snd_sb *chip)
9695bdb6a16STakashi Iwai {
9705bdb6a16STakashi Iwai 	switch (chip->hardware) {
9715bdb6a16STakashi Iwai 	case SB_HW_20:
9725bdb6a16STakashi Iwai 	case SB_HW_201:
9735bdb6a16STakashi Iwai 		restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
9745bdb6a16STakashi Iwai 		break;
9755bdb6a16STakashi Iwai 	case SB_HW_PRO:
9765bdb6a16STakashi Iwai 		restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9775bdb6a16STakashi Iwai 		break;
9785bdb6a16STakashi Iwai 	case SB_HW_16:
9795bdb6a16STakashi Iwai 	case SB_HW_ALS100:
9805bdb6a16STakashi Iwai 		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9815bdb6a16STakashi Iwai 		break;
9825bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9835bdb6a16STakashi Iwai 		restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9845bdb6a16STakashi Iwai 		break;
9855bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9865bdb6a16STakashi Iwai 		restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9875bdb6a16STakashi Iwai 		break;
9885bdb6a16STakashi Iwai 	default:
9895bdb6a16STakashi Iwai 		break;
9905bdb6a16STakashi Iwai 	}
9915bdb6a16STakashi Iwai }
9925bdb6a16STakashi Iwai #endif
993