patch-2.2.0-pre1 linux/drivers/sound/sb_mixer.c

Next file: linux/drivers/sound/sb_mixer.h
Previous file: linux/drivers/sound/sb_common.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.132/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
@@ -13,6 +13,24 @@
  *
  *
  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Rolf Fokkens    : ES188x recording level support
+ */
+
+/*
+ * About ES188x support:
+ *
+ * The standard ES1688 support doesn't take care of the ES188x recording
+ * levels very well. Whenever a device is selected (recmask) for recording
+ * it's recording level is loud, and it cannot be changed.
+ *
+ * The ES188x has separate registers to control the recording levels. The
+ * ES188x specific software makes these level the same as their corresponding
+ * playback levels, unless recmask says they aren't recorded. In the latter
+ * case the recording volumes are 0.
+ *
+ * Now recording levels of inputs can be controlled, by changing the playback
+ * levels. Futhermore several devices can be recorded together (which is not
+ * possible with the ES1688.
  */
 
 #include <linux/config.h>
@@ -78,22 +96,62 @@
 	sb_mixer_reset(devc);
 }
 
-static int smw_mixer_set(sb_devc * devc, int dev, int value)
+static int common_mixer_set(sb_devc * devc, int dev, int left, int right)
 {
-	int left = value & 0x000000ff;
-	int right = (value & 0x0000ff00) >> 8;
-	int reg, val;
+	int regoffs;
+	unsigned char val;
 
-	if (left > 100)
-		left = 100;
-	if (right > 100)
-		right = 100;
+	regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
 
-	if (dev > 31)
+	if (regoffs == 0)
 		return -EINVAL;
 
-	if (!(devc->supported_devices & (1 << dev)))	/* Not supported */
-		return -EINVAL;
+	val = sb_getmixer(devc, regoffs);
+	change_bits(devc, &val, dev, LEFT_CHN, left);
+
+	devc->levels[dev] = left | (left << 8);
+
+	if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs)	/*
+								 * Change register
+								 */
+	{
+		sb_setmixer(devc, regoffs, val);	/*
+							 * Save the old one
+							 */
+		regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
+
+		if (regoffs == 0)
+			return left | (left << 8);	/*
+							 * Just left channel present
+							 */
+
+		val = sb_getmixer(devc, regoffs);	/*
+							 * Read the new one
+							 */
+	}
+	change_bits(devc, &val, dev, RIGHT_CHN, right);
+
+	sb_setmixer(devc, regoffs, val);
+
+	devc->levels[dev] = left | (right << 8);
+	return left | (right << 8);
+}
+
+/*
+ * Changing playback levels at ES188x means having to take care of recording
+ * levels of recorded inputs (devc->recmask) too!
+ */
+static int es188x_mixer_set(sb_devc * devc, int dev, int left, int right)
+{
+	if (devc->recmask & (1 << dev)) {
+		common_mixer_set(devc, dev + ES188X_MIXER_RECDIFF, left, right);
+	}
+	return common_mixer_set(devc, dev, left, right);
+}
+
+static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
+{
+	int reg, val;
 
 	switch (dev)
 	{
@@ -134,12 +192,6 @@
 	int left = value & 0x000000ff;
 	int right = (value & 0x0000ff00) >> 8;
 
-	int regoffs;
-	unsigned char   val;
-
-	if (devc->model == MDL_SMW)
-		return smw_mixer_set(devc, dev, value);
-
 	if (left > 100)
 		left = 100;
 	if (right > 100)
@@ -153,47 +205,60 @@
 							 */
 		return -EINVAL;
 
-	regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
-
-	if (regoffs == 0)
-		return -EINVAL;
-
-	val = sb_getmixer(devc, regoffs);
-	change_bits(devc, &val, dev, LEFT_CHN, left);
-
-	devc->levels[dev] = left | (left << 8);
-
-	if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs)	/*
-								 * Change register
-								 */
-	{
-		sb_setmixer(devc, regoffs, val);	/*
-							 * Save the old one
-							 */
-		regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
-
-		if (regoffs == 0)
-			return left | (left << 8);	/*
-							 * Just left channel present
-							 */
-
-		val = sb_getmixer(devc, regoffs);	/*
-							 * Read the new one
-							 */
+	/* Differentiate dependong on the chipsets */
+	switch (devc->model) {
+	case MDL_SMW:
+		return smw_mixer_set(devc, dev, left, right);
+		break;
+	case MDL_ESS:
+		if (devc->submodel == SUBMDL_ES188X) {
+			return es188x_mixer_set(devc, dev, left, right);
+		}
+		break;
 	}
-	change_bits(devc, &val, dev, RIGHT_CHN, right);
 
-	sb_setmixer(devc, regoffs, val);
-
-	devc->levels[dev] = left | (right << 8);
-	return left | (right << 8);
+	return common_mixer_set(devc, dev, left, right);
 }
 
+/*
+ * set_recsrc doesn't apply to ES188x
+ */
 static void set_recsrc(sb_devc * devc, int src)
 {
 	sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
 }
 
+/*
+ * Changing the recmask on a ES188x means:
+ * (1) Find the differences
+ * (2) For "turned-on"  inputs: make the recording level the playback level
+ * (3) For "turned-off" inputs: make the recording level zero
+ */
+static int es188x_set_recmask(sb_devc * devc, int mask)
+{
+	int i, i_mask, cur_mask, diff_mask;
+	int value, left, right;
+
+	cur_mask  = devc->recmask;
+	diff_mask = (cur_mask ^ mask);
+
+	for (i = 0; i < 32; i++) {
+		i_mask = (1 << i);
+		if (diff_mask & i_mask) {	/* Difference? (1)	*/
+			if (mask & i_mask) {	/* Turn it on  (2)	*/
+				value = devc->levels[i];
+				left  = value & 0x000000ff;
+    				right = (value & 0x0000ff00) >> 8;
+			} else {		/* Turn it off (3)	*/
+				left  = 0;
+				right = 0;
+			}
+			common_mixer_set(devc, i + ES188X_MIXER_RECDIFF, left, right);
+		}
+	}
+	return mask;
+}
+
 static int set_recmask(sb_devc * devc, int mask)
 {
 	int devmask, i;
@@ -207,6 +272,14 @@
 		case MDL_ESS:
 		case MDL_JAZZ:
 		case MDL_SMW:
+			if (devc->model == MDL_ESS &&
+				devc->submodel == SUBMDL_ES188X) {
+				/*
+				 * ES188x needs a separate approach
+				 */
+				devmask = es188x_set_recmask(devc, devmask);
+				break;
+			};
 
 			if (devmask != SOUND_MASK_MIC &&
 				devmask != SOUND_MASK_LINE &&
@@ -423,7 +496,7 @@
 static void sb_mixer_reset(sb_devc * devc)
 {
 	char name[32];
-	int i;
+	int i, regval;
 	extern int sm_games;
 
 	sprintf(name, "SB_%d", devc->sbmixnum);
@@ -435,6 +508,24 @@
 
 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
 		sb_mixer_set(devc, i, devc->levels[i]);
+
+	/*
+	 * Separate actions for ES188x:
+	 * Change registers 7a and 1c to make the record mixer the
+	 * actual recording source.
+	 * Then call set_recmask twice to do extra ES188x initializations
+	 */
+	if (devc->model == MDL_ESS && devc->submodel == SUBMDL_ES188X) {
+	        regval = sb_getmixer(devc, 0x7a);
+		regval = (regval & 0xe7) | 0x08;
+	        sb_setmixer(devc, 0x7a, regval);
+		regval = sb_getmixer(devc, 0x1c);
+		regval = (regval & 0xf8) | 0x07;
+		sb_setmixer(devc, 0x1c, regval);
+
+		set_recmask(devc, ES188X_RECORDING_DEVICES);
+		set_recmask(devc, 0);
+	}
 	set_recmask(devc, SOUND_MASK_MIC);
 }
 
@@ -464,9 +555,26 @@
 
 		case MDL_ESS:
 			devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
-			devc->supported_devices = ES688_MIXER_DEVICES;
-			devc->supported_rec_devices = ES688_RECORDING_DEVICES;
-			devc->iomap = &es688_mix;
+
+			/*
+			 * Take care of ES188x specifics...
+			 */
+			switch (devc->submodel) {
+			case SUBMDL_ES188X:
+				devc->supported_devices
+					= ES188X_MIXER_DEVICES;
+				devc->supported_rec_devices
+					= ES188X_RECORDING_DEVICES;
+				devc->iomap = &es188x_mix;
+				break;
+			default:
+				devc->supported_devices
+					= ES688_MIXER_DEVICES;
+				devc->supported_rec_devices
+					= ES688_RECORDING_DEVICES;
+				devc->iomap = &es688_mix;
+			}
+
 			break;
 
 		case MDL_SMW:

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov