xref: /openbmc/linux/sound/isa/sb/sb_mixer.c (revision 10dc8ad5)
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 
snd_sbmixer_write(struct snd_sb * chip,unsigned char reg,unsigned char data)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 
snd_sbmixer_read(struct snd_sb * chip,unsigned char reg)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 
snd_sbmixer_info_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_sbmixer_get_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_sbmixer_put_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_sbmixer_info_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_sbmixer_get_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_sbmixer_put_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_dt019x_input_sw_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_dt019x_input_sw_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_dt019x_input_sw_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_als4k_mono_capture_route_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_als4k_mono_capture_route_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_als4k_mono_capture_route_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_sb8mixer_info_mux(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_sb8mixer_get_mux(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_sb8mixer_put_mux(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_sb16mixer_info_input_sw(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_sb16mixer_get_input_sw(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_sb16mixer_put_input_sw(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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  */
snd_sbmixer_add_ctl(struct snd_sb * chip,const char * name,int index,int type,unsigned long value)439029d64b0STakashi Iwai int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
4401da177e4SLinus Torvalds {
441fdd1f6fdSTakashi Iwai 	static const 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;
48575b1a8f9SJoe Perches 	strscpy(ctl->id.name, name, sizeof(ctl->id.name));
4861da177e4SLinus Torvalds 	ctl->id.index = index;
4871da177e4SLinus Torvalds 	ctl->private_value = value;
488*10dc8ad5STakashi Iwai 	err = snd_ctl_add(chip->card, ctl);
489*10dc8ad5STakashi Iwai 	if (err < 0)
4901da177e4SLinus Torvalds 		return err;
4911da177e4SLinus Torvalds 	return 0;
4921da177e4SLinus Torvalds }
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds /*
4951da177e4SLinus Torvalds  * SB 2.0 specific mixer elements
4961da177e4SLinus Torvalds  */
4971da177e4SLinus Torvalds 
4982a076d0aSTakashi Iwai static const struct sbmix_elem snd_sb20_controls[] = {
49974c2b45bSKrzysztof Helt 	SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
50074c2b45bSKrzysztof Helt 	SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
50174c2b45bSKrzysztof Helt 	SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
50274c2b45bSKrzysztof Helt 	SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
5031da177e4SLinus Torvalds };
5041da177e4SLinus Torvalds 
5052a076d0aSTakashi Iwai static const unsigned char snd_sb20_init_values[][2] = {
5061da177e4SLinus Torvalds 	{ SB_DSP20_MASTER_DEV, 0 },
5071da177e4SLinus Torvalds 	{ SB_DSP20_FM_DEV, 0 },
5081da177e4SLinus Torvalds };
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds /*
5111da177e4SLinus Torvalds  * SB Pro specific mixer elements
5121da177e4SLinus Torvalds  */
5132a076d0aSTakashi Iwai static const struct sbmix_elem snd_sbpro_controls[] = {
51474c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
51574c2b45bSKrzysztof Helt 		  SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
51674c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
51774c2b45bSKrzysztof Helt 		  SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),
51874c2b45bSKrzysztof Helt 	SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),
51974c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
52074c2b45bSKrzysztof Helt 		  SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),
52174c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),
52274c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
52374c2b45bSKrzysztof Helt 		  SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
52474c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
5251da177e4SLinus Torvalds 	{
5261da177e4SLinus Torvalds 		.name = "Capture Source",
5271da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_PRO
52874c2b45bSKrzysztof Helt 	},
52974c2b45bSKrzysztof Helt 	SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
53074c2b45bSKrzysztof Helt 	SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
5311da177e4SLinus Torvalds };
5321da177e4SLinus Torvalds 
5332a076d0aSTakashi Iwai static const unsigned char snd_sbpro_init_values[][2] = {
5341da177e4SLinus Torvalds 	{ SB_DSP_MASTER_DEV, 0 },
5351da177e4SLinus Torvalds 	{ SB_DSP_PCM_DEV, 0 },
5361da177e4SLinus Torvalds 	{ SB_DSP_FM_DEV, 0 },
5371da177e4SLinus Torvalds };
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds /*
5401da177e4SLinus Torvalds  * SB16 specific mixer elements
5411da177e4SLinus Torvalds  */
5422a076d0aSTakashi Iwai static const struct sbmix_elem snd_sb16_controls[] = {
54374c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
54474c2b45bSKrzysztof Helt 		  SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
54574c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
54674c2b45bSKrzysztof Helt 		  SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),
54774c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Synth Capture Route",
54874c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),
54974c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
55074c2b45bSKrzysztof Helt 		  SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),
55174c2b45bSKrzysztof Helt 	SB16_INPUT_SW("CD Capture Route",
55274c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),
55374c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Switch",
55474c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
55574c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume",
55674c2b45bSKrzysztof Helt 		  SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),
55774c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Mic Capture Route",
55874c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0),
55974c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
56074c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
56174c2b45bSKrzysztof Helt 	SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
56274c2b45bSKrzysztof Helt 	SB_DOUBLE("Capture Volume",
56374c2b45bSKrzysztof Helt 		  SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
56474c2b45bSKrzysztof Helt 	SB_DOUBLE("Playback Volume",
56574c2b45bSKrzysztof Helt 		  SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
56674c2b45bSKrzysztof Helt 	SB16_INPUT_SW("Line Capture Route",
56774c2b45bSKrzysztof Helt 		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),
56874c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Switch",
56974c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
57074c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
57174c2b45bSKrzysztof Helt 		  SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
57274c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
57374c2b45bSKrzysztof Helt 	SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),
57474c2b45bSKrzysztof Helt 	SB_DOUBLE("Tone Control - Bass",
57574c2b45bSKrzysztof Helt 		  SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
57674c2b45bSKrzysztof Helt 	SB_DOUBLE("Tone Control - Treble",
57774c2b45bSKrzysztof Helt 		  SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
5781da177e4SLinus Torvalds };
5791da177e4SLinus Torvalds 
5802a076d0aSTakashi Iwai static const unsigned char snd_sb16_init_values[][2] = {
5811da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
5821da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
5831da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
5841da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
5851da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
5861da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
5871da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
5881da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
5891da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
5901da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
5911da177e4SLinus Torvalds };
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds /*
5941da177e4SLinus Torvalds  * DT019x specific mixer elements
5951da177e4SLinus Torvalds  */
5962a076d0aSTakashi Iwai static const struct sbmix_elem snd_dt019x_controls[] = {
59774c2b45bSKrzysztof Helt 	/* ALS4000 below has some parts which we might be lacking,
59874c2b45bSKrzysztof Helt 	 * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
59974c2b45bSKrzysztof Helt 	SB_DOUBLE("Master Playback Volume",
60074c2b45bSKrzysztof Helt 		  SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15),
60174c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Switch",
60274c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
60374c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Volume",
60474c2b45bSKrzysztof Helt 		  SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15),
60574c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Switch",
60674c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
60774c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Volume",
60874c2b45bSKrzysztof Helt 		  SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15),
60974c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Switch",
61074c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
61174c2b45bSKrzysztof Helt 	SB_DOUBLE("CD Playback Volume",
61274c2b45bSKrzysztof Helt 		  SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15),
61374c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
61474c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7),
61574c2b45bSKrzysztof Helt 	SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7),
61674c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Switch",
61774c2b45bSKrzysztof Helt 		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
61874c2b45bSKrzysztof Helt 	SB_DOUBLE("Line Playback Volume",
61974c2b45bSKrzysztof Helt 		  SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15),
6201da177e4SLinus Torvalds 	{
6211da177e4SLinus Torvalds 		.name = "Capture Source",
6221da177e4SLinus Torvalds 		.type = SB_MIX_CAPTURE_DT019X
62374c2b45bSKrzysztof Helt 	}
6241da177e4SLinus Torvalds };
6251da177e4SLinus Torvalds 
6262a076d0aSTakashi Iwai static const unsigned char snd_dt019x_init_values[][2] = {
6271da177e4SLinus Torvalds         { SB_DT019X_MASTER_DEV, 0 },
6281da177e4SLinus Torvalds         { SB_DT019X_PCM_DEV, 0 },
6291da177e4SLinus Torvalds         { SB_DT019X_SYNTH_DEV, 0 },
6301da177e4SLinus Torvalds         { SB_DT019X_CD_DEV, 0 },
6311da177e4SLinus Torvalds         { SB_DT019X_MIC_DEV, 0 },	/* Includes PC-speaker in high nibble */
6321da177e4SLinus Torvalds         { SB_DT019X_LINE_DEV, 0 },
6331da177e4SLinus Torvalds         { SB_DSP4_OUTPUT_SW, 0 },
6341da177e4SLinus Torvalds         { SB_DT019X_OUTPUT_SW2, 0 },
6351da177e4SLinus Torvalds         { SB_DT019X_CAPTURE_SW, 0x06 },
6361da177e4SLinus Torvalds };
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds /*
6391da177e4SLinus Torvalds  * ALS4000 specific mixer elements
6401da177e4SLinus Torvalds  */
6412a076d0aSTakashi Iwai static const struct sbmix_elem snd_als4000_controls[] = {
64274c2b45bSKrzysztof Helt 	SB_DOUBLE("PCM Playback Switch",
64374c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
64474c2b45bSKrzysztof Helt 	SB_DOUBLE("Synth Playback Switch",
64574c2b45bSKrzysztof Helt 		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
64674c2b45bSKrzysztof Helt 	SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03),
64774c2b45bSKrzysztof Helt 	SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1),
64874c2b45bSKrzysztof Helt 	{
649ce71bfd1SAndreas Mohr 		.name = "Master Mono Capture Route",
650ce71bfd1SAndreas Mohr 		.type = SB_MIX_MONO_CAPTURE_ALS4K
65174c2b45bSKrzysztof Helt 	},
65274c2b45bSKrzysztof Helt 	SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1),
65374c2b45bSKrzysztof Helt 	SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01),
65474c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01),
655ce71bfd1SAndreas Mohr 	SB_SINGLE("Digital Loopback Switch",
65674c2b45bSKrzysztof Helt 		  SB_ALS4000_CR3_CONFIGURATION, 7, 0x01),
657ba7301c7SAndreas Mohr 	/* FIXME: functionality of 3D controls might be swapped, I didn't find
658ba7301c7SAndreas Mohr 	 * a description of how to identify what is supposed to be what */
65974c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07),
660ba7301c7SAndreas Mohr 	/* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
66174c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03),
662ba7301c7SAndreas Mohr 	/* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
663ba7301c7SAndreas Mohr 	 * but what ALSA 3D attribute is that actually? "Center", "Depth",
664ba7301c7SAndreas Mohr 	 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
66574c2b45bSKrzysztof Helt 	SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f),
66674c2b45bSKrzysztof Helt 	SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01),
667ce71bfd1SAndreas Mohr 	SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",
66874c2b45bSKrzysztof Helt 		  SB_ALS4000_FMDAC, 5, 0x01),
66944456d37SOlaf Hering #ifdef NOT_AVAILABLE
67074c2b45bSKrzysztof Helt 	SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01),
67174c2b45bSKrzysztof Helt 	SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f),
6721da177e4SLinus Torvalds #endif
6731da177e4SLinus Torvalds };
6741da177e4SLinus Torvalds 
6752a076d0aSTakashi Iwai static const unsigned char snd_als4000_init_values[][2] = {
6761da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 0, 0 },
6771da177e4SLinus Torvalds 	{ SB_DSP4_MASTER_DEV + 1, 0 },
6781da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 0, 0 },
6791da177e4SLinus Torvalds 	{ SB_DSP4_PCM_DEV + 1, 0 },
6801da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 0, 0 },
6811da177e4SLinus Torvalds 	{ SB_DSP4_SYNTH_DEV + 1, 0 },
6821da177e4SLinus Torvalds 	{ SB_DSP4_SPEAKER_DEV, 0 },
6831da177e4SLinus Torvalds 	{ SB_DSP4_OUTPUT_SW, 0 },
6841da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_LEFT, 0 },
6851da177e4SLinus Torvalds 	{ SB_DSP4_INPUT_RIGHT, 0 },
6861da177e4SLinus Torvalds 	{ SB_DT019X_OUTPUT_SW2, 0 },
6871da177e4SLinus Torvalds 	{ SB_ALS4000_MIC_IN_GAIN, 0 },
6881da177e4SLinus Torvalds };
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds /*
6911da177e4SLinus Torvalds  */
snd_sbmixer_init(struct snd_sb * chip,const struct sbmix_elem * controls,int controls_count,const unsigned char map[][2],int map_count,char * name)692029d64b0STakashi Iwai static int snd_sbmixer_init(struct snd_sb *chip,
6932a076d0aSTakashi Iwai 			    const struct sbmix_elem *controls,
6941da177e4SLinus Torvalds 			    int controls_count,
6952a076d0aSTakashi Iwai 			    const unsigned char map[][2],
6961da177e4SLinus Torvalds 			    int map_count,
6971da177e4SLinus Torvalds 			    char *name)
6981da177e4SLinus Torvalds {
6991da177e4SLinus Torvalds 	unsigned long flags;
700029d64b0STakashi Iwai 	struct snd_card *card = chip->card;
7011da177e4SLinus Torvalds 	int idx, err;
7021da177e4SLinus Torvalds 
7031da177e4SLinus Torvalds 	/* mixer reset */
7041da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->mixer_lock, flags);
7051da177e4SLinus Torvalds 	snd_sbmixer_write(chip, 0x00, 0x00);
7061da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->mixer_lock, flags);
7071da177e4SLinus Torvalds 
7081da177e4SLinus Torvalds 	/* mute and zero volume channels */
7091da177e4SLinus Torvalds 	for (idx = 0; idx < map_count; idx++) {
7101da177e4SLinus Torvalds 		spin_lock_irqsave(&chip->mixer_lock, flags);
7111da177e4SLinus Torvalds 		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
7121da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->mixer_lock, flags);
7131da177e4SLinus Torvalds 	}
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds 	for (idx = 0; idx < controls_count; idx++) {
71674c2b45bSKrzysztof Helt 		err = snd_sbmixer_add_ctl_elem(chip, &controls[idx]);
71774c2b45bSKrzysztof Helt 		if (err < 0)
7181da177e4SLinus Torvalds 			return err;
7191da177e4SLinus Torvalds 	}
7201da177e4SLinus Torvalds 	snd_component_add(card, name);
7211da177e4SLinus Torvalds 	strcpy(card->mixername, name);
7221da177e4SLinus Torvalds 	return 0;
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
snd_sbmixer_new(struct snd_sb * chip)725029d64b0STakashi Iwai int snd_sbmixer_new(struct snd_sb *chip)
7261da177e4SLinus Torvalds {
727029d64b0STakashi Iwai 	struct snd_card *card;
7281da177e4SLinus Torvalds 	int err;
7291da177e4SLinus Torvalds 
730622207dcSTakashi Iwai 	if (snd_BUG_ON(!chip || !chip->card))
731622207dcSTakashi Iwai 		return -EINVAL;
7321da177e4SLinus Torvalds 
7331da177e4SLinus Torvalds 	card = chip->card;
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 	switch (chip->hardware) {
7361da177e4SLinus Torvalds 	case SB_HW_10:
7371da177e4SLinus Torvalds 		return 0; /* no mixer chip on SB1.x */
7381da177e4SLinus Torvalds 	case SB_HW_20:
7391da177e4SLinus Torvalds 	case SB_HW_201:
740*10dc8ad5STakashi Iwai 		err = snd_sbmixer_init(chip,
7411da177e4SLinus Torvalds 				       snd_sb20_controls,
7421da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_sb20_controls),
7431da177e4SLinus Torvalds 				       snd_sb20_init_values,
7441da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_sb20_init_values),
745*10dc8ad5STakashi Iwai 				       "CTL1335");
746*10dc8ad5STakashi Iwai 		if (err < 0)
7471da177e4SLinus Torvalds 			return err;
7481da177e4SLinus Torvalds 		break;
7491da177e4SLinus Torvalds 	case SB_HW_PRO:
750ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
751*10dc8ad5STakashi Iwai 		err = snd_sbmixer_init(chip,
7521da177e4SLinus Torvalds 				       snd_sbpro_controls,
7531da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_sbpro_controls),
7541da177e4SLinus Torvalds 				       snd_sbpro_init_values,
7551da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_sbpro_init_values),
756*10dc8ad5STakashi Iwai 				       "CTL1345");
757*10dc8ad5STakashi Iwai 		if (err < 0)
7581da177e4SLinus Torvalds 			return err;
7591da177e4SLinus Torvalds 		break;
7601da177e4SLinus Torvalds 	case SB_HW_16:
7611da177e4SLinus Torvalds 	case SB_HW_ALS100:
762621887aeSTakashi Iwai 	case SB_HW_CS5530:
763*10dc8ad5STakashi Iwai 		err = snd_sbmixer_init(chip,
7641da177e4SLinus Torvalds 				       snd_sb16_controls,
7651da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_sb16_controls),
7661da177e4SLinus Torvalds 				       snd_sb16_init_values,
7671da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_sb16_init_values),
768*10dc8ad5STakashi Iwai 				       "CTL1745");
769*10dc8ad5STakashi Iwai 		if (err < 0)
7701da177e4SLinus Torvalds 			return err;
7711da177e4SLinus Torvalds 		break;
7721da177e4SLinus Torvalds 	case SB_HW_ALS4000:
77374c2b45bSKrzysztof Helt 		/* use only the first 16 controls from SB16 */
77474c2b45bSKrzysztof Helt 		err = snd_sbmixer_init(chip,
77574c2b45bSKrzysztof Helt 					snd_sb16_controls,
77674c2b45bSKrzysztof Helt 					16,
77774c2b45bSKrzysztof Helt 					snd_sb16_init_values,
77874c2b45bSKrzysztof Helt 					ARRAY_SIZE(snd_sb16_init_values),
77974c2b45bSKrzysztof Helt 					"ALS4000");
78074c2b45bSKrzysztof Helt 		if (err < 0)
78174c2b45bSKrzysztof Helt 			return err;
782*10dc8ad5STakashi Iwai 		err = snd_sbmixer_init(chip,
7831da177e4SLinus Torvalds 				       snd_als4000_controls,
7841da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_als4000_controls),
7851da177e4SLinus Torvalds 				       snd_als4000_init_values,
7861da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_als4000_init_values),
787*10dc8ad5STakashi Iwai 				       "ALS4000");
788*10dc8ad5STakashi Iwai 		if (err < 0)
7891da177e4SLinus Torvalds 			return err;
7901da177e4SLinus Torvalds 		break;
7911da177e4SLinus Torvalds 	case SB_HW_DT019X:
792665ebe92SDan Carpenter 		err = snd_sbmixer_init(chip,
7931da177e4SLinus Torvalds 				       snd_dt019x_controls,
7941da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_dt019x_controls),
7951da177e4SLinus Torvalds 				       snd_dt019x_init_values,
7961da177e4SLinus Torvalds 				       ARRAY_SIZE(snd_dt019x_init_values),
797665ebe92SDan Carpenter 				       "DT019X");
798665ebe92SDan Carpenter 		if (err < 0)
799665ebe92SDan Carpenter 			return err;
8001da177e4SLinus Torvalds 		break;
8011da177e4SLinus Torvalds 	default:
8021da177e4SLinus Torvalds 		strcpy(card->mixername, "???");
8031da177e4SLinus Torvalds 	}
8041da177e4SLinus Torvalds 	return 0;
8051da177e4SLinus Torvalds }
8065bdb6a16STakashi Iwai 
8075bdb6a16STakashi Iwai #ifdef CONFIG_PM
8082a076d0aSTakashi Iwai static const unsigned char sb20_saved_regs[] = {
8095bdb6a16STakashi Iwai 	SB_DSP20_MASTER_DEV,
8105bdb6a16STakashi Iwai 	SB_DSP20_PCM_DEV,
8115bdb6a16STakashi Iwai 	SB_DSP20_FM_DEV,
8125bdb6a16STakashi Iwai 	SB_DSP20_CD_DEV,
8135bdb6a16STakashi Iwai };
8145bdb6a16STakashi Iwai 
8152a076d0aSTakashi Iwai static const unsigned char sbpro_saved_regs[] = {
8165bdb6a16STakashi Iwai 	SB_DSP_MASTER_DEV,
8175bdb6a16STakashi Iwai 	SB_DSP_PCM_DEV,
8185bdb6a16STakashi Iwai 	SB_DSP_PLAYBACK_FILT,
8195bdb6a16STakashi Iwai 	SB_DSP_FM_DEV,
8205bdb6a16STakashi Iwai 	SB_DSP_CD_DEV,
8215bdb6a16STakashi Iwai 	SB_DSP_LINE_DEV,
8225bdb6a16STakashi Iwai 	SB_DSP_MIC_DEV,
8235bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_SOURCE,
8245bdb6a16STakashi Iwai 	SB_DSP_CAPTURE_FILT,
8255bdb6a16STakashi Iwai };
8265bdb6a16STakashi Iwai 
8272a076d0aSTakashi Iwai static const unsigned char sb16_saved_regs[] = {
8285bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
8295bdb6a16STakashi Iwai 	SB_DSP4_3DSE,
8305bdb6a16STakashi Iwai 	SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
8315bdb6a16STakashi Iwai 	SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,
8325bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
8335bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
8345bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
8355bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8365bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
8375bdb6a16STakashi Iwai 	SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
8385bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
8395bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
8405bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
8415bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
8425bdb6a16STakashi Iwai 	SB_DSP4_MIC_AGC
8435bdb6a16STakashi Iwai };
8445bdb6a16STakashi Iwai 
8452a076d0aSTakashi Iwai static const unsigned char dt019x_saved_regs[] = {
8465bdb6a16STakashi Iwai 	SB_DT019X_MASTER_DEV,
8475bdb6a16STakashi Iwai 	SB_DT019X_PCM_DEV,
8485bdb6a16STakashi Iwai 	SB_DT019X_SYNTH_DEV,
8495bdb6a16STakashi Iwai 	SB_DT019X_CD_DEV,
8505bdb6a16STakashi Iwai 	SB_DT019X_MIC_DEV,
8515bdb6a16STakashi Iwai 	SB_DT019X_SPKR_DEV,
8525bdb6a16STakashi Iwai 	SB_DT019X_LINE_DEV,
8535bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8545bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
8555bdb6a16STakashi Iwai 	SB_DT019X_CAPTURE_SW,
8565bdb6a16STakashi Iwai };
8575bdb6a16STakashi Iwai 
8582a076d0aSTakashi Iwai static const unsigned char als4000_saved_regs[] = {
859ce71bfd1SAndreas Mohr 	/* please verify in dsheet whether regs to be added
860ce71bfd1SAndreas Mohr 	   are actually real H/W or just dummy */
8615bdb6a16STakashi Iwai 	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
8625bdb6a16STakashi Iwai 	SB_DSP4_OUTPUT_SW,
8635bdb6a16STakashi Iwai 	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
8645bdb6a16STakashi Iwai 	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
8655bdb6a16STakashi Iwai 	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
8665bdb6a16STakashi Iwai 	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
8675bdb6a16STakashi Iwai 	SB_DSP4_MIC_DEV,
8685bdb6a16STakashi Iwai 	SB_DSP4_SPEAKER_DEV,
8695bdb6a16STakashi Iwai 	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
8705bdb6a16STakashi Iwai 	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
8715bdb6a16STakashi Iwai 	SB_DT019X_OUTPUT_SW2,
8725bdb6a16STakashi Iwai 	SB_ALS4000_MONO_IO_CTRL,
8735bdb6a16STakashi Iwai 	SB_ALS4000_MIC_IN_GAIN,
874ce71bfd1SAndreas Mohr 	SB_ALS4000_FMDAC,
8755bdb6a16STakashi Iwai 	SB_ALS4000_3D_SND_FX,
8765bdb6a16STakashi Iwai 	SB_ALS4000_3D_TIME_DELAY,
877ce71bfd1SAndreas Mohr 	SB_ALS4000_CR3_CONFIGURATION,
8785bdb6a16STakashi Iwai };
8795bdb6a16STakashi Iwai 
save_mixer(struct snd_sb * chip,const unsigned char * regs,int num_regs)8802a076d0aSTakashi Iwai static void save_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
8815bdb6a16STakashi Iwai {
8825bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
883622207dcSTakashi Iwai 	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
884622207dcSTakashi Iwai 		return;
8855bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
8865bdb6a16STakashi Iwai 		*val++ = snd_sbmixer_read(chip, *regs++);
8875bdb6a16STakashi Iwai }
8885bdb6a16STakashi Iwai 
restore_mixer(struct snd_sb * chip,const unsigned char * regs,int num_regs)8892a076d0aSTakashi Iwai static void restore_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
8905bdb6a16STakashi Iwai {
8915bdb6a16STakashi Iwai 	unsigned char *val = chip->saved_regs;
892622207dcSTakashi Iwai 	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
893622207dcSTakashi Iwai 		return;
8945bdb6a16STakashi Iwai 	for (; num_regs; num_regs--)
8955bdb6a16STakashi Iwai 		snd_sbmixer_write(chip, *regs++, *val++);
8965bdb6a16STakashi Iwai }
8975bdb6a16STakashi Iwai 
snd_sbmixer_suspend(struct snd_sb * chip)8985bdb6a16STakashi Iwai void snd_sbmixer_suspend(struct snd_sb *chip)
8995bdb6a16STakashi Iwai {
9005bdb6a16STakashi Iwai 	switch (chip->hardware) {
9015bdb6a16STakashi Iwai 	case SB_HW_20:
9025bdb6a16STakashi Iwai 	case SB_HW_201:
9035bdb6a16STakashi Iwai 		save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
9045bdb6a16STakashi Iwai 		break;
9055bdb6a16STakashi Iwai 	case SB_HW_PRO:
906ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
9075bdb6a16STakashi Iwai 		save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9085bdb6a16STakashi Iwai 		break;
9095bdb6a16STakashi Iwai 	case SB_HW_16:
9105bdb6a16STakashi Iwai 	case SB_HW_ALS100:
911621887aeSTakashi Iwai 	case SB_HW_CS5530:
9125bdb6a16STakashi Iwai 		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9135bdb6a16STakashi Iwai 		break;
9145bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9155bdb6a16STakashi Iwai 		save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9165bdb6a16STakashi Iwai 		break;
9175bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9185bdb6a16STakashi Iwai 		save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9195bdb6a16STakashi Iwai 		break;
9205bdb6a16STakashi Iwai 	default:
9215bdb6a16STakashi Iwai 		break;
9225bdb6a16STakashi Iwai 	}
9235bdb6a16STakashi Iwai }
9245bdb6a16STakashi Iwai 
snd_sbmixer_resume(struct snd_sb * chip)9255bdb6a16STakashi Iwai void snd_sbmixer_resume(struct snd_sb *chip)
9265bdb6a16STakashi Iwai {
9275bdb6a16STakashi Iwai 	switch (chip->hardware) {
9285bdb6a16STakashi Iwai 	case SB_HW_20:
9295bdb6a16STakashi Iwai 	case SB_HW_201:
9305bdb6a16STakashi Iwai 		restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
9315bdb6a16STakashi Iwai 		break;
9325bdb6a16STakashi Iwai 	case SB_HW_PRO:
933ad8decb7SKrzysztof Helt 	case SB_HW_JAZZ16:
9345bdb6a16STakashi Iwai 		restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
9355bdb6a16STakashi Iwai 		break;
9365bdb6a16STakashi Iwai 	case SB_HW_16:
9375bdb6a16STakashi Iwai 	case SB_HW_ALS100:
938621887aeSTakashi Iwai 	case SB_HW_CS5530:
9395bdb6a16STakashi Iwai 		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
9405bdb6a16STakashi Iwai 		break;
9415bdb6a16STakashi Iwai 	case SB_HW_ALS4000:
9425bdb6a16STakashi Iwai 		restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
9435bdb6a16STakashi Iwai 		break;
9445bdb6a16STakashi Iwai 	case SB_HW_DT019X:
9455bdb6a16STakashi Iwai 		restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
9465bdb6a16STakashi Iwai 		break;
9475bdb6a16STakashi Iwai 	default:
9485bdb6a16STakashi Iwai 		break;
9495bdb6a16STakashi Iwai 	}
9505bdb6a16STakashi Iwai }
9515bdb6a16STakashi Iwai #endif
952