ALSA: usb-audio: Add clock validity quirk for Denon MC7000/MCX8000
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>
This commit is contained in:
Родитель
7dafba3762
Коммит
9f35a31283
|
@ -151,8 +151,34 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
|
||||||
return ret;
|
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,
|
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
|
||||||
int protocol,
|
struct audioformat *fmt,
|
||||||
int source_id)
|
int source_id)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -160,7 +186,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
|
||||||
struct usb_device *dev = chip->dev;
|
struct usb_device *dev = chip->dev;
|
||||||
u32 bmControls;
|
u32 bmControls;
|
||||||
|
|
||||||
if (protocol == UAC_VERSION_3) {
|
if (fmt->protocol == UAC_VERSION_3) {
|
||||||
struct uac3_clock_source_descriptor *cs_desc =
|
struct uac3_clock_source_descriptor *cs_desc =
|
||||||
snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id);
|
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 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)
|
unsigned long *visited, bool validate)
|
||||||
{
|
{
|
||||||
struct uac_clock_source_descriptor *source;
|
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);
|
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
|
||||||
if (source) {
|
if (source) {
|
||||||
entity_id = source->bClockID;
|
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)) {
|
entity_id)) {
|
||||||
usb_audio_err(chip,
|
usb_audio_err(chip,
|
||||||
"clock source %d is not valid, cannot use\n",
|
"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;
|
cur = ret;
|
||||||
ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
|
ret = __uac_clock_find_source(chip, fmt,
|
||||||
visited, validate);
|
selector->baCSourceID[ret - 1],
|
||||||
|
visited, validate);
|
||||||
if (!validate || ret > 0 || !chip->autoclock)
|
if (!validate || ret > 0 || !chip->autoclock)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -260,8 +291,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
|
||||||
if (i == cur)
|
if (i == cur)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
|
ret = __uac_clock_find_source(chip, fmt,
|
||||||
visited, true);
|
selector->baCSourceID[i - 1],
|
||||||
|
visited, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
continue;
|
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 */
|
/* FIXME: multipliers only act as pass-thru element for now */
|
||||||
multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
|
multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
|
||||||
if (multiplier)
|
if (multiplier)
|
||||||
return __uac_clock_find_source(chip, multiplier->bCSourceID,
|
return __uac_clock_find_source(chip, fmt,
|
||||||
visited, validate);
|
multiplier->bCSourceID,
|
||||||
|
visited, validate);
|
||||||
|
|
||||||
return -EINVAL;
|
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,
|
||||||
unsigned long *visited, bool validate)
|
struct audioformat *fmt, int entity_id,
|
||||||
|
unsigned long *visited, bool validate)
|
||||||
{
|
{
|
||||||
struct uac3_clock_source_descriptor *source;
|
struct uac3_clock_source_descriptor *source;
|
||||||
struct uac3_clock_selector_descriptor *selector;
|
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);
|
source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
|
||||||
if (source) {
|
if (source) {
|
||||||
entity_id = source->bClockID;
|
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)) {
|
entity_id)) {
|
||||||
usb_audio_err(chip,
|
usb_audio_err(chip,
|
||||||
"clock source %d is not valid, cannot use\n",
|
"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;
|
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);
|
visited, validate);
|
||||||
if (!validate || ret > 0 || !chip->autoclock)
|
if (!validate || ret > 0 || !chip->autoclock)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -350,8 +385,9 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
|
||||||
if (i == cur)
|
if (i == cur)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1],
|
ret = __uac3_clock_find_source(chip, fmt,
|
||||||
visited, true);
|
selector->baCSourceID[i - 1],
|
||||||
|
visited, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
continue;
|
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,
|
multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
|
||||||
entity_id);
|
entity_id);
|
||||||
if (multiplier)
|
if (multiplier)
|
||||||
return __uac3_clock_find_source(chip, multiplier->bCSourceID,
|
return __uac3_clock_find_source(chip, fmt,
|
||||||
|
multiplier->bCSourceID,
|
||||||
visited, validate);
|
visited, validate);
|
||||||
|
|
||||||
return -EINVAL;
|
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.
|
* 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 snd_usb_clock_find_source(struct snd_usb_audio *chip,
|
||||||
int entity_id, bool validate)
|
struct audioformat *fmt, bool validate)
|
||||||
{
|
{
|
||||||
DECLARE_BITMAP(visited, 256);
|
DECLARE_BITMAP(visited, 256);
|
||||||
memset(visited, 0, sizeof(visited));
|
memset(visited, 0, sizeof(visited));
|
||||||
|
|
||||||
switch (protocol) {
|
switch (fmt->protocol) {
|
||||||
case UAC_VERSION_2:
|
case UAC_VERSION_2:
|
||||||
return __uac_clock_find_source(chip, entity_id, visited,
|
return __uac_clock_find_source(chip, fmt, fmt->clock, visited,
|
||||||
validate);
|
validate);
|
||||||
case UAC_VERSION_3:
|
case UAC_VERSION_3:
|
||||||
return __uac3_clock_find_source(chip, entity_id, visited,
|
return __uac3_clock_find_source(chip, fmt, fmt->clock, visited,
|
||||||
validate);
|
validate);
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
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
|
* automatic clock selection if the current clock is not
|
||||||
* valid.
|
* valid.
|
||||||
*/
|
*/
|
||||||
clock = snd_usb_clock_find_source(chip, fmt->protocol,
|
clock = snd_usb_clock_find_source(chip, fmt, true);
|
||||||
fmt->clock, true);
|
|
||||||
if (clock < 0) {
|
if (clock < 0) {
|
||||||
/* We did not find a valid clock, but that might be
|
/* We did not find a valid clock, but that might be
|
||||||
* because the current sample rate does not match an
|
* 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
|
* and we will do another validation after setting the
|
||||||
* rate.
|
* rate.
|
||||||
*/
|
*/
|
||||||
clock = snd_usb_clock_find_source(chip, fmt->protocol,
|
clock = snd_usb_clock_find_source(chip, fmt, false);
|
||||||
fmt->clock, false);
|
|
||||||
if (clock < 0)
|
if (clock < 0)
|
||||||
return clock;
|
return clock;
|
||||||
}
|
}
|
||||||
|
@ -577,7 +612,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
|
||||||
|
|
||||||
validation:
|
validation:
|
||||||
/* validate clock after rate change */
|
/* 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 -ENXIO;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
||||||
struct usb_host_interface *alts,
|
struct usb_host_interface *alts,
|
||||||
struct audioformat *fmt, int rate);
|
struct audioformat *fmt, int rate);
|
||||||
|
|
||||||
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
|
int snd_usb_clock_find_source(struct snd_usb_audio *chip,
|
||||||
int entity_id, bool validate);
|
struct audioformat *fmt, bool validate);
|
||||||
|
|
||||||
#endif /* __USBAUDIO_CLOCK_H */
|
#endif /* __USBAUDIO_CLOCK_H */
|
||||||
|
|
|
@ -336,8 +336,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
|
||||||
struct usb_device *dev = chip->dev;
|
struct usb_device *dev = chip->dev;
|
||||||
unsigned char tmp[2], *data;
|
unsigned char tmp[2], *data;
|
||||||
int nr_triplets, data_size, ret = 0, ret_l6;
|
int nr_triplets, data_size, ret = 0, ret_l6;
|
||||||
int clock = snd_usb_clock_find_source(chip, fp->protocol,
|
int clock = snd_usb_clock_find_source(chip, fp, false);
|
||||||
fp->clock, false);
|
|
||||||
|
|
||||||
if (clock < 0) {
|
if (clock < 0) {
|
||||||
dev_err(&dev->dev,
|
dev_err(&dev->dev,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче