xref: /openbmc/linux/sound/isa/sb/sb_mixer.c (revision 1a59d1b8)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds  *  Routines for Sound Blaster mixer control
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
76cbbfe1cSTakashi Iwai #include <linux/io.h>
81da177e4SLinus Torvalds #include <linux/delay.h>
91da177e4SLinus Torvalds #include <linux/time.h>
101da177e4SLinus Torvalds #include <sound/core.h>
111da177e4SLinus Torvalds #include <sound/sb.h>
121da177e4SLinus Torvalds #include <sound/control.h>
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #undef IO_DEBUG
151da177e4SLinus Torvalds 
16029d64b0STakashi Iwai void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data)
171da177e4SLinus Torvalds {
181da177e4SLinus Torvalds 	outb(reg, SBP(chip, MIXER_ADDR));
191da177e4SLinus Torvalds 	udelay(10);
201da177e4SLinus Torvalds 	outb(data, SBP(chip, MIXER_DATA));
211da177e4SLinus Torvalds 	udelay(10);
221da177e4SLinus Torvalds #ifdef IO_DEBUG
2399b359baSTakashi Iwai 	snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n", reg, data);
241da177e4SLinus Torvalds #endif
251da177e4SLinus Torvalds }
261da177e4SLinus Torvalds 
27029d64b0STakashi Iwai unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
281da177e4SLinus Torvalds {
291da177e4SLinus Torvalds 	unsigned char result;
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds 	outb(reg, SBP(chip, MIXER_ADDR));
321da177e4SLinus Torvalds 	udelay(10);
331da177e4SLinus Torvalds 	result = inb(SBP(chip, MIXER_DATA));
341da177e4SLinus Torvalds 	udelay(10);
351da177e4SLinus Torvalds #ifdef IO_DEBUG
3699b359baSTakashi Iwai 	snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n", reg, result);
371da177e4SLinus Torvalds #endif
381da177e4SLinus Torvalds 	return result;
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds /*
421da177e4SLinus Torvalds  * Single channel mixer element
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds 
45029d64b0STakashi Iwai static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
501da177e4SLinus Torvalds 	uinfo->count = 1;
511da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
521da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
531da177e4SLinus Torvalds 	return 0;
541da177e4SLinus Torvalds }
551da177e4SLinus Torvalds 
56029d64b0STakashi Iwai static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
571da177e4SLinus Torvalds {
58029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
591da177e4SLinus Torvalds 	unsigned long flags;
601da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
611da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 16) & 0xff;
621da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
631da177e4SLinus Torvalds 	unsigned char val;
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
661da177e4SLinus Torvalds 	val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
671da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
681da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = val;
691da177e4SLinus Torvalds 	return 0;
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds 
72029d64b0STakashi Iwai static int snd_sbmixer_put_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) & 0x07;
781da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
791da177e4SLinus Torvalds 	int change;
801da177e4SLinus Torvalds 	unsigned char val, oval;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	val = (ucontrol->value.integer.value[0] & mask) << shift;
831da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
841da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, reg);
851da177e4SLinus Torvalds 	val = (oval & ~(mask << shift)) | val;
861da177e4SLinus Torvalds 	change = val != oval;
871da177e4SLinus Torvalds 	if (change)
881da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg, val);
891da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
901da177e4SLinus Torvalds 	return change;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds /*
941da177e4SLinus Torvalds  * Double channel mixer element
951da177e4SLinus Torvalds  */
961da177e4SLinus Torvalds 
97029d64b0STakashi Iwai static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1021da177e4SLinus Torvalds 	uinfo->count = 2;
1031da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
1041da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
1051da177e4SLinus Torvalds 	return 0;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
108029d64b0STakashi Iwai static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1091da177e4SLinus Torvalds {
110029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1111da177e4SLinus Torvalds 	unsigned long flags;
1121da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
1131da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
1141da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x07;
1151da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 19) & 0x07;
1161da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1171da177e4SLinus Torvalds 	unsigned char left, right;
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1201da177e4SLinus Torvalds 	left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
1211da177e4SLinus Torvalds 	right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
1221da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1231da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = left;
1241da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = right;
1251da177e4SLinus Torvalds 	return 0;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
128029d64b0STakashi Iwai static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1291da177e4SLinus Torvalds {
130029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1311da177e4SLinus Torvalds 	unsigned long flags;
1321da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
1331da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
1341da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x07;
1351da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 19) & 0x07;
1361da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
1371da177e4SLinus Torvalds 	int change;
1381da177e4SLinus Torvalds 	unsigned char left, right, oleft, oright;
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	left = (ucontrol->value.integer.value[0] & mask) << left_shift;
1411da177e4SLinus Torvalds 	right = (ucontrol->value.integer.value[1] & mask) << right_shift;
1421da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1431da177e4SLinus Torvalds 	if (left_reg == right_reg) {
1441da177e4SLinus Torvalds 		oleft = snd_sbmixer_read(sb, left_reg);
1451da177e4SLinus Torvalds 		left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
1461da177e4SLinus Torvalds 		change = left != oleft;
1471da177e4SLinus Torvalds 		if (change)
1481da177e4SLinus Torvalds 			snd_sbmixer_write(sb, left_reg, left);
1491da177e4SLinus Torvalds 	} else {
1501da177e4SLinus Torvalds 		oleft = snd_sbmixer_read(sb, left_reg);
1511da177e4SLinus Torvalds 		oright = snd_sbmixer_read(sb, right_reg);
1521da177e4SLinus Torvalds 		left = (oleft & ~(mask << left_shift)) | left;
1531da177e4SLinus Torvalds 		right = (oright & ~(mask << right_shift)) | right;
1541da177e4SLinus Torvalds 		change = left != oleft || right != oright;
1551da177e4SLinus Torvalds 		if (change) {
1561da177e4SLinus Torvalds 			snd_sbmixer_write(sb, left_reg, left);
1571da177e4SLinus Torvalds 			snd_sbmixer_write(sb, right_reg, right);
1581da177e4SLinus Torvalds 		}
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1611da177e4SLinus Torvalds 	return change;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds /*
1651da177e4SLinus Torvalds  * DT-019x / ALS-007 capture/input switch
1661da177e4SLinus Torvalds  */
1671da177e4SLinus Torvalds 
168029d64b0STakashi Iwai static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1691da177e4SLinus Torvalds {
1706b9e1288STakashi Iwai 	static const char * const texts[5] = {
1711da177e4SLinus Torvalds 		"CD", "Mic", "Line", "Synth", "Master"
1721da177e4SLinus Torvalds 	};
1731da177e4SLinus Torvalds 
1746b9e1288STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 5, texts);
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
177029d64b0STakashi Iwai static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1781da177e4SLinus Torvalds {
179029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
1801da177e4SLinus Torvalds 	unsigned long flags;
1811da177e4SLinus Torvalds 	unsigned char oval;
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
1841da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
1851da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
1861da177e4SLinus Torvalds 	switch (oval & 0x07) {
1871da177e4SLinus Torvalds 	case SB_DT019X_CAP_CD:
1881da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 0;
1891da177e4SLinus Torvalds 		break;
1901da177e4SLinus Torvalds 	case SB_DT019X_CAP_MIC:
1911da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
1921da177e4SLinus Torvalds 		break;
1931da177e4SLinus Torvalds 	case SB_DT019X_CAP_LINE:
1941da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 2;
1951da177e4SLinus Torvalds 		break;
1961da177e4SLinus Torvalds 	case SB_DT019X_CAP_MAIN:
1971da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 4;
1981da177e4SLinus Torvalds 		break;
1991da177e4SLinus Torvalds 	/* To record the synth on these cards you must record the main.   */
2001da177e4SLinus Torvalds 	/* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */
2011da177e4SLinus Torvalds 	/* duplicate case labels if left uncommented. */
2021da177e4SLinus Torvalds 	/* case SB_DT019X_CAP_SYNTH:
2031da177e4SLinus Torvalds 	 *	ucontrol->value.enumerated.item[0] = 3;
2041da177e4SLinus Torvalds 	 *	break;
2051da177e4SLinus Torvalds 	 */
2061da177e4SLinus Torvalds 	default:
2071da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 4;
2081da177e4SLinus Torvalds 		break;
2091da177e4SLinus Torvalds 	}
2101da177e4SLinus Torvalds 	return 0;
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds 
213029d64b0STakashi Iwai static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2141da177e4SLinus Torvalds {
215029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
2161da177e4SLinus Torvalds 	unsigned long flags;
2171da177e4SLinus Torvalds 	int change;
2181da177e4SLinus Torvalds 	unsigned char nval, oval;
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	if (ucontrol->value.enumerated.item[0] > 4)
2211da177e4SLinus Torvalds 		return -EINVAL;
2221da177e4SLinus Torvalds 	switch (ucontrol->value.enumerated.item[0]) {
2231da177e4SLinus Torvalds 	case 0:
2241da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_CD;
2251da177e4SLinus Torvalds 		break;
2261da177e4SLinus Torvalds 	case 1:
2271da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MIC;
2281da177e4SLinus Torvalds 		break;
2291da177e4SLinus Torvalds 	case 2:
2301da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_LINE;
2311da177e4SLinus Torvalds 		break;
2321da177e4SLinus Torvalds 	case 3:
2331da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_SYNTH;
2341da177e4SLinus Torvalds 		break;
2351da177e4SLinus Torvalds 	case 4:
2361da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MAIN;
2371da177e4SLinus Torvalds 		break;
2381da177e4SLinus Torvalds 	default:
2391da177e4SLinus Torvalds 		nval = SB_DT019X_CAP_MAIN;
2401da177e4SLinus Torvalds 	}
2411da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
2421da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
2431da177e4SLinus Torvalds 	change = nval != oval;
2441da177e4SLinus Torvalds 	if (change)
2451da177e4SLinus Torvalds 		snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
2461da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
2471da177e4SLinus Torvalds 	return change;
2481da177e4SLinus Torvalds }
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds /*
251ce71bfd1SAndreas Mohr  * ALS4000 mono recording control switch
252ce71bfd1SAndreas Mohr  */
253ce71bfd1SAndreas Mohr 
254ce71bfd1SAndreas Mohr static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
255ce71bfd1SAndreas Mohr 					     struct snd_ctl_elem_info *uinfo)
256ce71bfd1SAndreas Mohr {
2576b9e1288STakashi Iwai 	static const char * const texts[3] = {
258ce71bfd1SAndreas Mohr 		"L chan only", "R chan only", "L ch/2 + R ch/2"
259ce71bfd1SAndreas Mohr 	};
260ce71bfd1SAndreas Mohr 
2616b9e1288STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 3, texts);
262ce71bfd1SAndreas Mohr }
263ce71bfd1SAndreas Mohr 
264ce71bfd1SAndreas Mohr static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
265ce71bfd1SAndreas Mohr 				struct snd_ctl_elem_value *ucontrol)
266ce71bfd1SAndreas Mohr {
267ce71bfd1SAndreas Mohr 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
268ce71bfd1SAndreas Mohr 	unsigned long flags;
269ce71bfd1SAndreas Mohr 	unsigned char oval;
270ce71bfd1SAndreas Mohr 
271ce71bfd1SAndreas Mohr 	spin_lock_irqsave(&sb->mixer_lock, flags);
272ce71bfd1SAndreas Mohr 	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
273ce71bfd1SAndreas Mohr 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
274ce71bfd1SAndreas Mohr 	oval >>= 6;
275ce71bfd1SAndreas Mohr 	if (oval > 2)
276ce71bfd1SAndreas Mohr 		oval = 2;
277ce71bfd1SAndreas Mohr 
278ce71bfd1SAndreas Mohr 	ucontrol->value.enumerated.item[0] = oval;
279ce71bfd1SAndreas Mohr 	return 0;
280ce71bfd1SAndreas Mohr }
281ce71bfd1SAndreas Mohr 
282ce71bfd1SAndreas Mohr static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
283ce71bfd1SAndreas Mohr 				struct snd_ctl_elem_value *ucontrol)
284ce71bfd1SAndreas Mohr {
285ce71bfd1SAndreas Mohr 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
286ce71bfd1SAndreas Mohr 	unsigned long flags;
287ce71bfd1SAndreas Mohr 	int change;
288ce71bfd1SAndreas Mohr 	unsigned char nval, oval;
289ce71bfd1SAndreas Mohr 
290ce71bfd1SAndreas Mohr 	if (ucontrol->value.enumerated.item[0] > 2)
291ce71bfd1SAndreas Mohr 		return -EINVAL;
292ce71bfd1SAndreas Mohr 	spin_lock_irqsave(&sb->mixer_lock, flags);
293ce71bfd1SAndreas Mohr 	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
294ce71bfd1SAndreas Mohr 
295ce71bfd1SAndreas Mohr 	nval = (oval & ~(3 << 6))
296ce71bfd1SAndreas Mohr 	     | (ucontrol->value.enumerated.item[0] << 6);
297ce71bfd1SAndreas Mohr 	change = nval != oval;
298ce71bfd1SAndreas Mohr 	if (change)
299ce71bfd1SAndreas Mohr 		snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);
300ce71bfd1SAndreas Mohr 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
301ce71bfd1SAndreas Mohr 	return change;
302ce71bfd1SAndreas Mohr }
303ce71bfd1SAndreas Mohr 
304ce71bfd1SAndreas Mohr /*
3051da177e4SLinus Torvalds  * SBPRO input multiplexer
3061da177e4SLinus Torvalds  */
3071da177e4SLinus Torvalds 
308029d64b0STakashi Iwai static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
3091da177e4SLinus Torvalds {
3106b9e1288STakashi Iwai 	static const char * const texts[3] = {
3111da177e4SLinus Torvalds 		"Mic", "CD", "Line"
3121da177e4SLinus Torvalds 	};
3131da177e4SLinus Torvalds 
3146b9e1288STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 3, texts);
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 
318029d64b0STakashi Iwai static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3191da177e4SLinus Torvalds {
320029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3211da177e4SLinus Torvalds 	unsigned long flags;
3221da177e4SLinus Torvalds 	unsigned char oval;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3251da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
3261da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3271da177e4SLinus Torvalds 	switch ((oval >> 0x01) & 0x03) {
3281da177e4SLinus Torvalds 	case SB_DSP_MIXS_CD:
3291da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 1;
3301da177e4SLinus Torvalds 		break;
3311da177e4SLinus Torvalds 	case SB_DSP_MIXS_LINE:
3321da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 2;
3331da177e4SLinus Torvalds 		break;
3341da177e4SLinus Torvalds 	default:
3351da177e4SLinus Torvalds 		ucontrol->value.enumerated.item[0] = 0;
3361da177e4SLinus Torvalds 		break;
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds 	return 0;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
341029d64b0STakashi Iwai static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3421da177e4SLinus Torvalds {
343029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3441da177e4SLinus Torvalds 	unsigned long flags;
3451da177e4SLinus Torvalds 	int change;
3461da177e4SLinus Torvalds 	unsigned char nval, oval;
3471da177e4SLinus Torvalds 
3481da177e4SLinus Torvalds 	if (ucontrol->value.enumerated.item[0] > 2)
3491da177e4SLinus Torvalds 		return -EINVAL;
3501da177e4SLinus Torvalds 	switch (ucontrol->value.enumerated.item[0]) {
3511da177e4SLinus Torvalds 	case 1:
3521da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_CD;
3531da177e4SLinus Torvalds 		break;
3541da177e4SLinus Torvalds 	case 2:
3551da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_LINE;
3561da177e4SLinus Torvalds 		break;
3571da177e4SLinus Torvalds 	default:
3581da177e4SLinus Torvalds 		nval = SB_DSP_MIXS_MIC;
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 	nval <<= 1;
3611da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3621da177e4SLinus Torvalds 	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
3631da177e4SLinus Torvalds 	nval |= oval & ~0x06;
3641da177e4SLinus Torvalds 	change = nval != oval;
3651da177e4SLinus Torvalds 	if (change)
3661da177e4SLinus Torvalds 		snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
3671da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3681da177e4SLinus Torvalds 	return change;
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds /*
3721da177e4SLinus Torvalds  * SB16 input switch
3731da177e4SLinus Torvalds  */
3741da177e4SLinus Torvalds 
375029d64b0STakashi Iwai static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
3761da177e4SLinus Torvalds {
3771da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3781da177e4SLinus Torvalds 	uinfo->count = 4;
3791da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
3801da177e4SLinus Torvalds 	uinfo->value.integer.max = 1;
3811da177e4SLinus Torvalds 	return 0;
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds 
384029d64b0STakashi Iwai static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3851da177e4SLinus Torvalds {
386029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
3871da177e4SLinus Torvalds 	unsigned long flags;
3881da177e4SLinus Torvalds 	int reg1 = kcontrol->private_value & 0xff;
3891da177e4SLinus Torvalds 	int reg2 = (kcontrol->private_value >> 8) & 0xff;
3901da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
3911da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
3921da177e4SLinus Torvalds 	unsigned char val1, val2;
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
3951da177e4SLinus Torvalds 	val1 = snd_sbmixer_read(sb, reg1);
3961da177e4SLinus Torvalds 	val2 = snd_sbmixer_read(sb, reg2);
3971da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
3981da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
3991da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
4001da177e4SLinus Torvalds 	ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
4011da177e4SLinus Torvalds 	ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
4021da177e4SLinus Torvalds 	return 0;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds 
405029d64b0STakashi Iwai static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4061da177e4SLinus Torvalds {
407029d64b0STakashi Iwai 	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
4081da177e4SLinus Torvalds 	unsigned long flags;
4091da177e4SLinus Torvalds 	int reg1 = kcontrol->private_value & 0xff;
4101da177e4SLinus Torvalds 	int reg2 = (kcontrol->private_value >> 8) & 0xff;
4111da177e4SLinus Torvalds 	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
4121da177e4SLinus Torvalds 	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
4131da177e4SLinus Torvalds 	int change;
4141da177e4SLinus Torvalds 	unsigned char val1, val2, oval1, oval2;
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds 	spin_lock_irqsave(&sb->mixer_lock, flags);
4171da177e4SLinus Torvalds 	oval1 = snd_sbmixer_read(sb, reg1);
4181da177e4SLinus Torvalds 	oval2 = snd_sbmixer_read(sb, reg2);
4191da177e4SLinus Torvalds 	val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
4201da177e4SLinus Torvalds 	val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
4211da177e4SLinus Torvalds 	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
4221da177e4SLinus Torvalds 	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
4231da177e4SLinus Torvalds 	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
4241da177e4SLinus Torvalds 	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
4251da177e4SLinus Torvalds 	change = val1 != oval1 || val2 != oval2;
4261da177e4SLinus Torvalds 	if (change) {
4271da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg1, val1);
4281da177e4SLinus Torvalds 		snd_sbmixer_write(sb, reg2, val2);
4291da177e4SLinus Torvalds 	}
4301da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sb->mixer_lock, flags);
4311da177e4SLinus Torvalds 	return change;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds /*
4361da177e4SLinus Torvalds  */
4371da177e4SLinus Torvalds /*
4381da177e4SLinus Torvalds  */
439029d64b0STakashi Iwai int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
4401da177e4SLinus Torvalds {
441029d64b0STakashi Iwai 	static struct snd_kcontrol_new newctls[] = {
4421da177e4SLinus Torvalds 		[SB_MIX_SINGLE] = {
4431da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4441da177e4SLinus Torvalds 			.info = snd_sbmixer_info_single,
4451da177e4SLinus Torvalds 			.get = snd_sbmixer_get_single,
4461da177e4SLinus Torvalds 			.put = snd_sbmixer_put_single,
4471da177e4SLinus Torvalds 		},
4481da177e4SLinus Torvalds 		[SB_MIX_DOUBLE] = {
4491da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4501da177e4SLinus Torvalds 			.info = snd_sbmixer_info_double,
4511da177e4SLinus Torvalds 			.get = snd_sbmixer_get_double,
4521da177e4SLinus Torvalds 			.put = snd_sbmixer_put_double,
4531da177e4SLinus Torvalds 		},
4541da177e4SLinus Torvalds 		[SB_MIX_INPUT_SW] = {
4551da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4561da177e4SLinus Torvalds 			.info = snd_sb16mixer_info_input_sw,
4571da177e4SLinus Torvalds 			.get = snd_sb16mixer_get_input_sw,
4581da177e4SLinus Torvalds 			.put = snd_sb16mixer_put_input_sw,
4591da177e4SLinus Torvalds 		},
4601da177e4SLinus Torvalds 		[SB_MIX_CAPTURE_PRO] = {
4611da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4621da177e4SLinus Torvalds 			.info = snd_sb8mixer_info_mux,
4631da177e4SLinus Torvalds 			.get = snd_sb8mixer_get_mux,
4641da177e4SLinus Torvalds 			.put = snd_sb8mixer_put_mux,
4651da177e4SLinus Torvalds 		},
4661da177e4SLinus Torvalds 		[SB_MIX_CAPTURE_DT019X] = {
4671da177e4SLinus Torvalds 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4681da177e4SLinus Torvalds 			.info = snd_dt019x_input_sw_info,
4691da177e4SLinus Torvalds 			.get = snd_dt019x_input_sw_get,
4701da177e4SLinus Torvalds 			.put = snd_dt019x_input_sw_put,
4711da177e4SLinus Torvalds 		},
472ce71bfd1SAndreas Mohr 		[SB_MIX_MONO_CAPTURE_ALS4K] = {
473ce71bfd1SAndreas Mohr 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
474ce71bfd1SAndreas Mohr 			.info = snd_als4k_mono_capture_route_info,
475ce71bfd1SAndreas Mohr 			.get = snd_als4k_mono_capture_route_get,
476ce71bfd1SAndreas Mohr 			.put = snd_als4k_mono_capture_route_put,
477ce71bfd1SAndreas Mohr 		},
4781da177e4SLinus Torvalds 	};
479029d64b0STakashi Iwai 	struct snd_kcontrol *ctl;
4801da177e4SLinus Torvalds 	int err;
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 	ctl = snd_ctl_new1(&newctls[type], chip);
4831da177e4SLinus Torvalds 	if (! ctl)
4841da177e4SLinus Torvalds 		return -ENOMEM;
4851da177e4SLinus Torvalds 	strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
4861da177e4SLinus Torvalds 	ctl->id.index = index;
4871da177e4SLinus Torvalds 	ctl->private_value = value;
488bcc54f9aSDave Jones 	if ((err = snd_ctl_add(chip->card, ctl)) < 0)
4891da177e4SLinus Torvalds 		return err;
4901da177e4SLinus Torvalds 	return 0;
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds /*
4941da177e4SLinus Torvalds  * SB 2.0 specific mixer elements
4951da177e4SLinus Torvalds  */
4961da177e4SLinus Torvalds 
49774c2b45bSKrzysztof Helt static struct sbmix_elem snd_sb20_controls[] = {
49874c2b45bSKrzysztof Helt 	SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
49974c2b45bSKrzysztof Helt 	SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
50074c2b45bSKrzysztof Helt 	SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
50174c2b45bSKrzysztof Helt 	SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
5021da177e4SLinus Torvalds };
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds static unsigned char snd_sb20_init_values[][2] = {
5051da177e4SLinus Torvalds 	{ SB_DSP20_MASTER_DEV, 0 },
5061da177e4SLinus Torvalds 	{ SB_DSP20_FM_DEV, 0 },
5071da177e4SLinus Torvalds };
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds /*
5101da177e4SLinus Torvalds  * SB Pro specific mixer elements
5111da177e4SLinus Torvalds  */
51274c2b45bSKrzysztof Helt static struct sbmix_elem snd_sbpro_controls[] = {
51374c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
51474c2b45bSKrzysztof Helt 		  SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
51574c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
51674c2b45bSKrzysztof Helt 		  SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),
51774c2b45bSKrzysztof Helt 	SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),
51874c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
51974c2b45bSKrzysztof Helt 		  SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),
52074c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),
52174c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
52274c2b45bSKrzysztof Helt 		  SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
52374c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
5241da177e4SLinus Torvalds 	{
5251da177e4SLinus Torvalds 		.name = "Capture Source",
5261da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_PRO
52774c2b45bSKrzysztof Helt 	},
52874c2b45bSKrzysztof Helt 	SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
52974c2b45bSKrzysztof Helt 	SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
5301da177e4SLinus Torvalds };
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds static unsigned char snd_sbpro_init_values[][2] = {
5331da177e4SLinus Torvalds 	{ SB_DSP_MASTER_DEV, 0 },
5341da177e4SLinus Torvalds 	{ SB_DSP_PCM_DEV, 0 },
5351da177e4SLinus Torvalds 	{ SB_DSP_FM_DEV, 0 },
5361da177e4SLinus Torvalds };
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds /*
5391da177e4SLinus Torvalds  * SB16 specific mixer elements
5401da177e4SLinus Torvalds  */
54174c2b45bSKrzysztof Helt static struct sbmix_elem snd_sb16_controls[] = {
54274c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
54374c2b45bSKrzysztof Helt 		  SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
54474c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
54574c2b45bSKrzysztof Helt 		  SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),
54674c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Synth Capture Route",
54774c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),
54874c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
54974c2b45bSKrzysztof Helt 		  SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),
55074c2b45bSKrzysztof Helt 	SB16_INPUT_SW("CD Capture Route",
55174c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),
55274c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Switch",
55374c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
55474c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume",
55574c2b45bSKrzysztof Helt 		  SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),
55674c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Mic Capture Route",
55774c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0),
55874c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
55974c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
56074c2b45bSKrzysztof Helt 	SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
56174c2b45bSKrzysztof Helt 	SB_DOUBLE("Capture Volume",
56274c2b45bSKrzysztof Helt 		  SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
56374c2b45bSKrzysztof Helt 	SB_DOUBLE("Playback Volume",
56474c2b45bSKrzysztof Helt 		  SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
56574c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Line Capture Route",
56674c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),
56774c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Switch",
56874c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
56974c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
57074c2b45bSKrzysztof Helt 		  SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
57174c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
57274c2b45bSKrzysztof Helt 	SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),
57374c2b45bSKrzysztof Helt 	SB_DOUBLE("Tone Control - Bass",
57474c2b45bSKrzysztof Helt 		  SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
57574c2b45bSKrzysztof Helt 	SB_DOUBLE("Tone Control - Treble",
57674c2b45bSKrzysztof Helt 		  SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
5771da177e4SLinus Torvalds };
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds static unsigned char snd_sb16_init_values[][2] = {
5801da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
5811da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
5821da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
5831da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
5841da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
5851da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
5861da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
5871da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
5881da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
5891da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
5901da177e4SLinus Torvalds };
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds /*
5931da177e4SLinus Torvalds  * DT019x specific mixer elements
5941da177e4SLinus Torvalds  */
59574c2b45bSKrzysztof Helt static struct sbmix_elem snd_dt019x_controls[] = {
59674c2b45bSKrzysztof Helt 	/* ALS4000 below has some parts which we might be lacking,
59774c2b45bSKrzysztof Helt 	 * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
59874c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
59974c2b45bSKrzysztof Helt 		  SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15),
60074c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Switch",
60174c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
60274c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
60374c2b45bSKrzysztof Helt 		  SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15),
60474c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Switch",
60574c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
60674c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
60774c2b45bSKrzysztof Helt 		  SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15),
60874c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Switch",
60974c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
61074c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume",
61174c2b45bSKrzysztof Helt 		  SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15),
61274c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
61374c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7),
61474c2b45bSKrzysztof Helt 	SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7),
61574c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Switch",
61674c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
61774c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
61874c2b45bSKrzysztof Helt 		  SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15),
6191da177e4SLinus Torvalds 	{
6201da177e4SLinus Torvalds 		.name = "Capture Source",
6211da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_DT019X
62274c2b45bSKrzysztof Helt 	}
6231da177e4SLinus Torvalds };
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds static unsigned char snd_dt019x_init_values[][2] = {
6261da177e4SLinus Torvalds         { SB_DT019X_MASTER_DEV, 0 },
6271da177e4SLinus Torvalds         { SB_DT019X_PCM_DEV, 0 },
6281da177e4SLinus Torvalds         { SB_DT019X_SYNTH_DEV, 0 },
6291da177e4SLinus Torvalds         { SB_DT019X_CD_DEV, 0 },
6301da177e4SLinus Torvalds         { SB_DT019X_MIC_DEV, 0 },	/* Includes PC-speaker in high nibble */
6311da177e4SLinus Torvalds         { SB_DT019X_LINE_DEV, 0 },
6321da177e4SLinus Torvalds         { SB_DSP4_OUTPUT_SW, 0 },
6331da177e4SLinus Torvalds         { SB_DT019X_OUTPUT_SW2, 0 },
6341da177e4SLinus Torvalds         { SB_DT019X_CAPTURE_SW, 0x06 },
6351da177e4SLinus Torvalds };
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds /*
6381da177e4SLinus Torvalds  * ALS4000 specific mixer elements
6391da177e4SLinus Torvalds  */
64074c2b45bSKrzysztof Helt static struct sbmix_elem snd_als4000_controls[] = {
64174c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Switch",
64274c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
64374c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Switch",
64474c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
64574c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03),
64674c2b45bSKrzysztof Helt 	SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1),
64774c2b45bSKrzysztof Helt 	{
648ce71bfd1SAndreas Mohr 		.name = "Master Mono Capture Route",
649ce71bfd1SAndreas Mohr 		.type = SB_MIX_MONO_CAPTURE_ALS4K
65074c2b45bSKrzysztof Helt 	},
65174c2b45bSKrzysztof Helt 	SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1),
65274c2b45bSKrzysztof Helt 	SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01),
65374c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01),
654ce71bfd1SAndreas Mohr 	SB_SINGLE("Digital Loopback Switch",
65574c2b45bSKrzysztof Helt 		  SB_ALS4000_CR3_CONFIGURATION, 7, 0x01),
656ba7301c7SAndreas Mohr 	/* FIXME: functionality of 3D controls might be swapped, I didn't find
657ba7301c7SAndreas Mohr 	 * a description of how to identify what is supposed to be what */
65874c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07),
659ba7301c7SAndreas Mohr 	/* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
66074c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03),
661ba7301c7SAndreas Mohr 	/* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
662ba7301c7SAndreas Mohr 	 * but what ALSA 3D attribute is that actually? "Center", "Depth",
663ba7301c7SAndreas Mohr 	 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
66474c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f),
66574c2b45bSKrzysztof Helt 	SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01),
666ce71bfd1SAndreas Mohr 	SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",
66774c2b45bSKrzysztof Helt 		  SB_ALS4000_FMDAC, 5, 0x01),
66844456d37SOlaf Hering #ifdef NOT_AVAILABLE
66974c2b45bSKrzysztof Helt 	SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01),
67074c2b45bSKrzysztof Helt 	SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f),
6711da177e4SLinus Torvalds #endif
6721da177e4SLinus Torvalds };
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds static unsigned char snd_als4000_init_values[][2] = {
6751da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
6761da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
6771da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
6781da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
6791da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
6801da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
6811da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
6821da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
6831da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
6841da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
6851da177e4SLinus Torvalds 	{ SB_DT019X_OUTPUT_SW2, 0 },
6861da177e4SLinus Torvalds 	{ SB_ALS4000_MIC_IN_GAIN, 0 },
6871da177e4SLinus Torvalds };
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds /*
6901da177e4SLinus Torvalds  */
691029d64b0STakashi Iwai static int snd_sbmixer_init(struct snd_sb *chip,
69274c2b45bSKrzysztof Helt 			    struct sbmix_elem *controls,
6931da177e4SLinus Torvalds 			    int controls_count,
6941da177e4SLinus Torvalds 			    unsigned char map[][2],
6951da177e4SLinus Torvalds 			    int map_count,
6961da177e4SLinus Torvalds 			    char *name)
6971da177e4SLinus Torvalds {
6981da177e4SLinus Torvalds 	unsigned long flags;
699029d64b0STakashi Iwai 	struct snd_card *card = chip->card;
7001da177e4SLinus Torvalds 	int idx, err;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	/* mixer reset */
7031da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->mixer_lock, flags);
7041da177e4SLinus Torvalds 	snd_sbmixer_write(chip, 0x00, 0x00);
7051da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->mixer_lock, flags);
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	/* mute and zero volume channels */
7081da177e4SLinus Torvalds 	for (idx = 0; idx < map_count; idx++) {
7091da177e4SLinus Torvalds 		spin_lock_irqsave(&chip->mixer_lock, flags);
7101da177e4SLinus Torvalds 		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
7111da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->mixer_lock, flags);
7121da177e4SLinus Torvalds 	}
7131da177e4SLinus Torvalds 
7141da177e4SLinus Torvalds 	for (idx = 0; idx < controls_count; idx++) {
71574c2b45bSKrzysztof Helt 		err = snd_sbmixer_add_ctl_elem(chip, &controls[idx]);
71674c2b45bSKrzysztof Helt 		if (err < 0)
7171da177e4SLinus Torvalds 			return err;
7181da177e4SLinus Torvalds 	}
7191da177e4SLinus Torvalds 	snd_component_add(card, name);
7201da177e4SLinus Torvalds 	strcpy(card->mixername, name);
7211da177e4SLinus Torvalds 	return 0;
7221da177e4SLinus Torvalds }
7231da177e4SLinus Torvalds 
724029d64b0STakashi Iwai int snd_sbmixer_new(struct snd_sb *chip)
7251da177e4SLinus Torvalds {
726029d64b0STakashi Iwai 	struct snd_card *card;
7271da177e4SLinus Torvalds 	int err;
7281da177e4SLinus Torvalds 
729622207dcSTakashi Iwai 	if (snd_BUG_ON(!chip || !chip->card))
730622207dcSTakashi Iwai 		return -EINVAL;
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds 	card = chip->card;
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds 	switch (chip->hardware) {
7351da177e4SLinus Torvalds 	case SB_HW_10:
7361da177e4SLinus Torvalds 		return 0; /* no mixer chip on SB1.x */
7371da177e4SLinus Torvalds 	case SB_HW_20:
7381da177e4SLinus Torvalds 	case SB_HW_201:
7391da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7401da177e4SLinus Torvalds 					    snd_sb20_controls,
7411da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb20_controls),
7421da177e4SLinus Torvalds 					    snd_sb20_init_values,
7431da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb20_init_values),
7441da177e4SLinus Torvalds 					    "CTL1335")) < 0)
7451da177e4SLinus Torvalds 			return err;
7461da177e4SLinus Torvalds 		break;
7471da177e4SLinus Torvalds 	case SB_HW_PRO:
748ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
7491da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7501da177e4SLinus Torvalds 					    snd_sbpro_controls,
7511da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sbpro_controls),
7521da177e4SLinus Torvalds 					    snd_sbpro_init_values,
7531da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sbpro_init_values),
7541da177e4SLinus Torvalds 					    "CTL1345")) < 0)
7551da177e4SLinus Torvalds 			return err;
7561da177e4SLinus Torvalds 		break;
7571da177e4SLinus Torvalds 	case SB_HW_16:
7581da177e4SLinus Torvalds 	case SB_HW_ALS100:
759621887aeSTakashi Iwai 	case SB_HW_CS5530:
7601da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7611da177e4SLinus Torvalds 					    snd_sb16_controls,
7621da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb16_controls),
7631da177e4SLinus Torvalds 					    snd_sb16_init_values,
7641da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_sb16_init_values),
7651da177e4SLinus Torvalds 					    "CTL1745")) < 0)
7661da177e4SLinus Torvalds 			return err;
7671da177e4SLinus Torvalds 		break;
7681da177e4SLinus Torvalds 	case SB_HW_ALS4000:
76974c2b45bSKrzysztof Helt 		/* use only the first 16 controls from SB16 */
77074c2b45bSKrzysztof Helt 		err = snd_sbmixer_init(chip,
77174c2b45bSKrzysztof Helt 					snd_sb16_controls,
77274c2b45bSKrzysztof Helt 					16,
77374c2b45bSKrzysztof Helt 					snd_sb16_init_values,
77474c2b45bSKrzysztof Helt 					ARRAY_SIZE(snd_sb16_init_values),
77574c2b45bSKrzysztof Helt 					"ALS4000");
77674c2b45bSKrzysztof Helt 		if (err < 0)
77774c2b45bSKrzysztof Helt 			return err;
7781da177e4SLinus Torvalds 		if ((err = snd_sbmixer_init(chip,
7791da177e4SLinus Torvalds 					    snd_als4000_controls,
7801da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_als4000_controls),
7811da177e4SLinus Torvalds 					    snd_als4000_init_values,
7821da177e4SLinus Torvalds 					    ARRAY_SIZE(snd_als4000_init_values),
7831da177e4SLinus Torvalds 					    "ALS4000")) < 0)
7841da177e4SLinus Torvalds 			return err;
7851da177e4SLinus Torvalds 		break;
7861da177e4SLinus Torvalds 	case SB_HW_DT019X:
787665ebe92SDan Carpenter 		err = snd_sbmixer_init(chip,
7881da177e4SLinus Torvalds 				       snd_dt019x_controls,
7891da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_dt019x_controls),
7901da177e4SLinus Torvalds 				       snd_dt019x_init_values,
7911da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_dt019x_init_values),
792665ebe92SDan Carpenter 				       "DT019X");
793665ebe92SDan Carpenter 		if (err < 0)
794665ebe92SDan Carpenter 			return err;
7951da177e4SLinus Torvalds 		break;
7961da177e4SLinus Torvalds 	default:
7971da177e4SLinus Torvalds 		strcpy(card->mixername, "???");
7981da177e4SLinus Torvalds 	}
7991da177e4SLinus Torvalds 	return 0;
8001da177e4SLinus Torvalds }
8015bdb6a16STakashi Iwai 
8025bdb6a16STakashi Iwai #ifdef CONFIG_PM
8035bdb6a16STakashi Iwai static unsigned char sb20_saved_regs[] = {
8045bdb6a16STakashi Iwai 	SB_DSP20_MASTER_DEV,
8055bdb6a16STakashi Iwai 	SB_DSP20_PCM_DEV,
8065bdb6a16STakashi Iwai 	SB_DSP20_FM_DEV,
8075bdb6a16STakashi Iwai 	SB_DSP20_CD_DEV,
8085bdb6a16STakashi Iwai };
8095bdb6a16STakashi Iwai 
8105bdb6a16STakashi Iwai static unsigned char sbpro_saved_regs[] = {
8115bdb6a16STakashi Iwai 	SB_DSP_MASTER_DEV,
8125bdb6a16STakashi Iwai 	SB_DSP_PCM_DEV,
8135bdb6a16STakashi Iwai 	SB_DSP_PLAYBACK_FILT,
8145bdb6a16STakashi Iwai 	SB_DSP_FM_DEV,
8155bdb6a16STakashi Iwai 	SB_DSP_CD_DEV,
8165bdb6a16STakashi Iwai 	SB_DSP_LINE_DEV,
8175bdb6a16STakashi Iwai 	SB_DSP_MIC_DEV,
8185bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_SOURCE,
8195bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_FILT,
8205bdb6a16STakashi Iwai };
8215bdb6a16STakashi Iwai 
8225bdb6a16STakashi Iwai static unsigned char sb16_saved_regs[] = {
8235bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
8245bdb6a16STakashi Iwai 	SB_DSP4_3DSE,
8255bdb6a16STakashi Iwai 	SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
8265bdb6a16STakashi Iwai 	SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,
8275bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
8285bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
8295bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
8305bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8315bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
8325bdb6a16STakashi Iwai 	SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
8335bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
8345bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
8355bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
8365bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
8375bdb6a16STakashi Iwai 	SB_DSP4_MIC_AGC
8385bdb6a16STakashi Iwai };
8395bdb6a16STakashi Iwai 
8405bdb6a16STakashi Iwai static unsigned char dt019x_saved_regs[] = {
8415bdb6a16STakashi Iwai 	SB_DT019X_MASTER_DEV,
8425bdb6a16STakashi Iwai 	SB_DT019X_PCM_DEV,
8435bdb6a16STakashi Iwai 	SB_DT019X_SYNTH_DEV,
8445bdb6a16STakashi Iwai 	SB_DT019X_CD_DEV,
8455bdb6a16STakashi Iwai 	SB_DT019X_MIC_DEV,
8465bdb6a16STakashi Iwai 	SB_DT019X_SPKR_DEV,
8475bdb6a16STakashi Iwai 	SB_DT019X_LINE_DEV,
8485bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8495bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
8505bdb6a16STakashi Iwai 	SB_DT019X_CAPTURE_SW,
8515bdb6a16STakashi Iwai };
8525bdb6a16STakashi Iwai 
8535bdb6a16STakashi Iwai static unsigned char als4000_saved_regs[] = {
854ce71bfd1SAndreas Mohr 	/* please verify in dsheet whether regs to be added
855ce71bfd1SAndreas Mohr 	   are actually real H/W or just dummy */
8565bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
8575bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8585bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
8595bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
8605bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
8615bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
8625bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
8635bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
8645bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
8655bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
8665bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
8675bdb6a16STakashi Iwai 	SB_ALS4000_MONO_IO_CTRL,
8685bdb6a16STakashi Iwai 	SB_ALS4000_MIC_IN_GAIN,
869ce71bfd1SAndreas Mohr 	SB_ALS4000_FMDAC,
8705bdb6a16STakashi Iwai 	SB_ALS4000_3D_SND_FX,
8715bdb6a16STakashi Iwai 	SB_ALS4000_3D_TIME_DELAY,
872ce71bfd1SAndreas Mohr 	SB_ALS4000_CR3_CONFIGURATION,
8735bdb6a16STakashi Iwai };
8745bdb6a16STakashi Iwai 
8755bdb6a16STakashi Iwai static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
8765bdb6a16STakashi Iwai {
8775bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
878622207dcSTakashi Iwai 	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
879622207dcSTakashi Iwai 		return;
8805bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
8815bdb6a16STakashi Iwai 		*val++ = snd_sbmixer_read(chip, *regs++);
8825bdb6a16STakashi Iwai }
8835bdb6a16STakashi Iwai 
8845bdb6a16STakashi Iwai static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
8855bdb6a16STakashi Iwai {
8865bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
887622207dcSTakashi Iwai 	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
888622207dcSTakashi Iwai 		return;
8895bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
8905bdb6a16STakashi Iwai 		snd_sbmixer_write(chip, *regs++, *val++);
8915bdb6a16STakashi Iwai }
8925bdb6a16STakashi Iwai 
8935bdb6a16STakashi Iwai void snd_sbmixer_suspend(struct snd_sb *chip)
8945bdb6a16STakashi Iwai {
8955bdb6a16STakashi Iwai 	switch (chip->hardware) {
8965bdb6a16STakashi Iwai 	case SB_HW_20:
8975bdb6a16STakashi Iwai 	case SB_HW_201:
8985bdb6a16STakashi Iwai 		save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
8995bdb6a16STakashi Iwai 		break;
9005bdb6a16STakashi Iwai 	case SB_HW_PRO:
901ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
9025bdb6a16STakashi Iwai 		save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9035bdb6a16STakashi Iwai 		break;
9045bdb6a16STakashi Iwai 	case SB_HW_16:
9055bdb6a16STakashi Iwai 	case SB_HW_ALS100:
906621887aeSTakashi Iwai 	case SB_HW_CS5530:
9075bdb6a16STakashi Iwai 		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9085bdb6a16STakashi Iwai 		break;
9095bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9105bdb6a16STakashi Iwai 		save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9115bdb6a16STakashi Iwai 		break;
9125bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9135bdb6a16STakashi Iwai 		save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9145bdb6a16STakashi Iwai 		break;
9155bdb6a16STakashi Iwai 	default:
9165bdb6a16STakashi Iwai 		break;
9175bdb6a16STakashi Iwai 	}
9185bdb6a16STakashi Iwai }
9195bdb6a16STakashi Iwai 
9205bdb6a16STakashi Iwai void snd_sbmixer_resume(struct snd_sb *chip)
9215bdb6a16STakashi Iwai {
9225bdb6a16STakashi Iwai 	switch (chip->hardware) {
9235bdb6a16STakashi Iwai 	case SB_HW_20:
9245bdb6a16STakashi Iwai 	case SB_HW_201:
9255bdb6a16STakashi Iwai 		restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
9265bdb6a16STakashi Iwai 		break;
9275bdb6a16STakashi Iwai 	case SB_HW_PRO:
928ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
9295bdb6a16STakashi Iwai 		restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9305bdb6a16STakashi Iwai 		break;
9315bdb6a16STakashi Iwai 	case SB_HW_16:
9325bdb6a16STakashi Iwai 	case SB_HW_ALS100:
933621887aeSTakashi Iwai 	case SB_HW_CS5530:
9345bdb6a16STakashi Iwai 		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9355bdb6a16STakashi Iwai 		break;
9365bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9375bdb6a16STakashi Iwai 		restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9385bdb6a16STakashi Iwai 		break;
9395bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9405bdb6a16STakashi Iwai 		restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9415bdb6a16STakashi Iwai 		break;
9425bdb6a16STakashi Iwai 	default:
9435bdb6a16STakashi Iwai 		break;
9445bdb6a16STakashi Iwai 	}
9455bdb6a16STakashi Iwai }
9465bdb6a16STakashi Iwai #endif
947