diff options
| author | Takashi Iwai <[email protected]> | 2018-04-02 19:50:41 +0200 | 
|---|---|---|
| committer | Takashi Iwai <[email protected]> | 2018-04-02 19:50:59 +0200 | 
| commit | bc334cb61b9ee6e85b9bb01519989a3ae8fe03f6 (patch) | |
| tree | 34424a812537fe11beeee727b30b19c67193fe36 /sound/usb/clock.c | |
| parent | 5607dddbfca774fb38bffadcb077fe03aa4ac5c6 (diff) | |
| parent | b44d419b98fae759b4f746186b1d1c8d01d962f2 (diff) | |
Merge branch 'for-next' into for-linus
Preparation for 4.17 merge.
Signed-off-by: Takashi Iwai <[email protected]>
Diffstat (limited to 'sound/usb/clock.c')
| -rw-r--r-- | sound/usb/clock.c | 231 | 
1 files changed, 205 insertions, 26 deletions
diff --git a/sound/usb/clock.c b/sound/usb/clock.c index eb3396ffba4c..ab39ccb974c6 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -23,6 +23,7 @@  #include <linux/usb.h>  #include <linux/usb/audio.h>  #include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h>  #include <sound/core.h>  #include <sound/info.h> @@ -50,6 +51,22 @@ static struct uac_clock_source_descriptor *  	return NULL;  } +static struct uac3_clock_source_descriptor * +	snd_usb_find_clock_source_v3(struct usb_host_interface *ctrl_iface, +				  int clock_id) +{ +	struct uac3_clock_source_descriptor *cs = NULL; + +	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, +					     ctrl_iface->extralen, +					     cs, UAC3_CLOCK_SOURCE))) { +		if (cs->bClockID == clock_id) +			return cs; +	} + +	return NULL; +} +  static struct uac_clock_selector_descriptor *  	snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,  				    int clock_id) @@ -69,6 +86,22 @@ static struct uac_clock_selector_descriptor *  	return NULL;  } +static struct uac3_clock_selector_descriptor * +	snd_usb_find_clock_selector_v3(struct usb_host_interface *ctrl_iface, +				    int clock_id) +{ +	struct uac3_clock_selector_descriptor *cs = NULL; + +	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, +					     ctrl_iface->extralen, +					     cs, UAC3_CLOCK_SELECTOR))) { +		if (cs->bClockID == clock_id) +			return cs; +	} + +	return NULL; +} +  static struct uac_clock_multiplier_descriptor *  	snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,  				      int clock_id) @@ -85,6 +118,22 @@ static struct uac_clock_multiplier_descriptor *  	return NULL;  } +static struct uac3_clock_multiplier_descriptor * +	snd_usb_find_clock_multiplier_v3(struct usb_host_interface *ctrl_iface, +				      int clock_id) +{ +	struct uac3_clock_multiplier_descriptor *cs = NULL; + +	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, +					     ctrl_iface->extralen, +					     cs, UAC3_CLOCK_MULTIPLIER))) { +		if (cs->bClockID == clock_id) +			return cs; +	} + +	return NULL; +} +  static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)  {  	unsigned char buf; @@ -138,20 +187,34 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i  	return ret;  } -static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) +static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, +				      int protocol, +				      int source_id)  {  	int err;  	unsigned char data;  	struct usb_device *dev = chip->dev; -	struct uac_clock_source_descriptor *cs_desc = -		snd_usb_find_clock_source(chip->ctrl_intf, source_id); - -	if (!cs_desc) -		return 0; +	u32 bmControls; + +	if (protocol == UAC_VERSION_3) { +		struct uac3_clock_source_descriptor *cs_desc = +			snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id); + +		if (!cs_desc) +			return 0; +		bmControls = le32_to_cpu(cs_desc->bmControls); +	} else { /* UAC_VERSION_1/2 */ +		struct uac_clock_source_descriptor *cs_desc = +			snd_usb_find_clock_source(chip->ctrl_intf, source_id); + +		if (!cs_desc) +			return 0; +		bmControls = cs_desc->bmControls; +	}  	/* If a clock source can't tell us whether it's valid, we assume it is */ -	if (!uac2_control_is_readable(cs_desc->bmControls, -				      UAC2_CS_CONTROL_CLOCK_VALID - 1)) +	if (!uac_v2v3_control_is_readable(bmControls, +				      UAC2_CS_CONTROL_CLOCK_VALID))  		return 1;  	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, @@ -170,9 +233,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)  	return !!data;  } -static int __uac_clock_find_source(struct snd_usb_audio *chip, -				   int entity_id, unsigned long *visited, -				   bool validate) +static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, +				   unsigned long *visited, bool validate)  {  	struct uac_clock_source_descriptor *source;  	struct uac_clock_selector_descriptor *selector; @@ -191,7 +253,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,  	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, entity_id)) { +		if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2, +								entity_id)) {  			usb_audio_err(chip,  				"clock source %d is not valid, cannot use\n",  				entity_id); @@ -260,6 +323,97 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,  	return -EINVAL;  } +static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, +				   unsigned long *visited, bool validate) +{ +	struct uac3_clock_source_descriptor *source; +	struct uac3_clock_selector_descriptor *selector; +	struct uac3_clock_multiplier_descriptor *multiplier; + +	entity_id &= 0xff; + +	if (test_and_set_bit(entity_id, visited)) { +		usb_audio_warn(chip, +			 "%s(): recursive clock topology detected, id %d.\n", +			 __func__, entity_id); +		return -EINVAL; +	} + +	/* first, see if the ID we're looking for is a clock source already */ +	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, +								entity_id)) { +			usb_audio_err(chip, +				"clock source %d is not valid, cannot use\n", +				entity_id); +			return -ENXIO; +		} +		return entity_id; +	} + +	selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id); +	if (selector) { +		int ret, i, cur; + +		/* the entity ID we are looking for is a selector. +		 * find out what it currently selects */ +		ret = uac_clock_selector_get_val(chip, selector->bClockID); +		if (ret < 0) +			return ret; + +		/* Selector values are one-based */ + +		if (ret > selector->bNrInPins || ret < 1) { +			usb_audio_err(chip, +				"%s(): selector reported illegal value, id %d, ret %d\n", +				__func__, selector->bClockID, ret); + +			return -EINVAL; +		} + +		cur = ret; +		ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1], +					       visited, validate); +		if (!validate || ret > 0 || !chip->autoclock) +			return ret; + +		/* The current clock source is invalid, try others. */ +		for (i = 1; i <= selector->bNrInPins; i++) { +			int err; + +			if (i == cur) +				continue; + +			ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1], +				visited, true); +			if (ret < 0) +				continue; + +			err = uac_clock_selector_set_val(chip, entity_id, i); +			if (err < 0) +				continue; + +			usb_audio_info(chip, +				 "found and selected valid clock source %d\n", +				 ret); +			return ret; +		} + +		return -ENXIO; +	} + +	/* FIXME: multipliers only act as pass-thru element for now */ +	multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf, +						      entity_id); +	if (multiplier) +		return __uac3_clock_find_source(chip, multiplier->bCSourceID, +						visited, validate); + +	return -EINVAL; +} +  /*   * For all kinds of sample rate settings and other device queries,   * the clock source (end-leaf) must be used. However, clock selectors, @@ -271,12 +425,22 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,   *   * Returns the clock source UnitID (>=0) on success, or an error.   */ -int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id, -			      bool validate) +int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, +			      int entity_id, bool validate)  {  	DECLARE_BITMAP(visited, 256);  	memset(visited, 0, sizeof(visited)); -	return __uac_clock_find_source(chip, entity_id, visited, validate); + +	switch (protocol) { +	case UAC_VERSION_2: +		return __uac_clock_find_source(chip, entity_id, visited, +					       validate); +	case UAC_VERSION_3: +		return __uac3_clock_find_source(chip, entity_id, visited, +					       validate); +	default: +		return -EINVAL; +	}  }  static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, @@ -335,7 +499,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,  	return 0;  } -static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface, +static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,  			      int altsetting, int clock)  {  	struct usb_device *dev = chip->dev; @@ -348,7 +512,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,  			      snd_usb_ctrl_intf(chip) | (clock << 8),  			      &data, sizeof(data));  	if (err < 0) { -		dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n", +		dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n",  			 iface, altsetting, err);  		return 0;  	} @@ -356,7 +520,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,  	return le32_to_cpu(data);  } -static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, +static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,  			      struct usb_host_interface *alts,  			      struct audioformat *fmt, int rate)  { @@ -365,18 +529,31 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,  	int err, cur_rate, prev_rate;  	int clock;  	bool writeable; -	struct uac_clock_source_descriptor *cs_desc; +	u32 bmControls; -	clock = snd_usb_clock_find_source(chip, fmt->clock, true); +	clock = snd_usb_clock_find_source(chip, fmt->protocol, +					  fmt->clock, true);  	if (clock < 0)  		return clock; -	prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock); +	prev_rate = get_sample_rate_v2v3(chip, iface, fmt->altsetting, clock);  	if (prev_rate == rate)  		return 0; -	cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); -	writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1); +	if (fmt->protocol == UAC_VERSION_3) { +		struct uac3_clock_source_descriptor *cs_desc; + +		cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock); +		bmControls = le32_to_cpu(cs_desc->bmControls); +	} else { +		struct uac_clock_source_descriptor *cs_desc; + +		cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); +		bmControls = cs_desc->bmControls; +	} + +	writeable = uac_v2v3_control_is_writeable(bmControls, +						  UAC2_CS_CONTROL_SAM_FREQ);  	if (writeable) {  		data = cpu_to_le32(rate);  		err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, @@ -386,12 +563,13 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,  				      &data, sizeof(data));  		if (err < 0) {  			usb_audio_err(chip, -				"%d:%d: cannot set freq %d (v2): err %d\n", +				"%d:%d: cannot set freq %d (v2/v3): err %d\n",  				iface, fmt->altsetting, rate, err);  			return err;  		} -		cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock); +		cur_rate = get_sample_rate_v2v3(chip, iface, +						fmt->altsetting, clock);  	} else {  		cur_rate = prev_rate;  	} @@ -430,7 +608,8 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,  		return set_sample_rate_v1(chip, iface, alts, fmt, rate);  	case UAC_VERSION_2: -		return set_sample_rate_v2(chip, iface, alts, fmt, rate); +	case UAC_VERSION_3: +		return set_sample_rate_v2v3(chip, iface, alts, fmt, rate);  	}  }  |