|
@@ -9,6 +9,8 @@
|
|
|
* Alan Cox (alan@lxorguk.ukuu.org.uk)
|
|
|
* Thomas Sailer (sailer@ife.ee.ethz.ch)
|
|
|
*
|
|
|
+ * Audio Advantage Micro II support added by:
|
|
|
+ * Przemek Rudy (prudy1@o2.pl)
|
|
|
*
|
|
|
* 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
|
|
@@ -30,6 +32,7 @@
|
|
|
#include <linux/usb.h>
|
|
|
#include <linux/usb/audio.h>
|
|
|
|
|
|
+#include <sound/asoundef.h>
|
|
|
#include <sound/core.h>
|
|
|
#include <sound/control.h>
|
|
|
#include <sound/hwdep.h>
|
|
@@ -1315,6 +1318,211 @@ static struct std_mono_table ebox44_table[] = {
|
|
|
{}
|
|
|
};
|
|
|
|
|
|
+/* Audio Advantage Micro II findings:
|
|
|
+ *
|
|
|
+ * Mapping spdif AES bits to vendor register.bit:
|
|
|
+ * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00
|
|
|
+ * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01
|
|
|
+ * AES2: [0 0 0 0 0 0 0 0]
|
|
|
+ * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request
|
|
|
+ * (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices
|
|
|
+ *
|
|
|
+ * power on values:
|
|
|
+ * r2: 0x10
|
|
|
+ * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set
|
|
|
+ * just after it to 0xa0, presumably it disables/mutes some analog
|
|
|
+ * parts when there is no audio.)
|
|
|
+ * r9: 0x28
|
|
|
+ *
|
|
|
+ * Optical transmitter on/off:
|
|
|
+ * vendor register.bit: 9.1
|
|
|
+ * 0 - on (0x28 register value)
|
|
|
+ * 1 - off (0x2a register value)
|
|
|
+ *
|
|
|
+ */
|
|
|
+static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_info *uinfo)
|
|
|
+{
|
|
|
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
+ uinfo->count = 1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
|
|
|
+ int err;
|
|
|
+ struct usb_interface *iface;
|
|
|
+ struct usb_host_interface *alts;
|
|
|
+ unsigned int ep;
|
|
|
+ unsigned char data[3];
|
|
|
+ int rate;
|
|
|
+
|
|
|
+ ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
|
|
|
+ ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
|
|
|
+ ucontrol->value.iec958.status[2] = 0x00;
|
|
|
+
|
|
|
+ /* use known values for that card: interface#1 altsetting#1 */
|
|
|
+ iface = usb_ifnum_to_if(mixer->chip->dev, 1);
|
|
|
+ alts = &iface->altsetting[1];
|
|
|
+ ep = get_endpoint(alts, 0)->bEndpointAddress;
|
|
|
+
|
|
|
+ err = snd_usb_ctl_msg(mixer->chip->dev,
|
|
|
+ usb_rcvctrlpipe(mixer->chip->dev, 0),
|
|
|
+ UAC_GET_CUR,
|
|
|
+ USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
|
|
|
+ UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
|
|
|
+ ep,
|
|
|
+ data,
|
|
|
+ sizeof(data));
|
|
|
+ if (err < 0)
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ rate = data[0] | (data[1] << 8) | (data[2] << 16);
|
|
|
+ ucontrol->value.iec958.status[3] = (rate == 48000) ?
|
|
|
+ IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
|
|
|
+
|
|
|
+ err = 0;
|
|
|
+end:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
|
|
|
+ int err;
|
|
|
+ u8 reg;
|
|
|
+ unsigned long priv_backup = kcontrol->private_value;
|
|
|
+
|
|
|
+ reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) |
|
|
|
+ (ucontrol->value.iec958.status[0] & 0x0f);
|
|
|
+ err = snd_usb_ctl_msg(mixer->chip->dev,
|
|
|
+ usb_sndctrlpipe(mixer->chip->dev, 0),
|
|
|
+ UAC_SET_CUR,
|
|
|
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
|
|
+ reg,
|
|
|
+ 2,
|
|
|
+ NULL,
|
|
|
+ 0);
|
|
|
+ if (err < 0)
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ kcontrol->private_value &= 0xfffff0f0;
|
|
|
+ kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
|
|
|
+ kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f);
|
|
|
+
|
|
|
+ reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ?
|
|
|
+ 0xa0 : 0x20;
|
|
|
+ reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f;
|
|
|
+ err = snd_usb_ctl_msg(mixer->chip->dev,
|
|
|
+ usb_sndctrlpipe(mixer->chip->dev, 0),
|
|
|
+ UAC_SET_CUR,
|
|
|
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
|
|
+ reg,
|
|
|
+ 3,
|
|
|
+ NULL,
|
|
|
+ 0);
|
|
|
+ if (err < 0)
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ kcontrol->private_value &= 0xffff0fff;
|
|
|
+ kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
|
|
|
+
|
|
|
+ /* The frequency bits in AES3 cannot be set via register access. */
|
|
|
+
|
|
|
+ /* Silently ignore any bits from the request that cannot be set. */
|
|
|
+
|
|
|
+ err = (priv_backup != kcontrol->private_value);
|
|
|
+end:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ ucontrol->value.iec958.status[0] = 0x0f;
|
|
|
+ ucontrol->value.iec958.status[1] = 0xff;
|
|
|
+ ucontrol->value.iec958.status[2] = 0x00;
|
|
|
+ ucontrol->value.iec958.status[3] = 0x00;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
|
|
|
+ int err;
|
|
|
+ u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
|
|
|
+
|
|
|
+ err = snd_usb_ctl_msg(mixer->chip->dev,
|
|
|
+ usb_sndctrlpipe(mixer->chip->dev, 0),
|
|
|
+ UAC_SET_CUR,
|
|
|
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
|
|
+ reg,
|
|
|
+ 9,
|
|
|
+ NULL,
|
|
|
+ 0);
|
|
|
+
|
|
|
+ if (!err) {
|
|
|
+ err = (reg != (kcontrol->private_value & 0x0ff));
|
|
|
+ if (err)
|
|
|
+ kcontrol->private_value = reg;
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
|
|
|
+ .info = snd_microii_spdif_info,
|
|
|
+ .get = snd_microii_spdif_default_get,
|
|
|
+ .put = snd_microii_spdif_default_put,
|
|
|
+ .private_value = 0x00000100UL,/* reset value */
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
|
|
|
+ .info = snd_microii_spdif_info,
|
|
|
+ .get = snd_microii_spdif_mask_get,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
|
|
|
+ .info = snd_ctl_boolean_mono_info,
|
|
|
+ .get = snd_microii_spdif_switch_get,
|
|
|
+ .put = snd_microii_spdif_switch_put,
|
|
|
+ .private_value = 0x00000028UL,/* reset value */
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
|
|
|
+{
|
|
|
+ int err, i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
|
|
|
+ err = snd_ctl_add(mixer->chip->card,
|
|
|
+ snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer));
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
|
|
{
|
|
|
int err = 0;
|
|
@@ -1353,6 +1561,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
|
|
err = snd_xonar_u1_controls_create(mixer);
|
|
|
break;
|
|
|
|
|
|
+ case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */
|
|
|
+ err = snd_microii_controls_create(mixer);
|
|
|
+ break;
|
|
|
+
|
|
|
case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
|
|
|
err = snd_nativeinstruments_create_mixer(mixer,
|
|
|
snd_nativeinstruments_ta6_mixers,
|