| From 5a451eff0e9b935221e2528fa1a8e94c3bd8685b Mon Sep 17 00:00:00 2001 |
| From: Alexander Tsoy <alexander@tsoy.me> |
| Date: Thu, 13 Feb 2020 02:54:50 +0300 |
| Subject: [PATCH] ALSA: usb-audio: Add clock validity quirk for Denon |
| MC7000/MCX8000 |
| |
| 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: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/sound/usb/clock.c b/sound/usb/clock.c |
| index 018b1ecb5404..a48313dfa967 100644 |
| --- a/sound/usb/clock.c |
| +++ b/sound/usb/clock.c |
| @@ -151,8 +151,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; |
| @@ -160,7 +186,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); |
| |
| @@ -194,10 +220,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; |
| @@ -217,7 +247,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", |
| @@ -248,8 +278,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; |
| |
| @@ -260,8 +291,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; |
| |
| @@ -281,14 +313,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; |
| @@ -307,7 +341,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", |
| @@ -338,7 +372,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; |
| @@ -350,8 +385,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; |
| |
| @@ -372,7 +408,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; |
| @@ -389,18 +426,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; |
| @@ -501,8 +538,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 |
| @@ -510,8 +546,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; |
| } |
| @@ -577,7 +612,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; |
| } |
| diff --git a/sound/usb/clock.h b/sound/usb/clock.h |
| index 076e31b79ee0..68df0fbe09d0 100644 |
| --- a/sound/usb/clock.h |
| +++ b/sound/usb/clock.h |
| @@ -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 */ |
| diff --git a/sound/usb/format.c b/sound/usb/format.c |
| index c02b51a82775..9e021b1e150e 100644 |
| --- a/sound/usb/format.c |
| +++ b/sound/usb/format.c |
| @@ -295,8 +295,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, |
| -- |
| 2.7.4 |
| |