ALSA: usb-audio: Add clock validity quirk for Denon MC7000/MCX8000
authorAlexander Tsoy <alexander@tsoy.me>
Wed, 12 Feb 2020 23:54:50 +0000 (02:54 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 19 Feb 2020 18:51:54 +0000 (19:51 +0100)
commit 9f35a31283775e6f6af73fb2c95c686a4c0acac7 upstream.

It should be safe to ignore clock validity check result if the following
conditions are met:
 - only one single sample rate is supported;
 - the terminal is directly connected to the clock source;
 - the clock type is internal.

This is to deal with some Denon DJ controllers that always reports that
clock is invalid.

Tested-by: Tobias Oszlanyi <toszlanyi@yahoo.de>
Signed-off-by: Alexander Tsoy <alexander@tsoy.me>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200212235450.697348-1-alexander@tsoy.me
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
sound/usb/clock.c
sound/usb/clock.h
sound/usb/format.c

index 4daeb3a..bfe5540 100644 (file)
@@ -165,8 +165,34 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
        return ret;
 }
 
+/*
+ * Assume the clock is valid if clock source supports only one single sample
+ * rate, the terminal is connected directly to it (there is no clock selector)
+ * and clock type is internal. This is to deal with some Denon DJ controllers
+ * that always reports that clock is invalid.
+ */
+static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
+                                           struct audioformat *fmt,
+                                           int source_id)
+{
+       if (fmt->protocol == UAC_VERSION_2) {
+               struct uac_clock_source_descriptor *cs_desc =
+                       snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+               if (!cs_desc)
+                       return false;
+
+               return (fmt->nr_rates == 1 &&
+                       (fmt->clock & 0xff) == cs_desc->bClockID &&
+                       (cs_desc->bmAttributes & 0x3) !=
+                               UAC_CLOCK_SOURCE_TYPE_EXT);
+       }
+
+       return false;
+}
+
 static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
-                                     int protocol,
+                                     struct audioformat *fmt,
                                      int source_id)
 {
        int err;
@@ -174,7 +200,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
        struct usb_device *dev = chip->dev;
        u32 bmControls;
 
-       if (protocol == UAC_VERSION_3) {
+       if (fmt->protocol == UAC_VERSION_3) {
                struct uac3_clock_source_descriptor *cs_desc =
                        snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id);
 
@@ -208,10 +234,14 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
                return false;
        }
 
-       return data ? true :  false;
+       if (data)
+               return true;
+       else
+               return uac_clock_source_is_valid_quirk(chip, fmt, source_id);
 }
 
-static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+static int __uac_clock_find_source(struct snd_usb_audio *chip,
+                                  struct audioformat *fmt, int entity_id,
                                   unsigned long *visited, bool validate)
 {
        struct uac_clock_source_descriptor *source;
@@ -231,7 +261,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
        source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
        if (source) {
                entity_id = source->bClockID;
-               if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2,
+               if (validate && !uac_clock_source_is_valid(chip, fmt,
                                                                entity_id)) {
                        usb_audio_err(chip,
                                "clock source %d is not valid, cannot use\n",
@@ -262,8 +292,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
                }
 
                cur = ret;
-               ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
-                                              visited, validate);
+               ret = __uac_clock_find_source(chip, fmt,
+                                             selector->baCSourceID[ret - 1],
+                                             visited, validate);
                if (!validate || ret > 0 || !chip->autoclock)
                        return ret;
 
@@ -274,8 +305,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
                        if (i == cur)
                                continue;
 
-                       ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
-                               visited, true);
+                       ret = __uac_clock_find_source(chip, fmt,
+                                                     selector->baCSourceID[i - 1],
+                                                     visited, true);
                        if (ret < 0)
                                continue;
 
@@ -295,14 +327,16 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
        /* FIXME: multipliers only act as pass-thru element for now */
        multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
        if (multiplier)
-               return __uac_clock_find_source(chip, multiplier->bCSourceID,
-                                               visited, validate);
+               return __uac_clock_find_source(chip, fmt,
+                                              multiplier->bCSourceID,
+                                              visited, validate);
 
        return -EINVAL;
 }
 
-static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
-                                  unsigned long *visited, bool validate)
+static int __uac3_clock_find_source(struct snd_usb_audio *chip,
+                                   struct audioformat *fmt, int entity_id,
+                                   unsigned long *visited, bool validate)
 {
        struct uac3_clock_source_descriptor *source;
        struct uac3_clock_selector_descriptor *selector;
@@ -321,7 +355,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
        source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
        if (source) {
                entity_id = source->bClockID;
-               if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3,
+               if (validate && !uac_clock_source_is_valid(chip, fmt,
                                                                entity_id)) {
                        usb_audio_err(chip,
                                "clock source %d is not valid, cannot use\n",
@@ -352,7 +386,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
                }
 
                cur = ret;
-               ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1],
+               ret = __uac3_clock_find_source(chip, fmt,
+                                              selector->baCSourceID[ret - 1],
                                               visited, validate);
                if (!validate || ret > 0 || !chip->autoclock)
                        return ret;
@@ -364,8 +399,9 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
                        if (i == cur)
                                continue;
 
-                       ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1],
-                               visited, true);
+                       ret = __uac3_clock_find_source(chip, fmt,
+                                                      selector->baCSourceID[i - 1],
+                                                      visited, true);
                        if (ret < 0)
                                continue;
 
@@ -386,7 +422,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
        multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
                                                      entity_id);
        if (multiplier)
-               return __uac3_clock_find_source(chip, multiplier->bCSourceID,
+               return __uac3_clock_find_source(chip, fmt,
+                                               multiplier->bCSourceID,
                                                visited, validate);
 
        return -EINVAL;
@@ -403,18 +440,18 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
  *
  * Returns the clock source UnitID (>=0) on success, or an error.
  */
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
-                             int entity_id, bool validate)
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+                             struct audioformat *fmt, bool validate)
 {
        DECLARE_BITMAP(visited, 256);
        memset(visited, 0, sizeof(visited));
 
-       switch (protocol) {
+       switch (fmt->protocol) {
        case UAC_VERSION_2:
-               return __uac_clock_find_source(chip, entity_id, visited,
+               return __uac_clock_find_source(chip, fmt, fmt->clock, visited,
                                               validate);
        case UAC_VERSION_3:
-               return __uac3_clock_find_source(chip, entity_id, visited,
+               return __uac3_clock_find_source(chip, fmt, fmt->clock, visited,
                                               validate);
        default:
                return -EINVAL;
@@ -515,8 +552,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
         * automatic clock selection if the current clock is not
         * valid.
         */
-       clock = snd_usb_clock_find_source(chip, fmt->protocol,
-                                         fmt->clock, true);
+       clock = snd_usb_clock_find_source(chip, fmt, true);
        if (clock < 0) {
                /* We did not find a valid clock, but that might be
                 * because the current sample rate does not match an
@@ -524,8 +560,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
                 * and we will do another validation after setting the
                 * rate.
                 */
-               clock = snd_usb_clock_find_source(chip, fmt->protocol,
-                                                 fmt->clock, false);
+               clock = snd_usb_clock_find_source(chip, fmt, false);
                if (clock < 0)
                        return clock;
        }
@@ -591,7 +626,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
 
 validation:
        /* validate clock after rate change */
-       if (!uac_clock_source_is_valid(chip, fmt->protocol, clock))
+       if (!uac_clock_source_is_valid(chip, fmt, clock))
                return -ENXIO;
        return 0;
 }
index 076e31b..68df0fb 100644 (file)
@@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
                             struct usb_host_interface *alts,
                             struct audioformat *fmt, int rate);
 
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
-                            int entity_id, bool validate);
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+                             struct audioformat *fmt, bool validate);
 
 #endif /* __USBAUDIO_CLOCK_H */
index fd13ac1..9d27429 100644 (file)
@@ -306,8 +306,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
        struct usb_device *dev = chip->dev;
        unsigned char tmp[2], *data;
        int nr_triplets, data_size, ret = 0;
-       int clock = snd_usb_clock_find_source(chip, fp->protocol,
-                                             fp->clock, false);
+       int clock = snd_usb_clock_find_source(chip, fp, false);
 
        if (clock < 0) {
                dev_err(&dev->dev,