Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e57f6a6 authored by Alexander Tsoy's avatar Alexander Tsoy Committed by Greg Kroah-Hartman
Browse files

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: default avatarTobias Oszlanyi <toszlanyi@yahoo.de>
Signed-off-by: default avatarAlexander Tsoy <alexander@tsoy.me>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200212235450.697348-1-alexander@tsoy.me


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 59ed2b7a
Loading
Loading
Loading
Loading
+63 −28
Original line number Diff line number Diff line
@@ -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,7 +292,8 @@ 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],
		ret = __uac_clock_find_source(chip, fmt,
					      selector->baCSourceID[ret - 1],
					      visited, validate);
		if (!validate || ret > 0 || !chip->autoclock)
			return ret;
@@ -274,7 +305,8 @@ 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],
			ret = __uac_clock_find_source(chip, fmt,
						      selector->baCSourceID[i - 1],
						      visited, true);
			if (ret < 0)
				continue;
@@ -295,13 +327,15 @@ 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,
		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,
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;
@@ -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,7 +399,8 @@ 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],
			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;
}
+2 −2
Original line number Diff line number Diff line
@@ -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 */
+1 −2
Original line number Diff line number Diff line
@@ -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,