[alsa-devel] [PATCH 3/8] add control interfaces

o-takashi at sakamocchi.jp o-takashi at sakamocchi.jp
Sat Jun 1 17:55:53 CEST 2013


From: Takashi Sakamoto <o-takashi at sakamocchi.jp>

This patch adds ALSA's control interfaces to control Fireworks by executing
Echo Fireworks Command.

Currently the policy of module implementation hope much controls in user-land.
So this module add any controls needed to decide the number of channels in
AMDTP stream, clock source, and to get physical metering.

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 sound/firewire/fireworks/fireworks_control.c |  537 ++++++++++++++++++++++++++
 1 file changed, 537 insertions(+)
 create mode 100644 sound/firewire/fireworks/fireworks_control.c

diff --git a/sound/firewire/fireworks/fireworks_control.c b/sound/firewire/fireworks/fireworks_control.c
new file mode 100644
index 0000000..1599579
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_control.c
@@ -0,0 +1,537 @@
+/*
+ * fireworks/control.c - driver for Firewire devices from Echo Digital Audio
+ *
+ * Copyright (c) 2013 Takashi Sakamoto <o-takashi at sakamocchi.jp>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fireworks.h"
+
+/*
+ * Currently this module support any controls related to decision of channels
+ * in stream, hardware metering and digital format. Users should utilize tools
+ * which FFADO project developed.
+ */
+
+/*
+ * Physical metering:
+ *  the value in unavvailable channels is zero.
+ */
+static int
+physical_metering_info(struct snd_kcontrol *ctl,
+		       struct snd_ctl_elem_info *info)
+{
+	struct snd_efw *efw = ctl->private_data;
+
+	info->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	info->count = (efw->input_meter_counts + efw->output_meter_counts)
+			 * 4 + 2;
+
+	return 0;
+}
+static int
+physical_metering_get(struct snd_kcontrol *ctl,
+		      struct snd_ctl_elem_value *value)
+{
+	struct snd_efw *efw = ctl->private_data;
+	struct snd_efw_phys_meters *meters;
+	int base = sizeof(struct snd_efw_phys_meters);
+	int count = efw->input_meter_counts + efw->output_meter_counts;
+	u32 *dst, *src;
+	int i, err;
+
+	meters = kzalloc(base + count * 4, GFP_KERNEL);
+	if (meters == NULL)
+		return -ENOMEM;
+
+	err = snd_efw_command_get_phys_meters(efw, meters, base + count * 4);
+	if (err < 0)
+		goto end;
+
+	value->value.bytes.data[0] = efw->input_meter_counts;
+	value->value.bytes.data[1] = efw->output_meter_counts;
+
+	dst = (u32 *)(value->value.bytes.data + 2);
+	src = meters->values;
+
+	for (i = 0; i < efw->input_meter_counts; i += 1)
+		dst[i] = src[efw->output_meter_counts + i];
+
+	for (i = 0; i < efw->output_meter_counts; i += 1)
+		dst[i + efw->input_meter_counts] = src[i];
+
+	err = 0;
+end:
+	kfree(meters);
+	return err;
+}
+static const
+struct snd_kcontrol_new physical_metering = {
+	.iface	= SNDRV_CTL_ELEM_IFACE_CARD,
+	.name	= "Physical Metering",
+	.access	= SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info	= physical_metering_info,
+	.get	= physical_metering_get
+};
+
+/*
+ * Global Control:  Digital capture and playback mode
+ *
+ * S/PDIF or ADAT, Coaxial or Optical
+ * snd_efw_hwinfo.flags include a flag for this control.
+ */
+static char *digital_iface_descs[] = {"S/PDIF Coaxial", "ADAT Coaxial",
+					  "S/PDIF Optical", "ADAT Optical"};
+static int
+control_digital_interface_info(struct snd_kcontrol *kctl,
+			       struct snd_ctl_elem_info *einf)
+{
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int value, i;
+
+	einf->value.enumerated.items = 0;
+	for (i = 0; i < ARRAY_SIZE(digital_iface_descs); i += 1) {
+		if ((1 << i) & efw->supported_digital_interface)
+			einf->value.enumerated.items += 1;
+	}
+
+	einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	einf->count = 1;
+
+	if (einf->value.enumerated.item >= einf->value.enumerated.items)
+		einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+	/* skip unsupported clock source */
+	value = einf->value.enumerated.item;
+	for (i = 0; i < ARRAY_SIZE(digital_iface_descs); i += 1) {
+		if (!((1 << i) & efw->supported_digital_interface))
+			continue;
+		else if (value == 0)
+			break;
+		else
+		value -= 1;
+	}
+
+	strcpy(einf->value.enumerated.name, digital_iface_descs[i]);
+
+	return 0;
+}
+static int
+control_digital_interface_get(struct snd_kcontrol *kctl,
+			      struct snd_ctl_elem_value *uval)
+{
+	int err = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	enum snd_efw_digital_interface digital_interface;
+	int i;
+
+	/* get current index */
+	err = snd_efw_command_get_digital_interface(efw, &digital_interface);
+	if (err < 0)
+		goto end;
+
+	/* check clock source */
+	if ((digital_interface < 0) &&
+	    (ARRAY_SIZE(digital_iface_descs) < digital_interface))
+		goto end;
+
+	/* generate user value */
+	uval->value.enumerated.item[0] = 0;
+	for (i = 0; i < digital_interface; i += 1) {
+		if ((1 << i) & efw->supported_digital_interface)
+			uval->value.enumerated.item[0] += 1;
+	}
+
+end:
+	return err;
+}
+static int
+control_digital_interface_put(struct snd_kcontrol *kctl,
+			      struct snd_ctl_elem_value *uval)
+{
+	int changed = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int index, value;
+
+	/* get index from user value */
+	value = uval->value.enumerated.item[0];
+	for (index = 0; index < ARRAY_SIZE(digital_iface_descs); index++) {
+		/* not supported */
+		if (!((1 << index) & efw->supported_digital_interface))
+			continue;
+		else if (value == 0)
+			break;
+		else
+			value -= 1;
+	}
+
+	/* set clock */
+	if (snd_efw_command_set_digital_interface(efw, index) < 0)
+		goto end;
+
+	changed = 1;
+
+end:
+	return changed;
+}
+
+/*
+ * Global Control: S/PDIF format are selectable from "Professional/Consumer".
+ *  Consumer:		IEC-60958 Digital audio interface
+ *				Part 3:Consumer applications
+ *  Professional:	IEC-60958 Digital audio interface
+ *				Part 4: Professional applications
+ *
+ * snd_efw_hwinfo.flags include a flag for this control
+ */
+static char *spdif_format_descs[] = {"Consumer", "Professional"};
+static int
+control_spdif_format_info(struct snd_kcontrol *kctl,
+			  struct snd_ctl_elem_info *einf)
+{
+	einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	einf->count = 1;
+	einf->value.enumerated.items = ARRAY_SIZE(spdif_format_descs);
+
+	if (einf->value.enumerated.item >= einf->value.enumerated.items)
+		einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+	strcpy(einf->value.enumerated.name,
+		spdif_format_descs[einf->value.enumerated.item]);
+
+	return 0;
+}
+static int
+control_spdif_format_get(struct snd_kcontrol *kctl,
+			 struct snd_ctl_elem_value *uvalue)
+{
+	int err = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	enum snd_efw_iec60958_format format;
+
+	err = snd_efw_command_get_iec60958_format(efw, &format);
+	if (err >= 0)
+		uvalue->value.enumerated.item[0] = format;
+
+	return 0;
+}
+static int
+control_spdif_format_put(struct snd_kcontrol *kctl,
+			 struct snd_ctl_elem_value *uval)
+{
+	int changed = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int value = uval->value.enumerated.item[0];
+
+	if (value < ARRAY_SIZE(spdif_format_descs)) {
+		if (snd_efw_command_set_iec60958_format(efw, value) < 0)
+			goto end;
+		changed = 1;
+	}
+
+end:
+	return changed;
+}
+
+/*
+ * Global Control: Sampling Rate Control
+ *
+ * snd_efw_hwinfo.min_sample_rate and struct efc_hwinfo.max_sample_rate
+ * is a minimum and maximum sampling rate
+ */
+static char *sampling_rate_descs[] = {"5512Hz", "8000Hz", "11025Hz",
+				      "16000Hz", "22050Hz", "32000Hz",
+				      "44100Hz", "48000Hz", "64000Hz",
+				      "88200Hz", "96000Hz", "176400Hz",
+				      "192000Hz"};
+static int sampling_rates[] = {5512, 8000, 11025, 16000, 22500, 32000, 44100,
+			       48000, 64000, 88200, 96000, 176400, 192000};
+static int
+control_sampling_rate_info(struct snd_kcontrol *kctl,
+			   struct snd_ctl_elem_info *einf)
+{
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int value, i;
+
+	/* maximum value for user */
+	einf->value.enumerated.items = 0;
+	for (i = 0; i < ARRAY_SIZE(sampling_rate_descs); i += 1) {
+		if ((1 << i) & efw->supported_sampling_rate)
+			einf->value.enumerated.items += 1;
+	}
+
+	einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	einf->count = 1;
+
+	if (einf->value.enumerated.item >= einf->value.enumerated.items)
+		einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+	/* skip unsupported clock source */
+	value = einf->value.enumerated.item;
+	for (i = 0; i < ARRAY_SIZE(sampling_rate_descs); i += 1) {
+		if (!((1 << i) & efw->supported_sampling_rate))
+			continue;
+		else if (value == 0)
+			break;
+		else
+			value -= 1;
+	}
+
+	strcpy(einf->value.enumerated.name, sampling_rate_descs[i]);
+
+	return 0;
+}
+static int
+control_sampling_rate_get(struct snd_kcontrol *kctl,
+			  struct snd_ctl_elem_value *uval)
+{
+	int err = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int sampling_rate;
+	int index, i;
+
+	/* get current sampling rate */
+	err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
+	if (err < 0)
+		goto end;
+
+	/* get index */
+	for (index = 0; index < ARRAY_SIZE(sampling_rates); index += 1) {
+		if (sampling_rates[index] == sampling_rate)
+			break;
+	}
+
+	/* get user value */
+	uval->value.enumerated.item[0] = 0;
+	for (i = 0; i < index; i += 1) {
+		if ((1 << i) & efw->supported_sampling_rate)
+			uval->value.enumerated.item[0] += 1;
+	}
+
+end:
+	return err;
+}
+static int
+control_sampling_rate_put(struct snd_kcontrol *kctl,
+			  struct snd_ctl_elem_value *uval)
+{
+	int changed = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int index, value;
+
+	/* get index from user value */
+	value = uval->value.enumerated.item[0];
+	for (index = 0; index < ARRAY_SIZE(sampling_rates); index += 1) {
+		/* not supported */
+		if (!((1 << index) & efw->supported_sampling_rate))
+			continue;
+		else if (value == 0)
+			break;
+		else
+			value -= 1;
+	}
+
+	/* set sampling rate */
+	if (snd_efw_command_set_sampling_rate(efw, sampling_rates[index]) < 0)
+		goto end;
+
+	changed = 1;
+
+end:
+	return changed;
+}
+
+/*
+ * Global Control: Clock Source Control
+ *
+ * snd_efw_hwinfo.supported_clocks is a flags for this control
+ */
+static char *clock_src_descs[] = {"Internal", "SYT Match", "Word",
+				     "S/PDIF", "ADAT1", "ADAT2"};
+static int
+control_clock_source_info(struct snd_kcontrol *kctl,
+			  struct snd_ctl_elem_info *einf)
+{
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int value, i;
+
+	/* skip unsupported clock source */
+	einf->value.enumerated.items = 0;
+	for (i = 0; i < ARRAY_SIZE(clock_src_descs); i += 1) {
+		if ((1 << i) & efw->supported_clock_source)
+			einf->value.enumerated.items += 1;
+	}
+
+	einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	einf->count = 1;
+
+	if (einf->value.enumerated.item >= einf->value.enumerated.items)
+		einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+	/* skip unsupported clock source */
+	value = einf->value.enumerated.item;
+	for (i = 0; i < ARRAY_SIZE(clock_src_descs); i += 1) {
+		if (!((1 << i) & efw->supported_clock_source))
+			continue;
+		else if (value == 0)
+			break;
+		else
+			value -= 1;
+	}
+
+	strcpy(einf->value.enumerated.name, clock_src_descs[i]);
+
+	return 0;
+}
+static int
+control_clock_source_get(struct snd_kcontrol *kctl,
+			 struct snd_ctl_elem_value *uval)
+{
+	int err = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	enum snd_efw_clock_source clock_source;
+	int i;
+
+	/* get current index */
+	err = snd_efw_command_get_clock_source(efw, &clock_source);
+	if (err < 0)
+		goto end;
+
+	/* check clock source */
+	if ((clock_source < 0) && (ARRAY_SIZE(clock_src_descs) < clock_source))
+		goto end;
+
+	/* generate user value */
+	uval->value.enumerated.item[0] = 0;
+	for (i = 0; i < clock_source; i += 1) {
+		if ((1 << i) & efw->supported_clock_source)
+			uval->value.enumerated.item[0] += 1;
+	}
+
+end:
+	return err;
+}
+static int
+control_clock_source_put(struct snd_kcontrol *kctl,
+			 struct snd_ctl_elem_value *uval)
+{
+	int changed = 0;
+
+	struct snd_efw *efw = snd_kcontrol_chip(kctl);
+	int index, value;
+
+	/* get index from user value */
+	value = uval->value.enumerated.item[0];
+	for (index = 0; index < ARRAY_SIZE(clock_src_descs); index += 1) {
+		/* not supported */
+		if (!((1 << index) & efw->supported_clock_source))
+			continue;
+		else if (value == 0)
+			break;
+		else
+			value -= 1;
+	}
+
+	/* set clock */
+	if (snd_efw_command_set_clock_source(efw, index) < 0)
+		goto end;
+
+	changed = 1;
+
+end:
+	return changed;
+}
+
+static struct snd_kcontrol_new global_clock_source_control = {
+	.name	= "Clock Source",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info	= control_clock_source_info,
+	.get	= control_clock_source_get,
+	.put	= control_clock_source_put
+};
+
+static struct snd_kcontrol_new global_sampling_rate_control = {
+	.name	= "Sampling Rate",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info	= control_sampling_rate_info,
+	.get	= control_sampling_rate_get,
+	.put	= control_sampling_rate_put
+};
+
+static struct snd_kcontrol_new global_digital_interface_control = {
+	.name	= "Digital Mode",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info	= control_digital_interface_info,
+	.get	= control_digital_interface_get,
+	.put	= control_digital_interface_put
+};
+
+static struct snd_kcontrol_new global_iec60958_format_control = {
+	.name	= "S/PDIF Format",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info	= control_spdif_format_info,
+	.get	= control_spdif_format_get,
+	.put	= control_spdif_format_put
+};
+
+int snd_efw_create_control_devices(struct snd_efw *efw)
+{
+	int err;
+	struct snd_kcontrol *kctl;
+
+	kctl = snd_ctl_new1(&physical_metering, efw);
+	err = snd_ctl_add(efw->card, kctl);
+	if (err < 0)
+		goto end;
+
+	if (efw->supported_clock_source > 0) {
+		kctl = snd_ctl_new1(&global_clock_source_control, efw);
+		err = snd_ctl_add(efw->card, kctl);
+		if (err < 0)
+			goto end;
+	}
+	if (efw->supported_sampling_rate > 0) {
+		kctl = snd_ctl_new1(&global_sampling_rate_control, efw);
+		err = snd_ctl_add(efw->card, kctl);
+		if (err < 0)
+			goto end;
+		efw->control_id_sampling_rate = &kctl->id;
+	}
+	if (efw->supported_digital_interface > 0) {
+		kctl = snd_ctl_new1(&global_digital_interface_control, efw);
+		err = snd_ctl_add(efw->card, kctl);
+		if (err < 0)
+			goto end;
+
+		kctl = snd_ctl_new1(&global_iec60958_format_control, efw);
+		err = snd_ctl_add(efw->card, kctl);
+		if (err < 0)
+			goto end;
+	}
+
+	err = 0;
+end:
+	return err;
+}
-- 
1.7.10.4



More information about the Alsa-devel mailing list