|
@@ -29,6 +29,7 @@
|
|
|
|
|
|
#include <linux/hid.h>
|
|
|
#include <linux/init.h>
|
|
|
+#include <linux/math64.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/usb.h>
|
|
|
#include <linux/usb/audio.h>
|
|
@@ -1817,6 +1818,380 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* RME Class Compliant device quirks */
|
|
|
+
|
|
|
+#define SND_RME_GET_STATUS1 23
|
|
|
+#define SND_RME_GET_CURRENT_FREQ 17
|
|
|
+#define SND_RME_CLK_SYSTEM_SHIFT 16
|
|
|
+#define SND_RME_CLK_SYSTEM_MASK 0x1f
|
|
|
+#define SND_RME_CLK_AES_SHIFT 8
|
|
|
+#define SND_RME_CLK_SPDIF_SHIFT 12
|
|
|
+#define SND_RME_CLK_AES_SPDIF_MASK 0xf
|
|
|
+#define SND_RME_CLK_SYNC_SHIFT 6
|
|
|
+#define SND_RME_CLK_SYNC_MASK 0x3
|
|
|
+#define SND_RME_CLK_FREQMUL_SHIFT 18
|
|
|
+#define SND_RME_CLK_FREQMUL_MASK 0x7
|
|
|
+#define SND_RME_CLK_SYSTEM(x) \
|
|
|
+ ((x >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK)
|
|
|
+#define SND_RME_CLK_AES(x) \
|
|
|
+ ((x >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
|
|
|
+#define SND_RME_CLK_SPDIF(x) \
|
|
|
+ ((x >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
|
|
|
+#define SND_RME_CLK_SYNC(x) \
|
|
|
+ ((x >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK)
|
|
|
+#define SND_RME_CLK_FREQMUL(x) \
|
|
|
+ ((x >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK)
|
|
|
+#define SND_RME_CLK_AES_LOCK 0x1
|
|
|
+#define SND_RME_CLK_AES_SYNC 0x4
|
|
|
+#define SND_RME_CLK_SPDIF_LOCK 0x2
|
|
|
+#define SND_RME_CLK_SPDIF_SYNC 0x8
|
|
|
+#define SND_RME_SPDIF_IF_SHIFT 4
|
|
|
+#define SND_RME_SPDIF_FORMAT_SHIFT 5
|
|
|
+#define SND_RME_BINARY_MASK 0x1
|
|
|
+#define SND_RME_SPDIF_IF(x) \
|
|
|
+ ((x >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK)
|
|
|
+#define SND_RME_SPDIF_FORMAT(x) \
|
|
|
+ ((x >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK)
|
|
|
+
|
|
|
+static const u32 snd_rme_rate_table[] = {
|
|
|
+ 32000, 44100, 48000, 50000,
|
|
|
+ 64000, 88200, 96000, 100000,
|
|
|
+ 128000, 176400, 192000, 200000,
|
|
|
+ 256000, 352800, 384000, 400000,
|
|
|
+ 512000, 705600, 768000, 800000
|
|
|
+};
|
|
|
+/* maximum number of items for AES and S/PDIF rates for above table */
|
|
|
+#define SND_RME_RATE_IDX_AES_SPDIF_NUM 12
|
|
|
+
|
|
|
+enum snd_rme_domain {
|
|
|
+ SND_RME_DOMAIN_SYSTEM,
|
|
|
+ SND_RME_DOMAIN_AES,
|
|
|
+ SND_RME_DOMAIN_SPDIF
|
|
|
+};
|
|
|
+
|
|
|
+enum snd_rme_clock_status {
|
|
|
+ SND_RME_CLOCK_NOLOCK,
|
|
|
+ SND_RME_CLOCK_LOCK,
|
|
|
+ SND_RME_CLOCK_SYNC
|
|
|
+};
|
|
|
+
|
|
|
+static int snd_rme_read_value(struct snd_usb_audio *chip,
|
|
|
+ unsigned int item,
|
|
|
+ u32 *value)
|
|
|
+{
|
|
|
+ struct usb_device *dev = chip->dev;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
|
+ item,
|
|
|
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
+ 0, 0,
|
|
|
+ value, sizeof(*value));
|
|
|
+ if (err < 0)
|
|
|
+ dev_err(&dev->dev,
|
|
|
+ "unable to issue vendor read request %d (ret = %d)",
|
|
|
+ item, err);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_get_status1(struct snd_kcontrol *kcontrol,
|
|
|
+ u32 *status1)
|
|
|
+{
|
|
|
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
|
|
|
+ struct snd_usb_audio *chip = list->mixer->chip;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_usb_lock_shutdown(chip);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, status1);
|
|
|
+ snd_usb_unlock_shutdown(chip);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_rate_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ u32 status1;
|
|
|
+ u32 rate = 0;
|
|
|
+ int idx;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_rme_get_status1(kcontrol, &status1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ switch (kcontrol->private_value) {
|
|
|
+ case SND_RME_DOMAIN_SYSTEM:
|
|
|
+ idx = SND_RME_CLK_SYSTEM(status1);
|
|
|
+ if (idx < ARRAY_SIZE(snd_rme_rate_table))
|
|
|
+ rate = snd_rme_rate_table[idx];
|
|
|
+ break;
|
|
|
+ case SND_RME_DOMAIN_AES:
|
|
|
+ idx = SND_RME_CLK_AES(status1);
|
|
|
+ if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
|
|
|
+ rate = snd_rme_rate_table[idx];
|
|
|
+ break;
|
|
|
+ case SND_RME_DOMAIN_SPDIF:
|
|
|
+ idx = SND_RME_CLK_SPDIF(status1);
|
|
|
+ if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
|
|
|
+ rate = snd_rme_rate_table[idx];
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ ucontrol->value.integer.value[0] = rate;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_sync_state_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ u32 status1;
|
|
|
+ int idx = SND_RME_CLOCK_NOLOCK;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_rme_get_status1(kcontrol, &status1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ switch (kcontrol->private_value) {
|
|
|
+ case SND_RME_DOMAIN_AES: /* AES */
|
|
|
+ if (status1 & SND_RME_CLK_AES_SYNC)
|
|
|
+ idx = SND_RME_CLOCK_SYNC;
|
|
|
+ else if (status1 & SND_RME_CLK_AES_LOCK)
|
|
|
+ idx = SND_RME_CLOCK_LOCK;
|
|
|
+ break;
|
|
|
+ case SND_RME_DOMAIN_SPDIF: /* SPDIF */
|
|
|
+ if (status1 & SND_RME_CLK_SPDIF_SYNC)
|
|
|
+ idx = SND_RME_CLOCK_SYNC;
|
|
|
+ else if (status1 & SND_RME_CLK_SPDIF_LOCK)
|
|
|
+ idx = SND_RME_CLOCK_LOCK;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ ucontrol->value.enumerated.item[0] = idx;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_spdif_if_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ u32 status1;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_rme_get_status1(kcontrol, &status1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_IF(status1);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_spdif_format_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ u32 status1;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_rme_get_status1(kcontrol, &status1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_FORMAT(status1);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_sync_source_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ u32 status1;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_rme_get_status1(kcontrol, &status1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ ucontrol->value.enumerated.item[0] = SND_RME_CLK_SYNC(status1);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_current_freq_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
|
|
|
+ struct snd_usb_audio *chip = list->mixer->chip;
|
|
|
+ u32 status1;
|
|
|
+ const u64 num = 104857600000000ULL;
|
|
|
+ u32 den;
|
|
|
+ unsigned int freq;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = snd_usb_lock_shutdown(chip);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, &status1);
|
|
|
+ if (err < 0)
|
|
|
+ goto end;
|
|
|
+ err = snd_rme_read_value(chip, SND_RME_GET_CURRENT_FREQ, &den);
|
|
|
+ if (err < 0)
|
|
|
+ goto end;
|
|
|
+ freq = (den == 0) ? 0 : div64_u64(num, den);
|
|
|
+ freq <<= SND_RME_CLK_FREQMUL(status1);
|
|
|
+ ucontrol->value.integer.value[0] = freq;
|
|
|
+
|
|
|
+end:
|
|
|
+ snd_usb_unlock_shutdown(chip);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_rate_info(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_info *uinfo)
|
|
|
+{
|
|
|
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
|
+ uinfo->count = 1;
|
|
|
+ switch (kcontrol->private_value) {
|
|
|
+ case SND_RME_DOMAIN_SYSTEM:
|
|
|
+ uinfo->value.integer.min = 32000;
|
|
|
+ uinfo->value.integer.max = 800000;
|
|
|
+ break;
|
|
|
+ case SND_RME_DOMAIN_AES:
|
|
|
+ case SND_RME_DOMAIN_SPDIF:
|
|
|
+ default:
|
|
|
+ uinfo->value.integer.min = 0;
|
|
|
+ uinfo->value.integer.max = 200000;
|
|
|
+ }
|
|
|
+ uinfo->value.integer.step = 0;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_sync_state_info(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_info *uinfo)
|
|
|
+{
|
|
|
+ static const char *const sync_states[] = {
|
|
|
+ "No Lock", "Lock", "Sync"
|
|
|
+ };
|
|
|
+
|
|
|
+ return snd_ctl_enum_info(uinfo, 1,
|
|
|
+ ARRAY_SIZE(sync_states), sync_states);
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_spdif_if_info(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_info *uinfo)
|
|
|
+{
|
|
|
+ static const char *const spdif_if[] = {
|
|
|
+ "Coaxial", "Optical"
|
|
|
+ };
|
|
|
+
|
|
|
+ return snd_ctl_enum_info(uinfo, 1,
|
|
|
+ ARRAY_SIZE(spdif_if), spdif_if);
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_spdif_format_info(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_info *uinfo)
|
|
|
+{
|
|
|
+ static const char *const optical_type[] = {
|
|
|
+ "Consumer", "Professional"
|
|
|
+ };
|
|
|
+
|
|
|
+ return snd_ctl_enum_info(uinfo, 1,
|
|
|
+ ARRAY_SIZE(optical_type), optical_type);
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_rme_sync_source_info(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_info *uinfo)
|
|
|
+{
|
|
|
+ static const char *const sync_sources[] = {
|
|
|
+ "Internal", "AES", "SPDIF", "Internal"
|
|
|
+ };
|
|
|
+
|
|
|
+ return snd_ctl_enum_info(uinfo, 1,
|
|
|
+ ARRAY_SIZE(sync_sources), sync_sources);
|
|
|
+}
|
|
|
+
|
|
|
+static struct snd_kcontrol_new snd_rme_controls[] = {
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "AES Rate",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_rate_info,
|
|
|
+ .get = snd_rme_rate_get,
|
|
|
+ .private_value = SND_RME_DOMAIN_AES
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "AES Sync",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_sync_state_info,
|
|
|
+ .get = snd_rme_sync_state_get,
|
|
|
+ .private_value = SND_RME_DOMAIN_AES
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "SPDIF Rate",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_rate_info,
|
|
|
+ .get = snd_rme_rate_get,
|
|
|
+ .private_value = SND_RME_DOMAIN_SPDIF
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "SPDIF Sync",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_sync_state_info,
|
|
|
+ .get = snd_rme_sync_state_get,
|
|
|
+ .private_value = SND_RME_DOMAIN_SPDIF
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "SPDIF Interface",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_spdif_if_info,
|
|
|
+ .get = snd_rme_spdif_if_get,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "SPDIF Format",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_spdif_format_info,
|
|
|
+ .get = snd_rme_spdif_format_get,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "Sync Source",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_sync_source_info,
|
|
|
+ .get = snd_rme_sync_source_get
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "System Rate",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_rate_info,
|
|
|
+ .get = snd_rme_rate_get,
|
|
|
+ .private_value = SND_RME_DOMAIN_SYSTEM
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
+ .name = "Current Frequency",
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
+ .info = snd_rme_rate_info,
|
|
|
+ .get = snd_rme_current_freq_get
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+static int snd_rme_controls_create(struct usb_mixer_interface *mixer)
|
|
|
+{
|
|
|
+ int err, i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(snd_rme_controls); ++i) {
|
|
|
+ err = add_single_ctl_with_resume(mixer, 0,
|
|
|
+ NULL,
|
|
|
+ &snd_rme_controls[i],
|
|
|
+ NULL);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
|
|
{
|
|
|
int err = 0;
|
|
@@ -1904,6 +2279,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
|
|
case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
|
|
|
err = dell_dock_mixer_init(mixer);
|
|
|
break;
|
|
|
+
|
|
|
+ case USB_ID(0x2a39, 0x3fd2): /* RME ADI-2 Pro */
|
|
|
+ case USB_ID(0x2a39, 0x3fd3): /* RME ADI-2 DAC */
|
|
|
+ case USB_ID(0x2a39, 0x3fd4): /* RME */
|
|
|
+ err = snd_rme_controls_create(mixer);
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
return err;
|