xref: /openbmc/linux/sound/usb/clock.c (revision 57dd5414)
179f920fbSDaniel Mack /*
279f920fbSDaniel Mack  *   Clock domain and sample rate management functions
379f920fbSDaniel Mack  *
479f920fbSDaniel Mack  *   This program is free software; you can redistribute it and/or modify
579f920fbSDaniel Mack  *   it under the terms of the GNU General Public License as published by
679f920fbSDaniel Mack  *   the Free Software Foundation; either version 2 of the License, or
779f920fbSDaniel Mack  *   (at your option) any later version.
879f920fbSDaniel Mack  *
979f920fbSDaniel Mack  *   This program is distributed in the hope that it will be useful,
1079f920fbSDaniel Mack  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1179f920fbSDaniel Mack  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1279f920fbSDaniel Mack  *   GNU General Public License for more details.
1379f920fbSDaniel Mack  *
1479f920fbSDaniel Mack  *   You should have received a copy of the GNU General Public License
1579f920fbSDaniel Mack  *   along with this program; if not, write to the Free Software
1679f920fbSDaniel Mack  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
1779f920fbSDaniel Mack  *
1879f920fbSDaniel Mack  */
1979f920fbSDaniel Mack 
2079f920fbSDaniel Mack #include <linux/bitops.h>
2179f920fbSDaniel Mack #include <linux/init.h>
2279f920fbSDaniel Mack #include <linux/string.h>
2379f920fbSDaniel Mack #include <linux/usb.h>
2479f920fbSDaniel Mack #include <linux/usb/audio.h>
2579f920fbSDaniel Mack #include <linux/usb/audio-v2.h>
2679f920fbSDaniel Mack 
2779f920fbSDaniel Mack #include <sound/core.h>
2879f920fbSDaniel Mack #include <sound/info.h>
2979f920fbSDaniel Mack #include <sound/pcm.h>
3079f920fbSDaniel Mack 
3179f920fbSDaniel Mack #include "usbaudio.h"
3279f920fbSDaniel Mack #include "card.h"
3379f920fbSDaniel Mack #include "helper.h"
34f22aa949SDaniel Mack #include "clock.h"
3521bb5aafSDaniel Mack #include "quirks.h"
3679f920fbSDaniel Mack 
3779f920fbSDaniel Mack static struct uac_clock_source_descriptor *
3879f920fbSDaniel Mack 	snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
3979f920fbSDaniel Mack 				  int clock_id)
4079f920fbSDaniel Mack {
4179f920fbSDaniel Mack 	struct uac_clock_source_descriptor *cs = NULL;
4279f920fbSDaniel Mack 
4379f920fbSDaniel Mack 	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
4479f920fbSDaniel Mack 					     ctrl_iface->extralen,
4579f920fbSDaniel Mack 					     cs, UAC2_CLOCK_SOURCE))) {
4679f920fbSDaniel Mack 		if (cs->bClockID == clock_id)
4779f920fbSDaniel Mack 			return cs;
4879f920fbSDaniel Mack 	}
4979f920fbSDaniel Mack 
5079f920fbSDaniel Mack 	return NULL;
5179f920fbSDaniel Mack }
5279f920fbSDaniel Mack 
5379f920fbSDaniel Mack static struct uac_clock_selector_descriptor *
5479f920fbSDaniel Mack 	snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
5579f920fbSDaniel Mack 				    int clock_id)
5679f920fbSDaniel Mack {
5779f920fbSDaniel Mack 	struct uac_clock_selector_descriptor *cs = NULL;
5879f920fbSDaniel Mack 
5979f920fbSDaniel Mack 	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
6079f920fbSDaniel Mack 					     ctrl_iface->extralen,
6179f920fbSDaniel Mack 					     cs, UAC2_CLOCK_SELECTOR))) {
6279f920fbSDaniel Mack 		if (cs->bClockID == clock_id)
6379f920fbSDaniel Mack 			return cs;
6479f920fbSDaniel Mack 	}
6579f920fbSDaniel Mack 
6679f920fbSDaniel Mack 	return NULL;
6779f920fbSDaniel Mack }
6879f920fbSDaniel Mack 
6979f920fbSDaniel Mack static struct uac_clock_multiplier_descriptor *
7079f920fbSDaniel Mack 	snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
7179f920fbSDaniel Mack 				      int clock_id)
7279f920fbSDaniel Mack {
7379f920fbSDaniel Mack 	struct uac_clock_multiplier_descriptor *cs = NULL;
7479f920fbSDaniel Mack 
7579f920fbSDaniel Mack 	while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
7679f920fbSDaniel Mack 					     ctrl_iface->extralen,
7779f920fbSDaniel Mack 					     cs, UAC2_CLOCK_MULTIPLIER))) {
7879f920fbSDaniel Mack 		if (cs->bClockID == clock_id)
7979f920fbSDaniel Mack 			return cs;
8079f920fbSDaniel Mack 	}
8179f920fbSDaniel Mack 
8279f920fbSDaniel Mack 	return NULL;
8379f920fbSDaniel Mack }
8479f920fbSDaniel Mack 
8579f920fbSDaniel Mack static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
8679f920fbSDaniel Mack {
8779f920fbSDaniel Mack 	unsigned char buf;
8879f920fbSDaniel Mack 	int ret;
8979f920fbSDaniel Mack 
9079f920fbSDaniel Mack 	ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
9179f920fbSDaniel Mack 			      UAC2_CS_CUR,
9279f920fbSDaniel Mack 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
9311bcbc44SDaniel Mack 			      UAC2_CX_CLOCK_SELECTOR << 8,
9411bcbc44SDaniel Mack 			      snd_usb_ctrl_intf(chip) | (selector_id << 8),
9517d900c4SClemens Ladisch 			      &buf, sizeof(buf));
9679f920fbSDaniel Mack 
9779f920fbSDaniel Mack 	if (ret < 0)
9879f920fbSDaniel Mack 		return ret;
9979f920fbSDaniel Mack 
10079f920fbSDaniel Mack 	return buf;
10179f920fbSDaniel Mack }
10279f920fbSDaniel Mack 
1038c55af3fSEldad Zack static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id,
1048c55af3fSEldad Zack 					unsigned char pin)
1058c55af3fSEldad Zack {
1068c55af3fSEldad Zack 	int ret;
1078c55af3fSEldad Zack 
1088c55af3fSEldad Zack 	ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
1098c55af3fSEldad Zack 			      UAC2_CS_CUR,
1108c55af3fSEldad Zack 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
1118c55af3fSEldad Zack 			      UAC2_CX_CLOCK_SELECTOR << 8,
1128c55af3fSEldad Zack 			      snd_usb_ctrl_intf(chip) | (selector_id << 8),
1138c55af3fSEldad Zack 			      &pin, sizeof(pin));
1148c55af3fSEldad Zack 	if (ret < 0)
1158c55af3fSEldad Zack 		return ret;
1168c55af3fSEldad Zack 
1178c55af3fSEldad Zack 	if (ret != sizeof(pin)) {
1180ba41d91STakashi Iwai 		usb_audio_err(chip,
1190ba41d91STakashi Iwai 			"setting selector (id %d) unexpected length %d\n",
1200ba41d91STakashi Iwai 			selector_id, ret);
1218c55af3fSEldad Zack 		return -EINVAL;
1228c55af3fSEldad Zack 	}
1238c55af3fSEldad Zack 
1248c55af3fSEldad Zack 	ret = uac_clock_selector_get_val(chip, selector_id);
1258c55af3fSEldad Zack 	if (ret < 0)
1268c55af3fSEldad Zack 		return ret;
1278c55af3fSEldad Zack 
1288c55af3fSEldad Zack 	if (ret != pin) {
1290ba41d91STakashi Iwai 		usb_audio_err(chip,
1300ba41d91STakashi Iwai 			"setting selector (id %d) to %x failed (current: %d)\n",
1310ba41d91STakashi Iwai 			selector_id, pin, ret);
1328c55af3fSEldad Zack 		return -EINVAL;
1338c55af3fSEldad Zack 	}
1348c55af3fSEldad Zack 
1358c55af3fSEldad Zack 	return ret;
1368c55af3fSEldad Zack }
1378c55af3fSEldad Zack 
13879f920fbSDaniel Mack static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
13979f920fbSDaniel Mack {
14079f920fbSDaniel Mack 	int err;
14179f920fbSDaniel Mack 	unsigned char data;
14279f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
1433bc6fbc7SDaniel Mack 	struct uac_clock_source_descriptor *cs_desc =
1443bc6fbc7SDaniel Mack 		snd_usb_find_clock_source(chip->ctrl_intf, source_id);
1453bc6fbc7SDaniel Mack 
1463bc6fbc7SDaniel Mack 	if (!cs_desc)
1473bc6fbc7SDaniel Mack 		return 0;
1483bc6fbc7SDaniel Mack 
1493bc6fbc7SDaniel Mack 	/* If a clock source can't tell us whether it's valid, we assume it is */
150aff252a8SDaniel Mack 	if (!uac2_control_is_readable(cs_desc->bmControls,
151aff252a8SDaniel Mack 				      UAC2_CS_CONTROL_CLOCK_VALID - 1))
1523bc6fbc7SDaniel Mack 		return 1;
15379f920fbSDaniel Mack 
15479f920fbSDaniel Mack 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
15579f920fbSDaniel Mack 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
15611bcbc44SDaniel Mack 			      UAC2_CS_CONTROL_CLOCK_VALID << 8,
15711bcbc44SDaniel Mack 			      snd_usb_ctrl_intf(chip) | (source_id << 8),
15817d900c4SClemens Ladisch 			      &data, sizeof(data));
15979f920fbSDaniel Mack 
16079f920fbSDaniel Mack 	if (err < 0) {
1610ba41d91STakashi Iwai 		dev_warn(&dev->dev,
1620ba41d91STakashi Iwai 			 "%s(): cannot get clock validity for id %d\n",
16379f920fbSDaniel Mack 			   __func__, source_id);
1643bc6fbc7SDaniel Mack 		return 0;
16579f920fbSDaniel Mack 	}
16679f920fbSDaniel Mack 
16779f920fbSDaniel Mack 	return !!data;
16879f920fbSDaniel Mack }
16979f920fbSDaniel Mack 
17079f920fbSDaniel Mack static int __uac_clock_find_source(struct snd_usb_audio *chip,
17106ffc1ebSEldad Zack 				   int entity_id, unsigned long *visited,
17206ffc1ebSEldad Zack 				   bool validate)
17379f920fbSDaniel Mack {
17479f920fbSDaniel Mack 	struct uac_clock_source_descriptor *source;
17579f920fbSDaniel Mack 	struct uac_clock_selector_descriptor *selector;
17679f920fbSDaniel Mack 	struct uac_clock_multiplier_descriptor *multiplier;
17779f920fbSDaniel Mack 
17879f920fbSDaniel Mack 	entity_id &= 0xff;
17979f920fbSDaniel Mack 
18079f920fbSDaniel Mack 	if (test_and_set_bit(entity_id, visited)) {
1810ba41d91STakashi Iwai 		usb_audio_warn(chip,
18279f920fbSDaniel Mack 			 "%s(): recursive clock topology detected, id %d.\n",
18379f920fbSDaniel Mack 			 __func__, entity_id);
18479f920fbSDaniel Mack 		return -EINVAL;
18579f920fbSDaniel Mack 	}
18679f920fbSDaniel Mack 
18779f920fbSDaniel Mack 	/* first, see if the ID we're looking for is a clock source already */
1883d8d4dcfSDaniel Mack 	source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
18906ffc1ebSEldad Zack 	if (source) {
19006ffc1ebSEldad Zack 		entity_id = source->bClockID;
19106ffc1ebSEldad Zack 		if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
1920ba41d91STakashi Iwai 			usb_audio_err(chip,
1930ba41d91STakashi Iwai 				"clock source %d is not valid, cannot use\n",
1940ba41d91STakashi Iwai 				entity_id);
19506ffc1ebSEldad Zack 			return -ENXIO;
19606ffc1ebSEldad Zack 		}
19706ffc1ebSEldad Zack 		return entity_id;
19806ffc1ebSEldad Zack 	}
19979f920fbSDaniel Mack 
2003d8d4dcfSDaniel Mack 	selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
20179f920fbSDaniel Mack 	if (selector) {
2028c55af3fSEldad Zack 		int ret, i, cur;
20379f920fbSDaniel Mack 
20479f920fbSDaniel Mack 		/* the entity ID we are looking for is a selector.
20579f920fbSDaniel Mack 		 * find out what it currently selects */
20679f920fbSDaniel Mack 		ret = uac_clock_selector_get_val(chip, selector->bClockID);
20779f920fbSDaniel Mack 		if (ret < 0)
20879f920fbSDaniel Mack 			return ret;
20979f920fbSDaniel Mack 
210157a57b6SDaniel Mack 		/* Selector values are one-based */
211157a57b6SDaniel Mack 
21279f920fbSDaniel Mack 		if (ret > selector->bNrInPins || ret < 1) {
2130ba41d91STakashi Iwai 			usb_audio_err(chip,
21479f920fbSDaniel Mack 				"%s(): selector reported illegal value, id %d, ret %d\n",
21579f920fbSDaniel Mack 				__func__, selector->bClockID, ret);
21679f920fbSDaniel Mack 
21779f920fbSDaniel Mack 			return -EINVAL;
21879f920fbSDaniel Mack 		}
21979f920fbSDaniel Mack 
2208c55af3fSEldad Zack 		cur = ret;
2218c55af3fSEldad Zack 		ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
22206ffc1ebSEldad Zack 					       visited, validate);
223ef02e29bSEldad Zack 		if (!validate || ret > 0 || !chip->autoclock)
2248c55af3fSEldad Zack 			return ret;
2258c55af3fSEldad Zack 
2268c55af3fSEldad Zack 		/* The current clock source is invalid, try others. */
2278c55af3fSEldad Zack 		for (i = 1; i <= selector->bNrInPins; i++) {
2288c55af3fSEldad Zack 			int err;
2298c55af3fSEldad Zack 
2308c55af3fSEldad Zack 			if (i == cur)
2318c55af3fSEldad Zack 				continue;
2328c55af3fSEldad Zack 
2338c55af3fSEldad Zack 			ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
2348c55af3fSEldad Zack 				visited, true);
2358c55af3fSEldad Zack 			if (ret < 0)
2368c55af3fSEldad Zack 				continue;
2378c55af3fSEldad Zack 
2388c55af3fSEldad Zack 			err = uac_clock_selector_set_val(chip, entity_id, i);
2398c55af3fSEldad Zack 			if (err < 0)
2408c55af3fSEldad Zack 				continue;
2418c55af3fSEldad Zack 
2420ba41d91STakashi Iwai 			usb_audio_info(chip,
2430ba41d91STakashi Iwai 				 "found and selected valid clock source %d\n",
2440ba41d91STakashi Iwai 				 ret);
2458c55af3fSEldad Zack 			return ret;
2468c55af3fSEldad Zack 		}
2478c55af3fSEldad Zack 
2488c55af3fSEldad Zack 		return -ENXIO;
24979f920fbSDaniel Mack 	}
25079f920fbSDaniel Mack 
25179f920fbSDaniel Mack 	/* FIXME: multipliers only act as pass-thru element for now */
2523d8d4dcfSDaniel Mack 	multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
25379f920fbSDaniel Mack 	if (multiplier)
2543d8d4dcfSDaniel Mack 		return __uac_clock_find_source(chip, multiplier->bCSourceID,
25506ffc1ebSEldad Zack 						visited, validate);
25679f920fbSDaniel Mack 
25779f920fbSDaniel Mack 	return -EINVAL;
25879f920fbSDaniel Mack }
25979f920fbSDaniel Mack 
260157a57b6SDaniel Mack /*
261157a57b6SDaniel Mack  * For all kinds of sample rate settings and other device queries,
262157a57b6SDaniel Mack  * the clock source (end-leaf) must be used. However, clock selectors,
263157a57b6SDaniel Mack  * clock multipliers and sample rate converters may be specified as
264157a57b6SDaniel Mack  * clock source input to terminal. This functions walks the clock path
265157a57b6SDaniel Mack  * to its end and tries to find the source.
266157a57b6SDaniel Mack  *
267157a57b6SDaniel Mack  * The 'visited' bitfield is used internally to detect recursive loops.
268157a57b6SDaniel Mack  *
269157a57b6SDaniel Mack  * Returns the clock source UnitID (>=0) on success, or an error.
270157a57b6SDaniel Mack  */
27106ffc1ebSEldad Zack int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
27206ffc1ebSEldad Zack 			      bool validate)
27379f920fbSDaniel Mack {
27479f920fbSDaniel Mack 	DECLARE_BITMAP(visited, 256);
27579f920fbSDaniel Mack 	memset(visited, 0, sizeof(visited));
27606ffc1ebSEldad Zack 	return __uac_clock_find_source(chip, entity_id, visited, validate);
27779f920fbSDaniel Mack }
27879f920fbSDaniel Mack 
27979f920fbSDaniel Mack static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
28079f920fbSDaniel Mack 			      struct usb_host_interface *alts,
28179f920fbSDaniel Mack 			      struct audioformat *fmt, int rate)
28279f920fbSDaniel Mack {
28379f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
28479f920fbSDaniel Mack 	unsigned int ep;
28579f920fbSDaniel Mack 	unsigned char data[3];
28679f920fbSDaniel Mack 	int err, crate;
28779f920fbSDaniel Mack 
288447d6275STakashi Iwai 	if (get_iface_desc(alts)->bNumEndpoints < 1)
289447d6275STakashi Iwai 		return -EINVAL;
29079f920fbSDaniel Mack 	ep = get_endpoint(alts, 0)->bEndpointAddress;
29179f920fbSDaniel Mack 
29279f920fbSDaniel Mack 	/* if endpoint doesn't have sampling rate control, bail out */
293d32d552eSClemens Ladisch 	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
29479f920fbSDaniel Mack 		return 0;
29579f920fbSDaniel Mack 
29679f920fbSDaniel Mack 	data[0] = rate;
29779f920fbSDaniel Mack 	data[1] = rate >> 8;
29879f920fbSDaniel Mack 	data[2] = rate >> 16;
29979f920fbSDaniel Mack 	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
30079f920fbSDaniel Mack 				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
30179f920fbSDaniel Mack 				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
30217d900c4SClemens Ladisch 				   data, sizeof(data))) < 0) {
3030ba41d91STakashi Iwai 		dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
3040ba41d91STakashi Iwai 			iface, fmt->altsetting, rate, ep);
30579f920fbSDaniel Mack 		return err;
30679f920fbSDaniel Mack 	}
30779f920fbSDaniel Mack 
308b62b9980SJoe Turner 	/* Don't check the sample rate for devices which we know don't
309b62b9980SJoe Turner 	 * support reading */
310b62b9980SJoe Turner 	if (snd_usb_get_sample_rate_quirk(chip))
311b62b9980SJoe Turner 		return 0;
31257dd5414STakashi Iwai 	/* the firmware is likely buggy, don't repeat to fail too many times */
31357dd5414STakashi Iwai 	if (chip->sample_rate_read_error > 2)
31457dd5414STakashi Iwai 		return 0;
315b62b9980SJoe Turner 
31679f920fbSDaniel Mack 	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
31779f920fbSDaniel Mack 				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
31879f920fbSDaniel Mack 				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
31917d900c4SClemens Ladisch 				   data, sizeof(data))) < 0) {
3200ba41d91STakashi Iwai 		dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
3210ba41d91STakashi Iwai 			iface, fmt->altsetting, ep);
32257dd5414STakashi Iwai 		chip->sample_rate_read_error++;
32379f920fbSDaniel Mack 		return 0; /* some devices don't support reading */
32479f920fbSDaniel Mack 	}
32579f920fbSDaniel Mack 
32679f920fbSDaniel Mack 	crate = data[0] | (data[1] << 8) | (data[2] << 16);
32779f920fbSDaniel Mack 	if (crate != rate) {
3280ba41d91STakashi Iwai 		dev_warn(&dev->dev, "current rate %d is different from the runtime rate %d\n", crate, rate);
32979f920fbSDaniel Mack 		// runtime->rate = crate;
33079f920fbSDaniel Mack 	}
33179f920fbSDaniel Mack 
33279f920fbSDaniel Mack 	return 0;
33379f920fbSDaniel Mack }
33479f920fbSDaniel Mack 
3357c517465STakashi Iwai static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
3367c517465STakashi Iwai 			      int altsetting, int clock)
3377c517465STakashi Iwai {
3387c517465STakashi Iwai 	struct usb_device *dev = chip->dev;
339f6a8bc70SEldad Zack 	__le32 data;
3407c517465STakashi Iwai 	int err;
3417c517465STakashi Iwai 
3427c517465STakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
3437c517465STakashi Iwai 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
3447c517465STakashi Iwai 			      UAC2_CS_CONTROL_SAM_FREQ << 8,
3457c517465STakashi Iwai 			      snd_usb_ctrl_intf(chip) | (clock << 8),
346f6a8bc70SEldad Zack 			      &data, sizeof(data));
3477c517465STakashi Iwai 	if (err < 0) {
3480ba41d91STakashi Iwai 		dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n",
3490ba41d91STakashi Iwai 			 iface, altsetting, err);
3507c517465STakashi Iwai 		return 0;
3517c517465STakashi Iwai 	}
3527c517465STakashi Iwai 
353f6a8bc70SEldad Zack 	return le32_to_cpu(data);
3547c517465STakashi Iwai }
3557c517465STakashi Iwai 
35679f920fbSDaniel Mack static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
35779f920fbSDaniel Mack 			      struct usb_host_interface *alts,
35879f920fbSDaniel Mack 			      struct audioformat *fmt, int rate)
35979f920fbSDaniel Mack {
36079f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
361f6a8bc70SEldad Zack 	__le32 data;
362690a863fSTorstein Hegge 	int err, cur_rate, prev_rate;
3638c55af3fSEldad Zack 	int clock;
3641dc669feSEldad Zack 	bool writeable;
3651dc669feSEldad Zack 	struct uac_clock_source_descriptor *cs_desc;
36679f920fbSDaniel Mack 
3678c55af3fSEldad Zack 	clock = snd_usb_clock_find_source(chip, fmt->clock, true);
36879f920fbSDaniel Mack 	if (clock < 0)
36979f920fbSDaniel Mack 		return clock;
37079f920fbSDaniel Mack 
3717c517465STakashi Iwai 	prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
372fa92dd77SDavid Henningsson 	if (prev_rate == rate)
373fa92dd77SDavid Henningsson 		return 0;
374690a863fSTorstein Hegge 
3751dc669feSEldad Zack 	cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
3761dc669feSEldad Zack 	writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
3771dc669feSEldad Zack 	if (writeable) {
378f6a8bc70SEldad Zack 		data = cpu_to_le32(rate);
3791dc669feSEldad Zack 		err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
38079f920fbSDaniel Mack 				      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
38111bcbc44SDaniel Mack 				      UAC2_CS_CONTROL_SAM_FREQ << 8,
38211bcbc44SDaniel Mack 				      snd_usb_ctrl_intf(chip) | (clock << 8),
3831dc669feSEldad Zack 				      &data, sizeof(data));
3841dc669feSEldad Zack 		if (err < 0) {
3850ba41d91STakashi Iwai 			usb_audio_err(chip,
3860ba41d91STakashi Iwai 				"%d:%d: cannot set freq %d (v2): err %d\n",
3870ba41d91STakashi Iwai 				iface, fmt->altsetting, rate, err);
38879f920fbSDaniel Mack 			return err;
38979f920fbSDaniel Mack 		}
39079f920fbSDaniel Mack 
3917c517465STakashi Iwai 		cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
3921dc669feSEldad Zack 	} else {
3931dc669feSEldad Zack 		cur_rate = prev_rate;
3941dc669feSEldad Zack 	}
39579f920fbSDaniel Mack 
396690a863fSTorstein Hegge 	if (cur_rate != rate) {
3971dc669feSEldad Zack 		if (!writeable) {
3980ba41d91STakashi Iwai 			usb_audio_warn(chip,
3990ba41d91STakashi Iwai 				 "%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n",
4000ba41d91STakashi Iwai 				 iface, fmt->altsetting, rate, cur_rate);
4011dc669feSEldad Zack 			return -ENXIO;
4021dc669feSEldad Zack 		}
4030ba41d91STakashi Iwai 		usb_audio_dbg(chip,
404690a863fSTorstein Hegge 			"current rate %d is different from the runtime rate %d\n",
405690a863fSTorstein Hegge 			cur_rate, rate);
406690a863fSTorstein Hegge 	}
407690a863fSTorstein Hegge 
408690a863fSTorstein Hegge 	/* Some devices doesn't respond to sample rate changes while the
409690a863fSTorstein Hegge 	 * interface is active. */
410690a863fSTorstein Hegge 	if (rate != prev_rate) {
411690a863fSTorstein Hegge 		usb_set_interface(dev, iface, 0);
41221bb5aafSDaniel Mack 		snd_usb_set_interface_quirk(dev);
413690a863fSTorstein Hegge 		usb_set_interface(dev, iface, fmt->altsetting);
41421bb5aafSDaniel Mack 		snd_usb_set_interface_quirk(dev);
415690a863fSTorstein Hegge 	}
41679f920fbSDaniel Mack 
41779f920fbSDaniel Mack 	return 0;
41879f920fbSDaniel Mack }
41979f920fbSDaniel Mack 
42079f920fbSDaniel Mack int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
42179f920fbSDaniel Mack 			     struct usb_host_interface *alts,
42279f920fbSDaniel Mack 			     struct audioformat *fmt, int rate)
42379f920fbSDaniel Mack {
4248f898e92SClemens Ladisch 	switch (fmt->protocol) {
42579f920fbSDaniel Mack 	case UAC_VERSION_1:
426a2acad82SClemens Ladisch 	default:
42779f920fbSDaniel Mack 		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
42879f920fbSDaniel Mack 
42979f920fbSDaniel Mack 	case UAC_VERSION_2:
43079f920fbSDaniel Mack 		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
43179f920fbSDaniel Mack 	}
43279f920fbSDaniel Mack }
43379f920fbSDaniel Mack 
434