xref: /openbmc/linux/sound/usb/clock.c (revision 21bb5aaf)
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)) {
1188c55af3fSEldad Zack 		snd_printk(KERN_ERR
1198c55af3fSEldad Zack 			"usb-audio:%d: setting selector (id %d) unexpected length %d\n",
1208c55af3fSEldad Zack 			chip->dev->devnum, 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) {
1298c55af3fSEldad Zack 		snd_printk(KERN_ERR
1308c55af3fSEldad Zack 			"usb-audio:%d: setting selector (id %d) to %x failed (current: %d)\n",
1318c55af3fSEldad Zack 			chip->dev->devnum, 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) {
16179f920fbSDaniel Mack 		snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
16279f920fbSDaniel Mack 			   __func__, source_id);
1633bc6fbc7SDaniel Mack 		return 0;
16479f920fbSDaniel Mack 	}
16579f920fbSDaniel Mack 
16679f920fbSDaniel Mack 	return !!data;
16779f920fbSDaniel Mack }
16879f920fbSDaniel Mack 
16979f920fbSDaniel Mack static int __uac_clock_find_source(struct snd_usb_audio *chip,
17006ffc1ebSEldad Zack 				   int entity_id, unsigned long *visited,
17106ffc1ebSEldad Zack 				   bool validate)
17279f920fbSDaniel Mack {
17379f920fbSDaniel Mack 	struct uac_clock_source_descriptor *source;
17479f920fbSDaniel Mack 	struct uac_clock_selector_descriptor *selector;
17579f920fbSDaniel Mack 	struct uac_clock_multiplier_descriptor *multiplier;
17679f920fbSDaniel Mack 
17779f920fbSDaniel Mack 	entity_id &= 0xff;
17879f920fbSDaniel Mack 
17979f920fbSDaniel Mack 	if (test_and_set_bit(entity_id, visited)) {
18079f920fbSDaniel Mack 		snd_printk(KERN_WARNING
18179f920fbSDaniel Mack 			"%s(): recursive clock topology detected, id %d.\n",
18279f920fbSDaniel Mack 			__func__, entity_id);
18379f920fbSDaniel Mack 		return -EINVAL;
18479f920fbSDaniel Mack 	}
18579f920fbSDaniel Mack 
18679f920fbSDaniel Mack 	/* first, see if the ID we're looking for is a clock source already */
1873d8d4dcfSDaniel Mack 	source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
18806ffc1ebSEldad Zack 	if (source) {
18906ffc1ebSEldad Zack 		entity_id = source->bClockID;
19006ffc1ebSEldad Zack 		if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
19106ffc1ebSEldad Zack 			snd_printk(KERN_ERR "usb-audio:%d: clock source %d is not valid, cannot use\n",
19206ffc1ebSEldad Zack 				   chip->dev->devnum, entity_id);
19306ffc1ebSEldad Zack 			return -ENXIO;
19406ffc1ebSEldad Zack 		}
19506ffc1ebSEldad Zack 		return entity_id;
19606ffc1ebSEldad Zack 	}
19779f920fbSDaniel Mack 
1983d8d4dcfSDaniel Mack 	selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
19979f920fbSDaniel Mack 	if (selector) {
2008c55af3fSEldad Zack 		int ret, i, cur;
20179f920fbSDaniel Mack 
20279f920fbSDaniel Mack 		/* the entity ID we are looking for is a selector.
20379f920fbSDaniel Mack 		 * find out what it currently selects */
20479f920fbSDaniel Mack 		ret = uac_clock_selector_get_val(chip, selector->bClockID);
20579f920fbSDaniel Mack 		if (ret < 0)
20679f920fbSDaniel Mack 			return ret;
20779f920fbSDaniel Mack 
208157a57b6SDaniel Mack 		/* Selector values are one-based */
209157a57b6SDaniel Mack 
21079f920fbSDaniel Mack 		if (ret > selector->bNrInPins || ret < 1) {
21106ffc1ebSEldad Zack 			snd_printk(KERN_ERR
21279f920fbSDaniel Mack 				"%s(): selector reported illegal value, id %d, ret %d\n",
21379f920fbSDaniel Mack 				__func__, selector->bClockID, ret);
21479f920fbSDaniel Mack 
21579f920fbSDaniel Mack 			return -EINVAL;
21679f920fbSDaniel Mack 		}
21779f920fbSDaniel Mack 
2188c55af3fSEldad Zack 		cur = ret;
2198c55af3fSEldad Zack 		ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
22006ffc1ebSEldad Zack 					       visited, validate);
221ef02e29bSEldad Zack 		if (!validate || ret > 0 || !chip->autoclock)
2228c55af3fSEldad Zack 			return ret;
2238c55af3fSEldad Zack 
2248c55af3fSEldad Zack 		/* The current clock source is invalid, try others. */
2258c55af3fSEldad Zack 		for (i = 1; i <= selector->bNrInPins; i++) {
2268c55af3fSEldad Zack 			int err;
2278c55af3fSEldad Zack 
2288c55af3fSEldad Zack 			if (i == cur)
2298c55af3fSEldad Zack 				continue;
2308c55af3fSEldad Zack 
2318c55af3fSEldad Zack 			ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
2328c55af3fSEldad Zack 				visited, true);
2338c55af3fSEldad Zack 			if (ret < 0)
2348c55af3fSEldad Zack 				continue;
2358c55af3fSEldad Zack 
2368c55af3fSEldad Zack 			err = uac_clock_selector_set_val(chip, entity_id, i);
2378c55af3fSEldad Zack 			if (err < 0)
2388c55af3fSEldad Zack 				continue;
2398c55af3fSEldad Zack 
2408c55af3fSEldad Zack 			snd_printk(KERN_INFO
2418c55af3fSEldad Zack 				"usb-audio:%d: found and selected valid clock source %d\n",
2428c55af3fSEldad Zack 				chip->dev->devnum, ret);
2438c55af3fSEldad Zack 			return ret;
2448c55af3fSEldad Zack 		}
2458c55af3fSEldad Zack 
2468c55af3fSEldad Zack 		return -ENXIO;
24779f920fbSDaniel Mack 	}
24879f920fbSDaniel Mack 
24979f920fbSDaniel Mack 	/* FIXME: multipliers only act as pass-thru element for now */
2503d8d4dcfSDaniel Mack 	multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
25179f920fbSDaniel Mack 	if (multiplier)
2523d8d4dcfSDaniel Mack 		return __uac_clock_find_source(chip, multiplier->bCSourceID,
25306ffc1ebSEldad Zack 						visited, validate);
25479f920fbSDaniel Mack 
25579f920fbSDaniel Mack 	return -EINVAL;
25679f920fbSDaniel Mack }
25779f920fbSDaniel Mack 
258157a57b6SDaniel Mack /*
259157a57b6SDaniel Mack  * For all kinds of sample rate settings and other device queries,
260157a57b6SDaniel Mack  * the clock source (end-leaf) must be used. However, clock selectors,
261157a57b6SDaniel Mack  * clock multipliers and sample rate converters may be specified as
262157a57b6SDaniel Mack  * clock source input to terminal. This functions walks the clock path
263157a57b6SDaniel Mack  * to its end and tries to find the source.
264157a57b6SDaniel Mack  *
265157a57b6SDaniel Mack  * The 'visited' bitfield is used internally to detect recursive loops.
266157a57b6SDaniel Mack  *
267157a57b6SDaniel Mack  * Returns the clock source UnitID (>=0) on success, or an error.
268157a57b6SDaniel Mack  */
26906ffc1ebSEldad Zack int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
27006ffc1ebSEldad Zack 			      bool validate)
27179f920fbSDaniel Mack {
27279f920fbSDaniel Mack 	DECLARE_BITMAP(visited, 256);
27379f920fbSDaniel Mack 	memset(visited, 0, sizeof(visited));
27406ffc1ebSEldad Zack 	return __uac_clock_find_source(chip, entity_id, visited, validate);
27579f920fbSDaniel Mack }
27679f920fbSDaniel Mack 
27779f920fbSDaniel Mack static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
27879f920fbSDaniel Mack 			      struct usb_host_interface *alts,
27979f920fbSDaniel Mack 			      struct audioformat *fmt, int rate)
28079f920fbSDaniel Mack {
28179f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
28279f920fbSDaniel Mack 	unsigned int ep;
28379f920fbSDaniel Mack 	unsigned char data[3];
28479f920fbSDaniel Mack 	int err, crate;
28579f920fbSDaniel Mack 
28679f920fbSDaniel Mack 	ep = get_endpoint(alts, 0)->bEndpointAddress;
28779f920fbSDaniel Mack 
28879f920fbSDaniel Mack 	/* if endpoint doesn't have sampling rate control, bail out */
289d32d552eSClemens Ladisch 	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
29079f920fbSDaniel Mack 		return 0;
29179f920fbSDaniel Mack 
29279f920fbSDaniel Mack 	data[0] = rate;
29379f920fbSDaniel Mack 	data[1] = rate >> 8;
29479f920fbSDaniel Mack 	data[2] = rate >> 16;
29579f920fbSDaniel Mack 	if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
29679f920fbSDaniel Mack 				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
29779f920fbSDaniel Mack 				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
29817d900c4SClemens Ladisch 				   data, sizeof(data))) < 0) {
29979f920fbSDaniel Mack 		snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
30079f920fbSDaniel Mack 			   dev->devnum, iface, fmt->altsetting, rate, ep);
30179f920fbSDaniel Mack 		return err;
30279f920fbSDaniel Mack 	}
30379f920fbSDaniel Mack 
30479f920fbSDaniel Mack 	if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
30579f920fbSDaniel Mack 				   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
30679f920fbSDaniel Mack 				   UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
30717d900c4SClemens Ladisch 				   data, sizeof(data))) < 0) {
30879f920fbSDaniel Mack 		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
30979f920fbSDaniel Mack 			   dev->devnum, iface, fmt->altsetting, ep);
31079f920fbSDaniel Mack 		return 0; /* some devices don't support reading */
31179f920fbSDaniel Mack 	}
31279f920fbSDaniel Mack 
31379f920fbSDaniel Mack 	crate = data[0] | (data[1] << 8) | (data[2] << 16);
31479f920fbSDaniel Mack 	if (crate != rate) {
31579f920fbSDaniel Mack 		snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
31679f920fbSDaniel Mack 		// runtime->rate = crate;
31779f920fbSDaniel Mack 	}
31879f920fbSDaniel Mack 
31979f920fbSDaniel Mack 	return 0;
32079f920fbSDaniel Mack }
32179f920fbSDaniel Mack 
3227c517465STakashi Iwai static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
3237c517465STakashi Iwai 			      int altsetting, int clock)
3247c517465STakashi Iwai {
3257c517465STakashi Iwai 	struct usb_device *dev = chip->dev;
326f6a8bc70SEldad Zack 	__le32 data;
3277c517465STakashi Iwai 	int err;
3287c517465STakashi Iwai 
3297c517465STakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
3307c517465STakashi Iwai 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
3317c517465STakashi Iwai 			      UAC2_CS_CONTROL_SAM_FREQ << 8,
3327c517465STakashi Iwai 			      snd_usb_ctrl_intf(chip) | (clock << 8),
333f6a8bc70SEldad Zack 			      &data, sizeof(data));
3347c517465STakashi Iwai 	if (err < 0) {
335027bbc15SEldad Zack 		snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2): err %d\n",
336027bbc15SEldad Zack 			   dev->devnum, iface, altsetting, err);
3377c517465STakashi Iwai 		return 0;
3387c517465STakashi Iwai 	}
3397c517465STakashi Iwai 
340f6a8bc70SEldad Zack 	return le32_to_cpu(data);
3417c517465STakashi Iwai }
3427c517465STakashi Iwai 
34379f920fbSDaniel Mack static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
34479f920fbSDaniel Mack 			      struct usb_host_interface *alts,
34579f920fbSDaniel Mack 			      struct audioformat *fmt, int rate)
34679f920fbSDaniel Mack {
34779f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
348f6a8bc70SEldad Zack 	__le32 data;
349690a863fSTorstein Hegge 	int err, cur_rate, prev_rate;
3508c55af3fSEldad Zack 	int clock;
3511dc669feSEldad Zack 	bool writeable;
3521dc669feSEldad Zack 	struct uac_clock_source_descriptor *cs_desc;
35379f920fbSDaniel Mack 
3548c55af3fSEldad Zack 	clock = snd_usb_clock_find_source(chip, fmt->clock, true);
35579f920fbSDaniel Mack 	if (clock < 0)
35679f920fbSDaniel Mack 		return clock;
35779f920fbSDaniel Mack 
3587c517465STakashi Iwai 	prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
359690a863fSTorstein Hegge 
3601dc669feSEldad Zack 	cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
3611dc669feSEldad Zack 	writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
3621dc669feSEldad Zack 	if (writeable) {
363f6a8bc70SEldad Zack 		data = cpu_to_le32(rate);
3641dc669feSEldad Zack 		err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
36579f920fbSDaniel Mack 				      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
36611bcbc44SDaniel Mack 				      UAC2_CS_CONTROL_SAM_FREQ << 8,
36711bcbc44SDaniel Mack 				      snd_usb_ctrl_intf(chip) | (clock << 8),
3681dc669feSEldad Zack 				      &data, sizeof(data));
3691dc669feSEldad Zack 		if (err < 0) {
370027bbc15SEldad Zack 			snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2): err %d\n",
371027bbc15SEldad Zack 				   dev->devnum, iface, fmt->altsetting, rate, err);
37279f920fbSDaniel Mack 			return err;
37379f920fbSDaniel Mack 		}
37479f920fbSDaniel Mack 
3757c517465STakashi Iwai 		cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
3761dc669feSEldad Zack 	} else {
3771dc669feSEldad Zack 		cur_rate = prev_rate;
3781dc669feSEldad Zack 	}
37979f920fbSDaniel Mack 
380690a863fSTorstein Hegge 	if (cur_rate != rate) {
3811dc669feSEldad Zack 		if (!writeable) {
3821dc669feSEldad Zack 			snd_printk(KERN_WARNING
3831dc669feSEldad Zack 				   "%d:%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n",
3841dc669feSEldad Zack 				   dev->devnum, iface, fmt->altsetting, rate, cur_rate);
3851dc669feSEldad Zack 			return -ENXIO;
3861dc669feSEldad Zack 		}
387690a863fSTorstein Hegge 		snd_printd(KERN_WARNING
388690a863fSTorstein Hegge 			   "current rate %d is different from the runtime rate %d\n",
389690a863fSTorstein Hegge 			   cur_rate, rate);
390690a863fSTorstein Hegge 	}
391690a863fSTorstein Hegge 
392690a863fSTorstein Hegge 	/* Some devices doesn't respond to sample rate changes while the
393690a863fSTorstein Hegge 	 * interface is active. */
394690a863fSTorstein Hegge 	if (rate != prev_rate) {
395690a863fSTorstein Hegge 		usb_set_interface(dev, iface, 0);
39621bb5aafSDaniel Mack 		snd_usb_set_interface_quirk(dev);
397690a863fSTorstein Hegge 		usb_set_interface(dev, iface, fmt->altsetting);
39821bb5aafSDaniel Mack 		snd_usb_set_interface_quirk(dev);
399690a863fSTorstein Hegge 	}
40079f920fbSDaniel Mack 
40179f920fbSDaniel Mack 	return 0;
40279f920fbSDaniel Mack }
40379f920fbSDaniel Mack 
40479f920fbSDaniel Mack int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
40579f920fbSDaniel Mack 			     struct usb_host_interface *alts,
40679f920fbSDaniel Mack 			     struct audioformat *fmt, int rate)
40779f920fbSDaniel Mack {
40879f920fbSDaniel Mack 	struct usb_interface_descriptor *altsd = get_iface_desc(alts);
40979f920fbSDaniel Mack 
41079f920fbSDaniel Mack 	switch (altsd->bInterfaceProtocol) {
41179f920fbSDaniel Mack 	case UAC_VERSION_1:
412a2acad82SClemens Ladisch 	default:
41379f920fbSDaniel Mack 		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
41479f920fbSDaniel Mack 
41579f920fbSDaniel Mack 	case UAC_VERSION_2:
41679f920fbSDaniel Mack 		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
41779f920fbSDaniel Mack 	}
41879f920fbSDaniel Mack }
41979f920fbSDaniel Mack 
420