[alsa-devel] [PATCH] snd-usb-6fire: volume control for individual analog channels

Torsten Schenk torsten.schenk at zoho.com
Thu Sep 15 17:32:10 CEST 2011


Added a stereo volume control for each analog output channel pair.
Master volume modified so that it acts as a software weight for
the individual channel faders.
Added mute switches for master and individual analog channels.

Signed-off-by: Torsten Schenk <torsten.schenk at zoho.com>
Cc: stable at kernel.org [3.0+]
---
diff -Nur a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
--- a/sound/usb/6fire/chip.c	2011-09-15 16:11:08.000000000 +0000
+++ b/sound/usb/6fire/chip.c	2011-09-15 16:11:46.000000000 +0000
@@ -5,7 +5,7 @@
  *
  * Author:	Torsten Schenk <torsten.schenk at zoho.com>
  * Created:	Jan 01, 2011
- * Version:	0.3.0
+ * Version:	0.3.6
  * Copyright:	(C) Torsten Schenk
  *
  * This program is free software; you can redistribute it and/or modify
@@ -29,7 +29,7 @@
 #include <sound/initval.h>
 
 MODULE_AUTHOR("Torsten Schenk <torsten.schenk at zoho.com>");
-MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0");
+MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.6");
 MODULE_LICENSE("GPL v2");
 MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}");
 
diff -Nur a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c
--- a/sound/usb/6fire/control.c	2011-09-02 13:48:10.000000000 +0000
+++ b/sound/usb/6fire/control.c	2011-09-02 13:50:35.000000000 +0000
@@ -5,9 +5,13 @@
  *
  * Author:	Torsten Schenk <torsten.schenk at zoho.com>
  * Created:	Jan 01, 2011
- * Version:	0.3.0
+ * Version:	0.3.6
  * Copyright:	(C) Torsten Schenk
  *
+ * Thanks to:
+ * - Holger Ruckdeschel: he found out how to control individual analog
+ *   channel volumes and introduced mute switches
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -59,7 +63,7 @@
 	{ 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
 	{ 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
 	{ 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
-	{ 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
+	{ 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
 	{ 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
 	{ 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
 	{ 0 } /* TERMINATING ENTRY */
@@ -74,15 +78,35 @@
 	DIGITAL_THRU_ONLY_SAMPLERATE = 3
 };
 
-static void usb6fire_control_master_vol_update(struct control_runtime *rt)
+static void usb6fire_control_output_vol_update(struct control_runtime *rt)
+{
+	struct comm_runtime *comm_rt = rt->chip->comm;
+	u8 value;
+
+	if (comm_rt) {
+		int i;
+		for (i = 0; i < 6; i++) {
+			/* set volume */
+			value = rt->output_vol[i] * rt->master_vol / 0x7f;
+			comm_rt->write8(comm_rt, 0x12, 0x0f + i, 0x7f -
+					log_volume_table[value]);
+		}
+	}
+}
+
+static void usb6fire_control_output_switch_update(struct control_runtime *rt)
 {
 	struct comm_runtime *comm_rt = rt->chip->comm;
+	int i;
+	u8 value = 0x00;
+
 	if (comm_rt) {
-		/* set volume */
-		comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f -
-				log_volume_table[rt->master_vol]);
-		 /* unmute */
-		comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00);
+		if (rt->master_switch) /* mute/unmute single outputs */
+			for (i = 0; i < 6; i++)
+				value |= !rt->output_switch[i] << i;
+		else /* mute all outputs */
+			value = 0x3f;
+		comm_rt->write8(comm_rt, 0x12, 0x0e, value);
 	}
 }
 
@@ -171,7 +195,17 @@
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 127;
+	uinfo->value.integer.max = 0x7f;
+	return 0;
+}
+
+static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0x7f;
 	return 0;
 }
 
@@ -182,7 +216,12 @@
 	int changed = 0;
 	if (rt->master_vol != ucontrol->value.integer.value[0]) {
 		rt->master_vol = ucontrol->value.integer.value[0];
-		usb6fire_control_master_vol_update(rt);
+		usb6fire_control_output_vol_update(rt);
+		changed = 1;
+	}
+	if (rt->master_vol != ucontrol->value.integer.value[0]) {
+		rt->master_vol = ucontrol->value.integer.value[0];
+		usb6fire_control_output_vol_update(rt);
 		changed = 1;
 	}
 	return changed;
@@ -196,6 +235,127 @@
 	return 0;
 }
 
+static int usb6fire_control_master_switch_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+	int changed = 0;
+	if (rt->master_switch != ucontrol->value.integer.value[0]) {
+		rt->master_switch = ucontrol->value.integer.value[0];
+		usb6fire_control_output_switch_update(rt);
+		changed = 1;
+	}
+	return changed;
+}
+
+static int usb6fire_control_master_switch_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = rt->master_switch;
+	return 0;
+}
+
+static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+	unsigned long chs = kcontrol->private_value;
+	unsigned long ch0 = 2 * chs;
+	unsigned long ch1 = 2 * chs + 1;
+	int changed = 0;
+
+	if (chs < 0 || chs > 2) {
+		snd_printk(KERN_ERR PREFIX
+			"Invalid channel in volume control.");
+		return 0;
+	}
+
+	if (rt->output_vol[ch0] != ucontrol->value.integer.value[0]) {
+		rt->output_vol[ch0] = ucontrol->value.integer.value[0];
+		changed = 1;
+	}
+	if (rt->output_vol[ch1] != ucontrol->value.integer.value[1]) {
+		rt->output_vol[ch1] = ucontrol->value.integer.value[1];
+		changed = 1;
+	}
+
+	if (changed)
+		usb6fire_control_output_vol_update(rt);
+
+	return changed;
+}
+
+static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+	unsigned long chs = kcontrol->private_value;
+	unsigned long ch0 = 2 * chs;
+	unsigned long ch1 = 2 * chs + 1;
+
+	if (chs < 0 || chs > 2) {
+		snd_printk(KERN_ERR PREFIX
+			"Invalid channel in volume control.");
+		return 0;
+	}
+
+	ucontrol->value.integer.value[0] = rt->output_vol[ch0];
+	ucontrol->value.integer.value[1] = rt->output_vol[ch1];
+
+	return 0;
+}
+
+static int usb6fire_control_output_switch_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+	unsigned long chs = kcontrol->private_value;
+	unsigned long ch0 = 2 * chs;
+	unsigned long ch1 = 2 * chs + 1;
+	int changed = 0;
+
+	if (chs < 0 || chs > 2) {
+		snd_printk(KERN_ERR PREFIX
+			"Invalid channel in volume control.");
+		return 0;
+	}
+
+	if (rt->output_switch[ch0] != ucontrol->value.integer.value[0]) {
+		rt->output_switch[ch0] = ucontrol->value.integer.value[0];
+		changed = 1;
+	}
+	if (rt->output_switch[ch1] != ucontrol->value.integer.value[1]) {
+		rt->output_switch[ch1] = ucontrol->value.integer.value[1];
+		changed = 1;
+	}
+
+	if (changed)
+		usb6fire_control_output_switch_update(rt);
+
+	return changed;
+}
+
+static int usb6fire_control_output_switch_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+	unsigned long chs = kcontrol->private_value;
+	unsigned long ch0 = 2 * chs;
+	unsigned long ch1 = 2 * chs + 1;
+
+	if (chs < 0 || chs > 2) {
+		snd_printk(KERN_ERR PREFIX
+			"Invalid channel in volume control.");
+		return 0;
+	}
+
+	ucontrol->value.integer.value[0] = rt->output_switch[ch0];
+	ucontrol->value.integer.value[1] = rt->output_switch[ch1];
+
+	return 0;
+}
+
 static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,
 		struct snd_ctl_elem_info *uinfo)
 {
@@ -299,6 +459,75 @@
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = snd_ctl_boolean_mono_info,
+		.get = usb6fire_control_master_switch_get,
+		.put = usb6fire_control_master_switch_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Output 1/2 Playback Volume",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = usb6fire_control_output_vol_info,
+		.get = usb6fire_control_output_vol_get,
+		.put = usb6fire_control_output_vol_put,
+		.private_value = 0
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Output 1/2 Playback Switch",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = snd_ctl_boolean_stereo_info,
+		.get = usb6fire_control_output_switch_get,
+		.put = usb6fire_control_output_switch_put,
+		.private_value = 0
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Output 3/4 Playback Volume",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = usb6fire_control_output_vol_info,
+		.get = usb6fire_control_output_vol_get,
+		.put = usb6fire_control_output_vol_put,
+		.private_value = 1
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Output 3/4 Playback Switch",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = snd_ctl_boolean_stereo_info,
+		.get = usb6fire_control_output_switch_get,
+		.put = usb6fire_control_output_switch_put,
+		.private_value = 1
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Output 5/6 Playback Volume",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = usb6fire_control_output_vol_info,
+		.get = usb6fire_control_output_vol_get,
+		.put = usb6fire_control_output_vol_put,
+		.private_value = 2
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Output 5/6 Playback Switch",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = snd_ctl_boolean_stereo_info,
+		.get = usb6fire_control_output_switch_get,
+		.put = usb6fire_control_output_switch_put,
+		.private_value = 2
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Line/Phono Capture Route",
 		.index = 0,
 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -352,7 +581,8 @@
 
 	usb6fire_control_opt_coax_update(rt);
 	usb6fire_control_line_phono_update(rt);
-	usb6fire_control_master_vol_update(rt);
+	usb6fire_control_output_vol_update(rt);
+	usb6fire_control_output_switch_update(rt);
 	usb6fire_control_streaming_update(rt);
 
 	i = 0;
@@ -378,3 +608,4 @@
 	kfree(chip->control);
 	chip->control = NULL;
 }
+
diff -Nur a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h
--- a/sound/usb/6fire/control.h	2011-09-02 13:48:10.000000000 +0000
+++ b/sound/usb/6fire/control.h	2011-09-02 13:50:30.000000000 +0000
@@ -3,9 +3,13 @@
  *
  * Author:	Torsten Schenk <torsten.schenk at zoho.com>
  * Created:	Jan 01, 2011
- * Version:	0.3.0
+ * Version:	0.3.6
  * Copyright:	(C) Torsten Schenk
  *
+ * Thanks to:
+ * - Holger Ruckdeschel: he found out how to control individual analog
+ *   channel volumes and introduced mute switches
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -45,6 +49,9 @@
 	bool digital_thru_switch;
 	bool usb_streaming;
 	u8 master_vol;
+	bool master_switch;
+	u8 output_vol[6];
+	bool output_switch[6];
 };
 
 int __devinit usb6fire_control_init(struct sfire_chip *chip);



More information about the Alsa-devel mailing list