Merge tag 'soc-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-2.6-microblaze.git] / sound / usb / mixer_quirks.c
index 0a3cb8f..a66ce03 100644 (file)
@@ -594,85 +594,208 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
                                          &snd_xonar_u1_output_switch, NULL);
 }
 
+/* Digidesign Mbox 1 helper functions */
+
+static int snd_mbox1_is_spdif_synced(struct snd_usb_audio *chip)
+{
+       unsigned char buff[3];
+       int err;
+       int is_spdif_synced;
+
+       /* Read clock source */
+       err = snd_usb_ctl_msg(chip->dev,
+                             usb_rcvctrlpipe(chip->dev, 0), 0x81,
+                             USB_DIR_IN |
+                             USB_TYPE_CLASS |
+                             USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+       if (err < 0)
+               return err;
+
+       /* spdif sync: buff is all zeroes */
+       is_spdif_synced = !(buff[0] | buff[1] | buff[2]);
+       return is_spdif_synced;
+}
+
+static int snd_mbox1_set_clk_source(struct snd_usb_audio *chip, int rate_or_zero)
+{
+       /* 2 possibilities:     Internal    -> expects sample rate
+        *                      S/PDIF sync -> expects rate = 0
+        */
+       unsigned char buff[3];
+
+       buff[0] = (rate_or_zero >>  0) & 0xff;
+       buff[1] = (rate_or_zero >>  8) & 0xff;
+       buff[2] = (rate_or_zero >> 16) & 0xff;
+
+       /* Set clock source */
+       return snd_usb_ctl_msg(chip->dev,
+                              usb_sndctrlpipe(chip->dev, 0), 0x1,
+                              USB_TYPE_CLASS |
+                              USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+}
+
+static int snd_mbox1_is_spdif_input(struct snd_usb_audio *chip)
+{
+       /* Hardware gives 2 possibilities:      ANALOG Source  -> 0x01
+        *                                      S/PDIF Source  -> 0x02
+        */
+       int err;
+       unsigned char source[1];
+
+       /* Read input source */
+       err = snd_usb_ctl_msg(chip->dev,
+                             usb_rcvctrlpipe(chip->dev, 0), 0x81,
+                             USB_DIR_IN |
+                             USB_TYPE_CLASS |
+                             USB_RECIP_INTERFACE, 0x00, 0x500, source, 1);
+       if (err < 0)
+               return err;
+
+       return (source[0] == 2);
+}
+
+static int snd_mbox1_set_input_source(struct snd_usb_audio *chip, int is_spdif)
+{
+       /* NB: Setting the input source to S/PDIF resets the clock source to S/PDIF
+        * Hardware expects 2 possibilities:    ANALOG Source  -> 0x01
+        *                                      S/PDIF Source  -> 0x02
+        */
+       unsigned char buff[1];
+
+       buff[0] = (is_spdif & 1) + 1;
+
+       /* Set input source */
+       return snd_usb_ctl_msg(chip->dev,
+                              usb_sndctrlpipe(chip->dev, 0), 0x1,
+                              USB_TYPE_CLASS |
+                              USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1);
+}
+
 /* Digidesign Mbox 1 clock source switch (internal/spdif) */
 
-static int snd_mbox1_switch_get(struct snd_kcontrol *kctl,
-                               struct snd_ctl_elem_value *ucontrol)
+static int snd_mbox1_clk_switch_get(struct snd_kcontrol *kctl,
+                                   struct snd_ctl_elem_value *ucontrol)
 {
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
+       struct snd_usb_audio *chip = list->mixer->chip;
+       int err;
+
+       err = snd_usb_lock_shutdown(chip);
+       if (err < 0)
+               goto err;
+
+       err = snd_mbox1_is_spdif_synced(chip);
+       if (err < 0)
+               goto err;
+
+       kctl->private_value = err;
+       err = 0;
        ucontrol->value.enumerated.item[0] = kctl->private_value;
-       return 0;
+err:
+       snd_usb_unlock_shutdown(chip);
+       return err;
 }
 
-static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val)
+static int snd_mbox1_clk_switch_update(struct usb_mixer_interface *mixer, int is_spdif_sync)
 {
        struct snd_usb_audio *chip = mixer->chip;
        int err;
-       unsigned char buff[3];
 
        err = snd_usb_lock_shutdown(chip);
        if (err < 0)
                return err;
 
-       /* Prepare for magic command to toggle clock source */
-       err = snd_usb_ctl_msg(chip->dev,
-                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
-                               USB_DIR_IN |
-                               USB_TYPE_CLASS |
-                               USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1);
+       err = snd_mbox1_is_spdif_input(chip);
        if (err < 0)
                goto err;
-       err = snd_usb_ctl_msg(chip->dev,
-                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
-                               USB_DIR_IN |
-                               USB_TYPE_CLASS |
-                               USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+
+       err = snd_mbox1_is_spdif_synced(chip);
        if (err < 0)
                goto err;
 
-       /* 2 possibilities:     Internal    -> send sample rate
-        *                      S/PDIF sync -> send zeroes
-        * NB: Sample rate locked to 48kHz on purpose to
-        *     prevent user from resetting the sample rate
-        *     while S/PDIF sync is enabled and confusing
-        *     this configuration.
-        */
-       if (val == 0) {
-               buff[0] = 0x80;
-               buff[1] = 0xbb;
-               buff[2] = 0x00;
-       } else {
-               buff[0] = buff[1] = buff[2] = 0x00;
-       }
+       /* FIXME: hardcoded sample rate */
+       err = snd_mbox1_set_clk_source(chip, is_spdif_sync ? 0 : 48000);
+       if (err < 0)
+               goto err;
 
-       /* Send the magic command to toggle the clock source */
-       err = snd_usb_ctl_msg(chip->dev,
-                               usb_sndctrlpipe(chip->dev, 0), 0x1,
-                               USB_TYPE_CLASS |
-                               USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+       err = snd_mbox1_is_spdif_synced(chip);
+err:
+       snd_usb_unlock_shutdown(chip);
+       return err;
+}
+
+static int snd_mbox1_clk_switch_put(struct snd_kcontrol *kctl,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
+       struct usb_mixer_interface *mixer = list->mixer;
+       int err;
+       bool cur_val, new_val;
+
+       cur_val = kctl->private_value;
+       new_val = ucontrol->value.enumerated.item[0];
+       if (cur_val == new_val)
+               return 0;
+
+       kctl->private_value = new_val;
+       err = snd_mbox1_clk_switch_update(mixer, new_val);
+       return err < 0 ? err : 1;
+}
+
+static int snd_mbox1_clk_switch_info(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
+{
+       static const char *const texts[2] = {
+               "Internal",
+               "S/PDIF"
+       };
+
+       return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
+}
+
+static int snd_mbox1_clk_switch_resume(struct usb_mixer_elem_list *list)
+{
+       return snd_mbox1_clk_switch_update(list->mixer, list->kctl->private_value);
+}
+
+/* Digidesign Mbox 1 input source switch (analog/spdif) */
+
+static int snd_mbox1_src_switch_get(struct snd_kcontrol *kctl,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = kctl->private_value;
+       return 0;
+}
+
+static int snd_mbox1_src_switch_update(struct usb_mixer_interface *mixer, int is_spdif_input)
+{
+       struct snd_usb_audio *chip = mixer->chip;
+       int err;
+
+       err = snd_usb_lock_shutdown(chip);
+       if (err < 0)
+               return err;
+
+       err = snd_mbox1_is_spdif_input(chip);
        if (err < 0)
                goto err;
-       err = snd_usb_ctl_msg(chip->dev,
-                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
-                               USB_DIR_IN |
-                               USB_TYPE_CLASS |
-                               USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+
+       err = snd_mbox1_set_input_source(chip, is_spdif_input);
        if (err < 0)
                goto err;
-       err = snd_usb_ctl_msg(chip->dev,
-                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
-                               USB_DIR_IN |
-                               USB_TYPE_CLASS |
-                               USB_RECIP_ENDPOINT, 0x100, 0x2, buff, 3);
+
+       err = snd_mbox1_is_spdif_input(chip);
        if (err < 0)
                goto err;
 
+       err = snd_mbox1_is_spdif_synced(chip);
 err:
        snd_usb_unlock_shutdown(chip);
        return err;
 }
 
-static int snd_mbox1_switch_put(struct snd_kcontrol *kctl,
-                               struct snd_ctl_elem_value *ucontrol)
+static int snd_mbox1_src_switch_put(struct snd_kcontrol *kctl,
+                                   struct snd_ctl_elem_value *ucontrol)
 {
        struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
        struct usb_mixer_interface *mixer = list->mixer;
@@ -685,42 +808,60 @@ static int snd_mbox1_switch_put(struct snd_kcontrol *kctl,
                return 0;
 
        kctl->private_value = new_val;
-       err = snd_mbox1_switch_update(mixer, new_val);
+       err = snd_mbox1_src_switch_update(mixer, new_val);
        return err < 0 ? err : 1;
 }
 
-static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_info *uinfo)
+static int snd_mbox1_src_switch_info(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
 {
        static const char *const texts[2] = {
-               "Internal",
+               "Analog",
                "S/PDIF"
        };
 
        return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
 }
 
-static int snd_mbox1_switch_resume(struct usb_mixer_elem_list *list)
+static int snd_mbox1_src_switch_resume(struct usb_mixer_elem_list *list)
 {
-       return snd_mbox1_switch_update(list->mixer, list->kctl->private_value);
+       return snd_mbox1_src_switch_update(list->mixer, list->kctl->private_value);
 }
 
-static const struct snd_kcontrol_new snd_mbox1_switch = {
+static const struct snd_kcontrol_new snd_mbox1_clk_switch = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "Clock Source",
        .index = 0,
        .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
-       .info = snd_mbox1_switch_info,
-       .get = snd_mbox1_switch_get,
-       .put = snd_mbox1_switch_put,
+       .info = snd_mbox1_clk_switch_info,
+       .get = snd_mbox1_clk_switch_get,
+       .put = snd_mbox1_clk_switch_put,
+       .private_value = 0
+};
+
+static const struct snd_kcontrol_new snd_mbox1_src_switch = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Input Source",
+       .index = 1,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info = snd_mbox1_src_switch_info,
+       .get = snd_mbox1_src_switch_get,
+       .put = snd_mbox1_src_switch_put,
        .private_value = 0
 };
 
-static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer)
+static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer)
 {
-       return add_single_ctl_with_resume(mixer, 0,
-                                         snd_mbox1_switch_resume,
-                                         &snd_mbox1_switch, NULL);
+       int err;
+       err = add_single_ctl_with_resume(mixer, 0,
+                                        snd_mbox1_clk_switch_resume,
+                                        &snd_mbox1_clk_switch, NULL);
+       if (err < 0)
+               return err;
+
+       return add_single_ctl_with_resume(mixer, 1,
+                                         snd_mbox1_src_switch_resume,
+                                         &snd_mbox1_src_switch, NULL);
 }
 
 /* Native Instruments device quirks */
@@ -3029,7 +3170,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
                break;
 
        case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */
-               err = snd_mbox1_create_sync_switch(mixer);
+               err = snd_mbox1_controls_create(mixer);
                break;
 
        case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */