[ALSA] Enable DXS controls for VIA VT82xx

Modules: VIA82xx driver

The patch enable separate DXS controls of sound function of VIA VT82xx
controller in case DXS volume is not needed for PCM Playback volume
control emulation.

Signed-off-by: Honza Maly <hkmaly@matfyz.cz>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 78a5dfb..7d46bee 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -361,7 +361,8 @@
 	unsigned int mpu_port_saved;
 #endif
 
-	unsigned char playback_volume[2]; /* for VIA8233/C/8235; default = 0 */
+	unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */
+	unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */
 
 	unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */
 
@@ -937,8 +938,8 @@
 	snd_assert((rbits & ~0xfffff) == 0, return -EINVAL);
 	snd_via82xx_channel_reset(chip, viadev);
 	snd_via82xx_set_table_ptr(chip, viadev);
-	outb(chip->playback_volume[0], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_L));
-	outb(chip->playback_volume[1], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_R));
+	outb(chip->playback_volume[viadev->reg_offset / 0x10][0], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_L));
+	outb(chip->playback_volume[viadev->reg_offset / 0x10][1], VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_R));
 	outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | /* format */
 	     (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | /* stereo */
 	     rbits | /* rate */
@@ -1498,14 +1499,46 @@
 static int snd_via8233_dxs_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
-	ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume[0];
-	ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume[1];
+	unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+
+	ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][0];
+	ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][1];
+	return 0;
+}
+
+static int snd_via8233_pcmdxs_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume_c[0];
+	ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume_c[1];
 	return 0;
 }
 
 static int snd_via8233_dxs_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+	unsigned long port = chip->port + 0x10 * idx;
+	unsigned char val;
+	int i, change = 0;
+
+	for (i = 0; i < 2; i++) {
+		val = ucontrol->value.integer.value[i];
+		if (val > VIA_DXS_MAX_VOLUME)
+			val = VIA_DXS_MAX_VOLUME;
+		val = VIA_DXS_MAX_VOLUME - val;
+		change |= val != chip->playback_volume[idx][i];
+		if (change) {
+			chip->playback_volume[idx][i] = val;
+			outb(val, port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+		}
+	}
+	return change;
+}
+
+static int snd_via8233_pcmdxs_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
 	unsigned int idx;
 	unsigned char val;
 	int i, change = 0;
@@ -1515,11 +1548,12 @@
 		if (val > VIA_DXS_MAX_VOLUME)
 			val = VIA_DXS_MAX_VOLUME;
 		val = VIA_DXS_MAX_VOLUME - val;
-		if (val != chip->playback_volume[i]) {
+		if (val != chip->playback_volume_c[i]) {
 			change = 1;
-			chip->playback_volume[i] = val;
+			chip->playback_volume_c[i] = val;
 			for (idx = 0; idx < 4; idx++) {
 				unsigned long port = chip->port + 0x10 * idx;
+				chip->playback_volume[idx][i] = val;
 				outb(val, port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
 			}
 		}
@@ -1527,10 +1561,19 @@
 	return change;
 }
 
-static snd_kcontrol_new_t snd_via8233_dxs_volume_control __devinitdata = {
+static snd_kcontrol_new_t snd_via8233_pcmdxs_volume_control __devinitdata = {
 	.name = "PCM Playback Volume",
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.info = snd_via8233_dxs_volume_info,
+	.get = snd_via8233_pcmdxs_volume_get,
+	.put = snd_via8233_pcmdxs_volume_put,
+};
+
+static snd_kcontrol_new_t snd_via8233_dxs_volume_control __devinitdata = {
+	.name = "VIA DXS Playback Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.count = 4,
+	.info = snd_via8233_dxs_volume_info,
 	.get = snd_via8233_dxs_volume_get,
 	.put = snd_via8233_dxs_volume_put,
 };
@@ -1723,12 +1766,19 @@
 		strcpy(sid.name, "PCM Playback Volume");
 		sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 		if (! snd_ctl_find_id(chip->card, &sid)) {
+			snd_printd(KERN_INFO "Using DXS as PCM Playback\n");
+			err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_pcmdxs_volume_control, chip));
+			if (err < 0)
+				return err;
+		}
+		else /* Using DXS when PCM emulation is enabled is really weird */
+		{
+			/* Standalone DXS controls */
 			err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip));
 			if (err < 0)
 				return err;
 		}
 	}
-
 	/* select spdif data slot 10/11 */
 	pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val);
 	val = (val & ~VIA8233_SPDIF_SLOT_MASK) | VIA8233_SPDIF_SLOT_1011;
@@ -1941,8 +1991,10 @@
 		int i, idx;
 		for (idx = 0; idx < 4; idx++) {
 			unsigned long port = chip->port + 0x10 * idx;
-			for (i = 0; i < 2; i++)
-				outb(chip->playback_volume[i], port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+			for (i = 0; i < 2; i++) {
+				chip->playback_volume[idx][i]=chip->playback_volume_c[i];
+				outb(chip->playback_volume_c[i], port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+			}
 		}
 	}