xref: /openbmc/linux/sound/usb/clock.c (revision 086b957c)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
279f920fbSDaniel Mack /*
379f920fbSDaniel Mack  *   Clock domain and sample rate management functions
479f920fbSDaniel Mack  */
579f920fbSDaniel Mack 
679f920fbSDaniel Mack #include <linux/bitops.h>
779f920fbSDaniel Mack #include <linux/init.h>
879f920fbSDaniel Mack #include <linux/string.h>
979f920fbSDaniel Mack #include <linux/usb.h>
1079f920fbSDaniel Mack #include <linux/usb/audio.h>
1179f920fbSDaniel Mack #include <linux/usb/audio-v2.h>
129a2fe9b8SRuslan Bilovol #include <linux/usb/audio-v3.h>
1379f920fbSDaniel Mack 
1479f920fbSDaniel Mack #include <sound/core.h>
1579f920fbSDaniel Mack #include <sound/info.h>
1679f920fbSDaniel Mack #include <sound/pcm.h>
1779f920fbSDaniel Mack 
1879f920fbSDaniel Mack #include "usbaudio.h"
1979f920fbSDaniel Mack #include "card.h"
2079f920fbSDaniel Mack #include "helper.h"
21f22aa949SDaniel Mack #include "clock.h"
2221bb5aafSDaniel Mack #include "quirks.h"
2379f920fbSDaniel Mack 
24f7645bd6STakashi Iwai static void *find_uac_clock_desc(struct usb_host_interface *iface, int id,
25f7645bd6STakashi Iwai 				 bool (*validator)(void *, int), u8 type)
2679f920fbSDaniel Mack {
27f7645bd6STakashi Iwai 	void *cs = NULL;
2879f920fbSDaniel Mack 
29f7645bd6STakashi Iwai 	while ((cs = snd_usb_find_csint_desc(iface->extra, iface->extralen,
30f7645bd6STakashi Iwai 					     cs, type))) {
31f7645bd6STakashi Iwai 		if (validator(cs, id))
3279f920fbSDaniel Mack 			return cs;
3379f920fbSDaniel Mack 	}
3479f920fbSDaniel Mack 
3579f920fbSDaniel Mack 	return NULL;
3679f920fbSDaniel Mack }
3779f920fbSDaniel Mack 
38f7645bd6STakashi Iwai static bool validate_clock_source_v2(void *p, int id)
399a2fe9b8SRuslan Bilovol {
40f7645bd6STakashi Iwai 	struct uac_clock_source_descriptor *cs = p;
41b8e4f1fdSTakashi Iwai 	return cs->bClockID == id;
429a2fe9b8SRuslan Bilovol }
439a2fe9b8SRuslan Bilovol 
44f7645bd6STakashi Iwai static bool validate_clock_source_v3(void *p, int id)
4579f920fbSDaniel Mack {
46f7645bd6STakashi Iwai 	struct uac3_clock_source_descriptor *cs = p;
47b8e4f1fdSTakashi Iwai 	return cs->bClockID == id;
480a62d6c9STakashi Iwai }
4979f920fbSDaniel Mack 
50f7645bd6STakashi Iwai static bool validate_clock_selector_v2(void *p, int id)
519a2fe9b8SRuslan Bilovol {
52f7645bd6STakashi Iwai 	struct uac_clock_selector_descriptor *cs = p;
53b8e4f1fdSTakashi Iwai 	return cs->bClockID == id;
549a2fe9b8SRuslan Bilovol }
559a2fe9b8SRuslan Bilovol 
56f7645bd6STakashi Iwai static bool validate_clock_selector_v3(void *p, int id)
5779f920fbSDaniel Mack {
58f7645bd6STakashi Iwai 	struct uac3_clock_selector_descriptor *cs = p;
59b8e4f1fdSTakashi Iwai 	return cs->bClockID == id;
6079f920fbSDaniel Mack }
6179f920fbSDaniel Mack 
62f7645bd6STakashi Iwai static bool validate_clock_multiplier_v2(void *p, int id)
639a2fe9b8SRuslan Bilovol {
64f7645bd6STakashi Iwai 	struct uac_clock_multiplier_descriptor *cs = p;
65b8e4f1fdSTakashi Iwai 	return cs->bClockID == id;
669a2fe9b8SRuslan Bilovol }
679a2fe9b8SRuslan Bilovol 
68f7645bd6STakashi Iwai static bool validate_clock_multiplier_v3(void *p, int id)
69f7645bd6STakashi Iwai {
70f7645bd6STakashi Iwai 	struct uac3_clock_multiplier_descriptor *cs = p;
71b8e4f1fdSTakashi Iwai 	return cs->bClockID == id;
729a2fe9b8SRuslan Bilovol }
739a2fe9b8SRuslan Bilovol 
74f7645bd6STakashi Iwai #define DEFINE_FIND_HELPER(name, obj, validator, type)		\
75f7645bd6STakashi Iwai static obj *name(struct usb_host_interface *iface, int id)	\
76f7645bd6STakashi Iwai {								\
77f7645bd6STakashi Iwai 	return find_uac_clock_desc(iface, id, validator, type);	\
78f7645bd6STakashi Iwai }
79f7645bd6STakashi Iwai 
80f7645bd6STakashi Iwai DEFINE_FIND_HELPER(snd_usb_find_clock_source,
81f7645bd6STakashi Iwai 		   struct uac_clock_source_descriptor,
82f7645bd6STakashi Iwai 		   validate_clock_source_v2, UAC2_CLOCK_SOURCE);
83f7645bd6STakashi Iwai DEFINE_FIND_HELPER(snd_usb_find_clock_source_v3,
84f7645bd6STakashi Iwai 		   struct uac3_clock_source_descriptor,
85f7645bd6STakashi Iwai 		   validate_clock_source_v3, UAC3_CLOCK_SOURCE);
86f7645bd6STakashi Iwai 
87f7645bd6STakashi Iwai DEFINE_FIND_HELPER(snd_usb_find_clock_selector,
88f7645bd6STakashi Iwai 		   struct uac_clock_selector_descriptor,
89f7645bd6STakashi Iwai 		   validate_clock_selector_v2, UAC2_CLOCK_SELECTOR);
90f7645bd6STakashi Iwai DEFINE_FIND_HELPER(snd_usb_find_clock_selector_v3,
91f7645bd6STakashi Iwai 		   struct uac3_clock_selector_descriptor,
92f7645bd6STakashi Iwai 		   validate_clock_selector_v3, UAC3_CLOCK_SELECTOR);
93f7645bd6STakashi Iwai 
94f7645bd6STakashi Iwai DEFINE_FIND_HELPER(snd_usb_find_clock_multiplier,
95f7645bd6STakashi Iwai 		   struct uac_clock_multiplier_descriptor,
96f7645bd6STakashi Iwai 		   validate_clock_multiplier_v2, UAC2_CLOCK_MULTIPLIER);
97f7645bd6STakashi Iwai DEFINE_FIND_HELPER(snd_usb_find_clock_multiplier_v3,
98f7645bd6STakashi Iwai 		   struct uac3_clock_multiplier_descriptor,
99f7645bd6STakashi Iwai 		   validate_clock_multiplier_v3, UAC3_CLOCK_MULTIPLIER);
100f7645bd6STakashi Iwai 
10179f920fbSDaniel Mack static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
10279f920fbSDaniel Mack {
10379f920fbSDaniel Mack 	unsigned char buf;
10479f920fbSDaniel Mack 	int ret;
10579f920fbSDaniel Mack 
10679f920fbSDaniel Mack 	ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
10779f920fbSDaniel Mack 			      UAC2_CS_CUR,
10879f920fbSDaniel Mack 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
10911bcbc44SDaniel Mack 			      UAC2_CX_CLOCK_SELECTOR << 8,
11011bcbc44SDaniel Mack 			      snd_usb_ctrl_intf(chip) | (selector_id << 8),
11117d900c4SClemens Ladisch 			      &buf, sizeof(buf));
11279f920fbSDaniel Mack 
11379f920fbSDaniel Mack 	if (ret < 0)
11479f920fbSDaniel Mack 		return ret;
11579f920fbSDaniel Mack 
11679f920fbSDaniel Mack 	return buf;
11779f920fbSDaniel Mack }
11879f920fbSDaniel Mack 
1198c55af3fSEldad Zack static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id,
1208c55af3fSEldad Zack 					unsigned char pin)
1218c55af3fSEldad Zack {
1228c55af3fSEldad Zack 	int ret;
1238c55af3fSEldad Zack 
1248c55af3fSEldad Zack 	ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
1258c55af3fSEldad Zack 			      UAC2_CS_CUR,
1268c55af3fSEldad Zack 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
1278c55af3fSEldad Zack 			      UAC2_CX_CLOCK_SELECTOR << 8,
1288c55af3fSEldad Zack 			      snd_usb_ctrl_intf(chip) | (selector_id << 8),
1298c55af3fSEldad Zack 			      &pin, sizeof(pin));
1308c55af3fSEldad Zack 	if (ret < 0)
1318c55af3fSEldad Zack 		return ret;
1328c55af3fSEldad Zack 
1338c55af3fSEldad Zack 	if (ret != sizeof(pin)) {
1340ba41d91STakashi Iwai 		usb_audio_err(chip,
1350ba41d91STakashi Iwai 			"setting selector (id %d) unexpected length %d\n",
1360ba41d91STakashi Iwai 			selector_id, ret);
1378c55af3fSEldad Zack 		return -EINVAL;
1388c55af3fSEldad Zack 	}
1398c55af3fSEldad Zack 
1408c55af3fSEldad Zack 	ret = uac_clock_selector_get_val(chip, selector_id);
1418c55af3fSEldad Zack 	if (ret < 0)
1428c55af3fSEldad Zack 		return ret;
1438c55af3fSEldad Zack 
1448c55af3fSEldad Zack 	if (ret != pin) {
1450ba41d91STakashi Iwai 		usb_audio_err(chip,
1460ba41d91STakashi Iwai 			"setting selector (id %d) to %x failed (current: %d)\n",
1470ba41d91STakashi Iwai 			selector_id, pin, ret);
1488c55af3fSEldad Zack 		return -EINVAL;
1498c55af3fSEldad Zack 	}
1508c55af3fSEldad Zack 
1518c55af3fSEldad Zack 	return ret;
1528c55af3fSEldad Zack }
1538c55af3fSEldad Zack 
1549f35a312SAlexander Tsoy static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
155cab941b7STakashi Iwai 					    const struct audioformat *fmt,
1569f35a312SAlexander Tsoy 					    int source_id)
1579f35a312SAlexander Tsoy {
1582edb84e3SAlexander Tsoy 	bool ret = false;
1592edb84e3SAlexander Tsoy 	int count;
1602edb84e3SAlexander Tsoy 	unsigned char data;
1612edb84e3SAlexander Tsoy 	struct usb_device *dev = chip->dev;
1622edb84e3SAlexander Tsoy 
1639f35a312SAlexander Tsoy 	if (fmt->protocol == UAC_VERSION_2) {
1649f35a312SAlexander Tsoy 		struct uac_clock_source_descriptor *cs_desc =
1659f35a312SAlexander Tsoy 			snd_usb_find_clock_source(chip->ctrl_intf, source_id);
1669f35a312SAlexander Tsoy 
1679f35a312SAlexander Tsoy 		if (!cs_desc)
1689f35a312SAlexander Tsoy 			return false;
1699f35a312SAlexander Tsoy 
1702edb84e3SAlexander Tsoy 		/*
1712edb84e3SAlexander Tsoy 		 * Assume the clock is valid if clock source supports only one
1722edb84e3SAlexander Tsoy 		 * single sample rate, the terminal is connected directly to it
1732edb84e3SAlexander Tsoy 		 * (there is no clock selector) and clock type is internal.
1742edb84e3SAlexander Tsoy 		 * This is to deal with some Denon DJ controllers that always
1752edb84e3SAlexander Tsoy 		 * reports that clock is invalid.
1762edb84e3SAlexander Tsoy 		 */
1772edb84e3SAlexander Tsoy 		if (fmt->nr_rates == 1 &&
1789f35a312SAlexander Tsoy 		    (fmt->clock & 0xff) == cs_desc->bClockID &&
1799f35a312SAlexander Tsoy 		    (cs_desc->bmAttributes & 0x3) !=
1802edb84e3SAlexander Tsoy 				UAC_CLOCK_SOURCE_TYPE_EXT)
1812edb84e3SAlexander Tsoy 			return true;
1829f35a312SAlexander Tsoy 	}
1839f35a312SAlexander Tsoy 
1842edb84e3SAlexander Tsoy 	/*
1852edb84e3SAlexander Tsoy 	 * MOTU MicroBook IIc
1862edb84e3SAlexander Tsoy 	 * Sample rate changes takes more than 2 seconds for this device. Clock
1872edb84e3SAlexander Tsoy 	 * validity request returns false during that period.
1882edb84e3SAlexander Tsoy 	 */
1892edb84e3SAlexander Tsoy 	if (chip->usb_id == USB_ID(0x07fd, 0x0004)) {
1902edb84e3SAlexander Tsoy 		count = 0;
1912edb84e3SAlexander Tsoy 
1922edb84e3SAlexander Tsoy 		while ((!ret) && (count < 50)) {
1932edb84e3SAlexander Tsoy 			int err;
1942edb84e3SAlexander Tsoy 
1952edb84e3SAlexander Tsoy 			msleep(100);
1962edb84e3SAlexander Tsoy 
1972edb84e3SAlexander Tsoy 			err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
1982edb84e3SAlexander Tsoy 					      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
1992edb84e3SAlexander Tsoy 					      UAC2_CS_CONTROL_CLOCK_VALID << 8,
2002edb84e3SAlexander Tsoy 					      snd_usb_ctrl_intf(chip) | (source_id << 8),
2012edb84e3SAlexander Tsoy 					      &data, sizeof(data));
2022edb84e3SAlexander Tsoy 			if (err < 0) {
2032edb84e3SAlexander Tsoy 				dev_warn(&dev->dev,
2042edb84e3SAlexander Tsoy 					 "%s(): cannot get clock validity for id %d\n",
2052edb84e3SAlexander Tsoy 					   __func__, source_id);
2069f35a312SAlexander Tsoy 				return false;
2079f35a312SAlexander Tsoy 			}
2089f35a312SAlexander Tsoy 
2092edb84e3SAlexander Tsoy 			ret = !!data;
2102edb84e3SAlexander Tsoy 			count++;
2112edb84e3SAlexander Tsoy 		}
2122edb84e3SAlexander Tsoy 	}
2132edb84e3SAlexander Tsoy 
2142edb84e3SAlexander Tsoy 	return ret;
2152edb84e3SAlexander Tsoy }
2162edb84e3SAlexander Tsoy 
2179a2fe9b8SRuslan Bilovol static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
218cab941b7STakashi Iwai 				      const struct audioformat *fmt,
2199a2fe9b8SRuslan Bilovol 				      int source_id)
22079f920fbSDaniel Mack {
22179f920fbSDaniel Mack 	int err;
22279f920fbSDaniel Mack 	unsigned char data;
22379f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
2249a2fe9b8SRuslan Bilovol 	u32 bmControls;
2259a2fe9b8SRuslan Bilovol 
2269f35a312SAlexander Tsoy 	if (fmt->protocol == UAC_VERSION_3) {
2279a2fe9b8SRuslan Bilovol 		struct uac3_clock_source_descriptor *cs_desc =
2289a2fe9b8SRuslan Bilovol 			snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id);
2299a2fe9b8SRuslan Bilovol 
2309a2fe9b8SRuslan Bilovol 		if (!cs_desc)
2311d4961d9SSaurav Girepunje 			return false;
2329a2fe9b8SRuslan Bilovol 		bmControls = le32_to_cpu(cs_desc->bmControls);
2339a2fe9b8SRuslan Bilovol 	} else { /* UAC_VERSION_1/2 */
2343bc6fbc7SDaniel Mack 		struct uac_clock_source_descriptor *cs_desc =
2353bc6fbc7SDaniel Mack 			snd_usb_find_clock_source(chip->ctrl_intf, source_id);
2363bc6fbc7SDaniel Mack 
2373bc6fbc7SDaniel Mack 		if (!cs_desc)
2381d4961d9SSaurav Girepunje 			return false;
2399a2fe9b8SRuslan Bilovol 		bmControls = cs_desc->bmControls;
2409a2fe9b8SRuslan Bilovol 	}
2413bc6fbc7SDaniel Mack 
2423bc6fbc7SDaniel Mack 	/* If a clock source can't tell us whether it's valid, we assume it is */
2439a2fe9b8SRuslan Bilovol 	if (!uac_v2v3_control_is_readable(bmControls,
24421e9b3e9SAndrew Chant 				      UAC2_CS_CONTROL_CLOCK_VALID))
2451d4961d9SSaurav Girepunje 		return true;
24679f920fbSDaniel Mack 
24779f920fbSDaniel Mack 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
24879f920fbSDaniel Mack 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
24911bcbc44SDaniel Mack 			      UAC2_CS_CONTROL_CLOCK_VALID << 8,
25011bcbc44SDaniel Mack 			      snd_usb_ctrl_intf(chip) | (source_id << 8),
25117d900c4SClemens Ladisch 			      &data, sizeof(data));
25279f920fbSDaniel Mack 
25379f920fbSDaniel Mack 	if (err < 0) {
2540ba41d91STakashi Iwai 		dev_warn(&dev->dev,
2550ba41d91STakashi Iwai 			 "%s(): cannot get clock validity for id %d\n",
25679f920fbSDaniel Mack 			   __func__, source_id);
2571d4961d9SSaurav Girepunje 		return false;
25879f920fbSDaniel Mack 	}
25979f920fbSDaniel Mack 
2609f35a312SAlexander Tsoy 	if (data)
2619f35a312SAlexander Tsoy 		return true;
2629f35a312SAlexander Tsoy 	else
2639f35a312SAlexander Tsoy 		return uac_clock_source_is_valid_quirk(chip, fmt, source_id);
26479f920fbSDaniel Mack }
26579f920fbSDaniel Mack 
2669f35a312SAlexander Tsoy static int __uac_clock_find_source(struct snd_usb_audio *chip,
267cab941b7STakashi Iwai 				   const struct audioformat *fmt, int entity_id,
2689a2fe9b8SRuslan Bilovol 				   unsigned long *visited, bool validate)
26979f920fbSDaniel Mack {
27079f920fbSDaniel Mack 	struct uac_clock_source_descriptor *source;
27179f920fbSDaniel Mack 	struct uac_clock_selector_descriptor *selector;
27279f920fbSDaniel Mack 	struct uac_clock_multiplier_descriptor *multiplier;
27379f920fbSDaniel Mack 
27479f920fbSDaniel Mack 	entity_id &= 0xff;
27579f920fbSDaniel Mack 
27679f920fbSDaniel Mack 	if (test_and_set_bit(entity_id, visited)) {
2770ba41d91STakashi Iwai 		usb_audio_warn(chip,
27879f920fbSDaniel Mack 			 "%s(): recursive clock topology detected, id %d.\n",
27979f920fbSDaniel Mack 			 __func__, entity_id);
28079f920fbSDaniel Mack 		return -EINVAL;
28179f920fbSDaniel Mack 	}
28279f920fbSDaniel Mack 
28379f920fbSDaniel Mack 	/* first, see if the ID we're looking for is a clock source already */
2843d8d4dcfSDaniel Mack 	source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
28506ffc1ebSEldad Zack 	if (source) {
28606ffc1ebSEldad Zack 		entity_id = source->bClockID;
2879f35a312SAlexander Tsoy 		if (validate && !uac_clock_source_is_valid(chip, fmt,
2889a2fe9b8SRuslan Bilovol 								entity_id)) {
2890ba41d91STakashi Iwai 			usb_audio_err(chip,
2900ba41d91STakashi Iwai 				"clock source %d is not valid, cannot use\n",
2910ba41d91STakashi Iwai 				entity_id);
29206ffc1ebSEldad Zack 			return -ENXIO;
29306ffc1ebSEldad Zack 		}
29406ffc1ebSEldad Zack 		return entity_id;
29506ffc1ebSEldad Zack 	}
29679f920fbSDaniel Mack 
2973d8d4dcfSDaniel Mack 	selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
29879f920fbSDaniel Mack 	if (selector) {
2998c55af3fSEldad Zack 		int ret, i, cur;
30079f920fbSDaniel Mack 
301*086b957cSTakashi Iwai 		if (selector->bNrInPins == 1) {
302*086b957cSTakashi Iwai 			ret = 1;
303*086b957cSTakashi Iwai 			goto find_source;
304*086b957cSTakashi Iwai 		}
305*086b957cSTakashi Iwai 
30679f920fbSDaniel Mack 		/* the entity ID we are looking for is a selector.
30779f920fbSDaniel Mack 		 * find out what it currently selects */
30879f920fbSDaniel Mack 		ret = uac_clock_selector_get_val(chip, selector->bClockID);
30979f920fbSDaniel Mack 		if (ret < 0)
31079f920fbSDaniel Mack 			return ret;
31179f920fbSDaniel Mack 
312157a57b6SDaniel Mack 		/* Selector values are one-based */
313157a57b6SDaniel Mack 
31479f920fbSDaniel Mack 		if (ret > selector->bNrInPins || ret < 1) {
3150ba41d91STakashi Iwai 			usb_audio_err(chip,
31679f920fbSDaniel Mack 				"%s(): selector reported illegal value, id %d, ret %d\n",
31779f920fbSDaniel Mack 				__func__, selector->bClockID, ret);
31879f920fbSDaniel Mack 
31979f920fbSDaniel Mack 			return -EINVAL;
32079f920fbSDaniel Mack 		}
32179f920fbSDaniel Mack 
322*086b957cSTakashi Iwai 	find_source:
3238c55af3fSEldad Zack 		cur = ret;
3249f35a312SAlexander Tsoy 		ret = __uac_clock_find_source(chip, fmt,
3259f35a312SAlexander Tsoy 					      selector->baCSourceID[ret - 1],
32606ffc1ebSEldad Zack 					      visited, validate);
327ef02e29bSEldad Zack 		if (!validate || ret > 0 || !chip->autoclock)
3288c55af3fSEldad Zack 			return ret;
3298c55af3fSEldad Zack 
3308c55af3fSEldad Zack 		/* The current clock source is invalid, try others. */
3318c55af3fSEldad Zack 		for (i = 1; i <= selector->bNrInPins; i++) {
3328c55af3fSEldad Zack 			int err;
3338c55af3fSEldad Zack 
3348c55af3fSEldad Zack 			if (i == cur)
3358c55af3fSEldad Zack 				continue;
3368c55af3fSEldad Zack 
3379f35a312SAlexander Tsoy 			ret = __uac_clock_find_source(chip, fmt,
3389f35a312SAlexander Tsoy 						      selector->baCSourceID[i - 1],
3398c55af3fSEldad Zack 						      visited, true);
3408c55af3fSEldad Zack 			if (ret < 0)
3418c55af3fSEldad Zack 				continue;
3428c55af3fSEldad Zack 
3438c55af3fSEldad Zack 			err = uac_clock_selector_set_val(chip, entity_id, i);
3448c55af3fSEldad Zack 			if (err < 0)
3458c55af3fSEldad Zack 				continue;
3468c55af3fSEldad Zack 
3470ba41d91STakashi Iwai 			usb_audio_info(chip,
3480ba41d91STakashi Iwai 				 "found and selected valid clock source %d\n",
3490ba41d91STakashi Iwai 				 ret);
3508c55af3fSEldad Zack 			return ret;
3518c55af3fSEldad Zack 		}
3528c55af3fSEldad Zack 
3538c55af3fSEldad Zack 		return -ENXIO;
35479f920fbSDaniel Mack 	}
35579f920fbSDaniel Mack 
35679f920fbSDaniel Mack 	/* FIXME: multipliers only act as pass-thru element for now */
3573d8d4dcfSDaniel Mack 	multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
35879f920fbSDaniel Mack 	if (multiplier)
3599f35a312SAlexander Tsoy 		return __uac_clock_find_source(chip, fmt,
3609f35a312SAlexander Tsoy 					       multiplier->bCSourceID,
36106ffc1ebSEldad Zack 					       visited, validate);
36279f920fbSDaniel Mack 
36379f920fbSDaniel Mack 	return -EINVAL;
36479f920fbSDaniel Mack }
36579f920fbSDaniel Mack 
3669f35a312SAlexander Tsoy static int __uac3_clock_find_source(struct snd_usb_audio *chip,
367cab941b7STakashi Iwai 				    const struct audioformat *fmt, int entity_id,
3689a2fe9b8SRuslan Bilovol 				    unsigned long *visited, bool validate)
3699a2fe9b8SRuslan Bilovol {
3709a2fe9b8SRuslan Bilovol 	struct uac3_clock_source_descriptor *source;
3719a2fe9b8SRuslan Bilovol 	struct uac3_clock_selector_descriptor *selector;
3729a2fe9b8SRuslan Bilovol 	struct uac3_clock_multiplier_descriptor *multiplier;
3739a2fe9b8SRuslan Bilovol 
3749a2fe9b8SRuslan Bilovol 	entity_id &= 0xff;
3759a2fe9b8SRuslan Bilovol 
3769a2fe9b8SRuslan Bilovol 	if (test_and_set_bit(entity_id, visited)) {
3779a2fe9b8SRuslan Bilovol 		usb_audio_warn(chip,
3789a2fe9b8SRuslan Bilovol 			 "%s(): recursive clock topology detected, id %d.\n",
3799a2fe9b8SRuslan Bilovol 			 __func__, entity_id);
3809a2fe9b8SRuslan Bilovol 		return -EINVAL;
3819a2fe9b8SRuslan Bilovol 	}
3829a2fe9b8SRuslan Bilovol 
3839a2fe9b8SRuslan Bilovol 	/* first, see if the ID we're looking for is a clock source already */
3849a2fe9b8SRuslan Bilovol 	source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
3859a2fe9b8SRuslan Bilovol 	if (source) {
3869a2fe9b8SRuslan Bilovol 		entity_id = source->bClockID;
3879f35a312SAlexander Tsoy 		if (validate && !uac_clock_source_is_valid(chip, fmt,
3889a2fe9b8SRuslan Bilovol 								entity_id)) {
3899a2fe9b8SRuslan Bilovol 			usb_audio_err(chip,
3909a2fe9b8SRuslan Bilovol 				"clock source %d is not valid, cannot use\n",
3919a2fe9b8SRuslan Bilovol 				entity_id);
3929a2fe9b8SRuslan Bilovol 			return -ENXIO;
3939a2fe9b8SRuslan Bilovol 		}
3949a2fe9b8SRuslan Bilovol 		return entity_id;
3959a2fe9b8SRuslan Bilovol 	}
3969a2fe9b8SRuslan Bilovol 
3979a2fe9b8SRuslan Bilovol 	selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id);
3989a2fe9b8SRuslan Bilovol 	if (selector) {
3999a2fe9b8SRuslan Bilovol 		int ret, i, cur;
4009a2fe9b8SRuslan Bilovol 
4019a2fe9b8SRuslan Bilovol 		/* the entity ID we are looking for is a selector.
4029a2fe9b8SRuslan Bilovol 		 * find out what it currently selects */
4039a2fe9b8SRuslan Bilovol 		ret = uac_clock_selector_get_val(chip, selector->bClockID);
4049a2fe9b8SRuslan Bilovol 		if (ret < 0)
4059a2fe9b8SRuslan Bilovol 			return ret;
4069a2fe9b8SRuslan Bilovol 
4079a2fe9b8SRuslan Bilovol 		/* Selector values are one-based */
4089a2fe9b8SRuslan Bilovol 
4099a2fe9b8SRuslan Bilovol 		if (ret > selector->bNrInPins || ret < 1) {
4109a2fe9b8SRuslan Bilovol 			usb_audio_err(chip,
4119a2fe9b8SRuslan Bilovol 				"%s(): selector reported illegal value, id %d, ret %d\n",
4129a2fe9b8SRuslan Bilovol 				__func__, selector->bClockID, ret);
4139a2fe9b8SRuslan Bilovol 
4149a2fe9b8SRuslan Bilovol 			return -EINVAL;
4159a2fe9b8SRuslan Bilovol 		}
4169a2fe9b8SRuslan Bilovol 
4179a2fe9b8SRuslan Bilovol 		cur = ret;
4189f35a312SAlexander Tsoy 		ret = __uac3_clock_find_source(chip, fmt,
4199f35a312SAlexander Tsoy 					       selector->baCSourceID[ret - 1],
4209a2fe9b8SRuslan Bilovol 					       visited, validate);
4219a2fe9b8SRuslan Bilovol 		if (!validate || ret > 0 || !chip->autoclock)
4229a2fe9b8SRuslan Bilovol 			return ret;
4239a2fe9b8SRuslan Bilovol 
4249a2fe9b8SRuslan Bilovol 		/* The current clock source is invalid, try others. */
4259a2fe9b8SRuslan Bilovol 		for (i = 1; i <= selector->bNrInPins; i++) {
4269a2fe9b8SRuslan Bilovol 			int err;
4279a2fe9b8SRuslan Bilovol 
4289a2fe9b8SRuslan Bilovol 			if (i == cur)
4299a2fe9b8SRuslan Bilovol 				continue;
4309a2fe9b8SRuslan Bilovol 
4319f35a312SAlexander Tsoy 			ret = __uac3_clock_find_source(chip, fmt,
4329f35a312SAlexander Tsoy 						       selector->baCSourceID[i - 1],
4339a2fe9b8SRuslan Bilovol 						       visited, true);
4349a2fe9b8SRuslan Bilovol 			if (ret < 0)
4359a2fe9b8SRuslan Bilovol 				continue;
4369a2fe9b8SRuslan Bilovol 
4379a2fe9b8SRuslan Bilovol 			err = uac_clock_selector_set_val(chip, entity_id, i);
4389a2fe9b8SRuslan Bilovol 			if (err < 0)
4399a2fe9b8SRuslan Bilovol 				continue;
4409a2fe9b8SRuslan Bilovol 
4419a2fe9b8SRuslan Bilovol 			usb_audio_info(chip,
4429a2fe9b8SRuslan Bilovol 				 "found and selected valid clock source %d\n",
4439a2fe9b8SRuslan Bilovol 				 ret);
4449a2fe9b8SRuslan Bilovol 			return ret;
4459a2fe9b8SRuslan Bilovol 		}
4469a2fe9b8SRuslan Bilovol 
4479a2fe9b8SRuslan Bilovol 		return -ENXIO;
4489a2fe9b8SRuslan Bilovol 	}
4499a2fe9b8SRuslan Bilovol 
4509a2fe9b8SRuslan Bilovol 	/* FIXME: multipliers only act as pass-thru element for now */
4519a2fe9b8SRuslan Bilovol 	multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
4529a2fe9b8SRuslan Bilovol 						      entity_id);
4539a2fe9b8SRuslan Bilovol 	if (multiplier)
4549f35a312SAlexander Tsoy 		return __uac3_clock_find_source(chip, fmt,
4559f35a312SAlexander Tsoy 						multiplier->bCSourceID,
4569a2fe9b8SRuslan Bilovol 						visited, validate);
4579a2fe9b8SRuslan Bilovol 
4589a2fe9b8SRuslan Bilovol 	return -EINVAL;
4599a2fe9b8SRuslan Bilovol }
4609a2fe9b8SRuslan Bilovol 
461157a57b6SDaniel Mack /*
462157a57b6SDaniel Mack  * For all kinds of sample rate settings and other device queries,
463157a57b6SDaniel Mack  * the clock source (end-leaf) must be used. However, clock selectors,
464157a57b6SDaniel Mack  * clock multipliers and sample rate converters may be specified as
465157a57b6SDaniel Mack  * clock source input to terminal. This functions walks the clock path
466157a57b6SDaniel Mack  * to its end and tries to find the source.
467157a57b6SDaniel Mack  *
468157a57b6SDaniel Mack  * The 'visited' bitfield is used internally to detect recursive loops.
469157a57b6SDaniel Mack  *
470157a57b6SDaniel Mack  * Returns the clock source UnitID (>=0) on success, or an error.
471157a57b6SDaniel Mack  */
4729f35a312SAlexander Tsoy int snd_usb_clock_find_source(struct snd_usb_audio *chip,
473cab941b7STakashi Iwai 			      const struct audioformat *fmt, bool validate)
47479f920fbSDaniel Mack {
47579f920fbSDaniel Mack 	DECLARE_BITMAP(visited, 256);
47679f920fbSDaniel Mack 	memset(visited, 0, sizeof(visited));
4779a2fe9b8SRuslan Bilovol 
4789f35a312SAlexander Tsoy 	switch (fmt->protocol) {
4799a2fe9b8SRuslan Bilovol 	case UAC_VERSION_2:
4809f35a312SAlexander Tsoy 		return __uac_clock_find_source(chip, fmt, fmt->clock, visited,
4819a2fe9b8SRuslan Bilovol 					       validate);
4829a2fe9b8SRuslan Bilovol 	case UAC_VERSION_3:
4839f35a312SAlexander Tsoy 		return __uac3_clock_find_source(chip, fmt, fmt->clock, visited,
4849a2fe9b8SRuslan Bilovol 					       validate);
4859a2fe9b8SRuslan Bilovol 	default:
4869a2fe9b8SRuslan Bilovol 		return -EINVAL;
4879a2fe9b8SRuslan Bilovol 	}
48879f920fbSDaniel Mack }
48979f920fbSDaniel Mack 
490953a446bSTakashi Iwai static int set_sample_rate_v1(struct snd_usb_audio *chip,
491cab941b7STakashi Iwai 			      const struct audioformat *fmt, int rate)
49279f920fbSDaniel Mack {
49379f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
49479f920fbSDaniel Mack 	unsigned char data[3];
49579f920fbSDaniel Mack 	int err, crate;
49679f920fbSDaniel Mack 
49779f920fbSDaniel Mack 	/* if endpoint doesn't have sampling rate control, bail out */
498d32d552eSClemens Ladisch 	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
49979f920fbSDaniel Mack 		return 0;
50079f920fbSDaniel Mack 
50179f920fbSDaniel Mack 	data[0] = rate;
50279f920fbSDaniel Mack 	data[1] = rate >> 8;
50379f920fbSDaniel Mack 	data[2] = rate >> 16;
504f25ecf8fSTakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
50579f920fbSDaniel Mack 			      USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
50687cb9af9STakashi Iwai 			      UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
50787cb9af9STakashi Iwai 			      fmt->endpoint, data, sizeof(data));
508f25ecf8fSTakashi Iwai 	if (err < 0) {
5090ba41d91STakashi Iwai 		dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
51087cb9af9STakashi Iwai 			fmt->iface, fmt->altsetting, rate, fmt->endpoint);
51179f920fbSDaniel Mack 		return err;
51279f920fbSDaniel Mack 	}
51379f920fbSDaniel Mack 
514b62b9980SJoe Turner 	/* Don't check the sample rate for devices which we know don't
515b62b9980SJoe Turner 	 * support reading */
516b62b9980SJoe Turner 	if (snd_usb_get_sample_rate_quirk(chip))
517b62b9980SJoe Turner 		return 0;
51857dd5414STakashi Iwai 	/* the firmware is likely buggy, don't repeat to fail too many times */
51957dd5414STakashi Iwai 	if (chip->sample_rate_read_error > 2)
52057dd5414STakashi Iwai 		return 0;
521b62b9980SJoe Turner 
522f25ecf8fSTakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
52379f920fbSDaniel Mack 			      USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
52487cb9af9STakashi Iwai 			      UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
52587cb9af9STakashi Iwai 			      fmt->endpoint, data, sizeof(data));
526f25ecf8fSTakashi Iwai 	if (err < 0) {
5270ba41d91STakashi Iwai 		dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
52887cb9af9STakashi Iwai 			fmt->iface, fmt->altsetting, fmt->endpoint);
52957dd5414STakashi Iwai 		chip->sample_rate_read_error++;
53079f920fbSDaniel Mack 		return 0; /* some devices don't support reading */
53179f920fbSDaniel Mack 	}
53279f920fbSDaniel Mack 
53379f920fbSDaniel Mack 	crate = data[0] | (data[1] << 8) | (data[2] << 16);
5349df28edcSTakashi Iwai 	if (!crate) {
5359df28edcSTakashi Iwai 		dev_info(&dev->dev, "failed to read current rate; disabling the check\n");
5369df28edcSTakashi Iwai 		chip->sample_rate_read_error = 3; /* three strikes, see above */
5379df28edcSTakashi Iwai 		return 0;
5389df28edcSTakashi Iwai 	}
5399df28edcSTakashi Iwai 
54079f920fbSDaniel Mack 	if (crate != rate) {
5410ba41d91STakashi Iwai 		dev_warn(&dev->dev, "current rate %d is different from the runtime rate %d\n", crate, rate);
54279f920fbSDaniel Mack 		// runtime->rate = crate;
54379f920fbSDaniel Mack 	}
54479f920fbSDaniel Mack 
54579f920fbSDaniel Mack 	return 0;
54679f920fbSDaniel Mack }
54779f920fbSDaniel Mack 
5489a2fe9b8SRuslan Bilovol static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
5497c517465STakashi Iwai 			      int altsetting, int clock)
5507c517465STakashi Iwai {
5517c517465STakashi Iwai 	struct usb_device *dev = chip->dev;
552f6a8bc70SEldad Zack 	__le32 data;
5537c517465STakashi Iwai 	int err;
5547c517465STakashi Iwai 
5557c517465STakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
5567c517465STakashi Iwai 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
5577c517465STakashi Iwai 			      UAC2_CS_CONTROL_SAM_FREQ << 8,
5587c517465STakashi Iwai 			      snd_usb_ctrl_intf(chip) | (clock << 8),
559f6a8bc70SEldad Zack 			      &data, sizeof(data));
5607c517465STakashi Iwai 	if (err < 0) {
5619a2fe9b8SRuslan Bilovol 		dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n",
5620ba41d91STakashi Iwai 			 iface, altsetting, err);
5637c517465STakashi Iwai 		return 0;
5647c517465STakashi Iwai 	}
5657c517465STakashi Iwai 
566f6a8bc70SEldad Zack 	return le32_to_cpu(data);
5677c517465STakashi Iwai }
5687c517465STakashi Iwai 
56993db51d0STakashi Iwai /*
57093db51d0STakashi Iwai  * Try to set the given sample rate:
57193db51d0STakashi Iwai  *
57293db51d0STakashi Iwai  * Return 0 if the clock source is read-only, the actual rate on success,
57393db51d0STakashi Iwai  * or a negative error code.
57493db51d0STakashi Iwai  *
57593db51d0STakashi Iwai  * This function gets called from format.c to validate each sample rate, too.
57693db51d0STakashi Iwai  * Hence no message is shown upon error
57793db51d0STakashi Iwai  */
57893db51d0STakashi Iwai int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip,
57993db51d0STakashi Iwai 				 const struct audioformat *fmt,
58093db51d0STakashi Iwai 				 int clock, int rate)
58193db51d0STakashi Iwai {
58293db51d0STakashi Iwai 	bool writeable;
58393db51d0STakashi Iwai 	u32 bmControls;
58493db51d0STakashi Iwai 	__le32 data;
58593db51d0STakashi Iwai 	int err;
58693db51d0STakashi Iwai 
58793db51d0STakashi Iwai 	if (fmt->protocol == UAC_VERSION_3) {
58893db51d0STakashi Iwai 		struct uac3_clock_source_descriptor *cs_desc;
58993db51d0STakashi Iwai 
59093db51d0STakashi Iwai 		cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock);
59193db51d0STakashi Iwai 		bmControls = le32_to_cpu(cs_desc->bmControls);
59293db51d0STakashi Iwai 	} else {
59393db51d0STakashi Iwai 		struct uac_clock_source_descriptor *cs_desc;
59493db51d0STakashi Iwai 
59593db51d0STakashi Iwai 		cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
59693db51d0STakashi Iwai 		bmControls = cs_desc->bmControls;
59793db51d0STakashi Iwai 	}
59893db51d0STakashi Iwai 
59993db51d0STakashi Iwai 	writeable = uac_v2v3_control_is_writeable(bmControls,
60093db51d0STakashi Iwai 						  UAC2_CS_CONTROL_SAM_FREQ);
60193db51d0STakashi Iwai 	if (!writeable)
60293db51d0STakashi Iwai 		return 0;
60393db51d0STakashi Iwai 
60493db51d0STakashi Iwai 	data = cpu_to_le32(rate);
60593db51d0STakashi Iwai 	err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC2_CS_CUR,
60693db51d0STakashi Iwai 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
60793db51d0STakashi Iwai 			      UAC2_CS_CONTROL_SAM_FREQ << 8,
60893db51d0STakashi Iwai 			      snd_usb_ctrl_intf(chip) | (clock << 8),
60993db51d0STakashi Iwai 			      &data, sizeof(data));
61093db51d0STakashi Iwai 	if (err < 0)
61193db51d0STakashi Iwai 		return err;
61293db51d0STakashi Iwai 
61393db51d0STakashi Iwai 	return get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock);
61493db51d0STakashi Iwai }
61593db51d0STakashi Iwai 
616953a446bSTakashi Iwai static int set_sample_rate_v2v3(struct snd_usb_audio *chip,
617cab941b7STakashi Iwai 				const struct audioformat *fmt, int rate)
61879f920fbSDaniel Mack {
61993db51d0STakashi Iwai 	int cur_rate, prev_rate;
6208c55af3fSEldad Zack 	int clock;
62179f920fbSDaniel Mack 
62258cabe87SAdam Goode 	/* First, try to find a valid clock. This may trigger
62358cabe87SAdam Goode 	 * automatic clock selection if the current clock is not
62458cabe87SAdam Goode 	 * valid.
62558cabe87SAdam Goode 	 */
6269f35a312SAlexander Tsoy 	clock = snd_usb_clock_find_source(chip, fmt, true);
62758cabe87SAdam Goode 	if (clock < 0) {
62858cabe87SAdam Goode 		/* We did not find a valid clock, but that might be
62958cabe87SAdam Goode 		 * because the current sample rate does not match an
63058cabe87SAdam Goode 		 * external clock source. Try again without validation
63158cabe87SAdam Goode 		 * and we will do another validation after setting the
63258cabe87SAdam Goode 		 * rate.
63358cabe87SAdam Goode 		 */
6349f35a312SAlexander Tsoy 		clock = snd_usb_clock_find_source(chip, fmt, false);
63579f920fbSDaniel Mack 		if (clock < 0)
63679f920fbSDaniel Mack 			return clock;
63758cabe87SAdam Goode 	}
63879f920fbSDaniel Mack 
639953a446bSTakashi Iwai 	prev_rate = get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock);
640fa92dd77SDavid Henningsson 	if (prev_rate == rate)
64158cabe87SAdam Goode 		goto validation;
642690a863fSTorstein Hegge 
64393db51d0STakashi Iwai 	cur_rate = snd_usb_set_sample_rate_v2v3(chip, fmt, clock, rate);
64493db51d0STakashi Iwai 	if (cur_rate < 0) {
6450ba41d91STakashi Iwai 		usb_audio_err(chip,
6469a2fe9b8SRuslan Bilovol 			      "%d:%d: cannot set freq %d (v2/v3): err %d\n",
647953a446bSTakashi Iwai 			      fmt->iface, fmt->altsetting, rate, cur_rate);
64893db51d0STakashi Iwai 		return cur_rate;
64979f920fbSDaniel Mack 	}
65079f920fbSDaniel Mack 
65193db51d0STakashi Iwai 	if (!cur_rate)
6521dc669feSEldad Zack 		cur_rate = prev_rate;
65379f920fbSDaniel Mack 
654690a863fSTorstein Hegge 	if (cur_rate != rate) {
6550ba41d91STakashi Iwai 		usb_audio_warn(chip,
6560ba41d91STakashi Iwai 			       "%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n",
65793db51d0STakashi Iwai 			       fmt->iface, fmt->altsetting, rate, cur_rate);
6581dc669feSEldad Zack 		return -ENXIO;
6591dc669feSEldad Zack 	}
660690a863fSTorstein Hegge 
66158cabe87SAdam Goode validation:
66258cabe87SAdam Goode 	/* validate clock after rate change */
6639f35a312SAlexander Tsoy 	if (!uac_clock_source_is_valid(chip, fmt, clock))
66458cabe87SAdam Goode 		return -ENXIO;
66579f920fbSDaniel Mack 	return 0;
66679f920fbSDaniel Mack }
66779f920fbSDaniel Mack 
668953a446bSTakashi Iwai int snd_usb_init_sample_rate(struct snd_usb_audio *chip,
669cab941b7STakashi Iwai 			     const struct audioformat *fmt, int rate)
67079f920fbSDaniel Mack {
671bf6313a0STakashi Iwai 	usb_audio_dbg(chip, "%d:%d Set sample rate %d, clock %d\n",
672bf6313a0STakashi Iwai 		      fmt->iface, fmt->altsetting, rate, fmt->clock);
673bf6313a0STakashi Iwai 
6748f898e92SClemens Ladisch 	switch (fmt->protocol) {
67579f920fbSDaniel Mack 	case UAC_VERSION_1:
676a2acad82SClemens Ladisch 	default:
677953a446bSTakashi Iwai 		return set_sample_rate_v1(chip, fmt, rate);
67879f920fbSDaniel Mack 
6799a2fe9b8SRuslan Bilovol 	case UAC_VERSION_3:
68017156f23SRuslan Bilovol 		if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
68117156f23SRuslan Bilovol 			if (rate != UAC3_BADD_SAMPLING_RATE)
68217156f23SRuslan Bilovol 				return -ENXIO;
68317156f23SRuslan Bilovol 			else
68417156f23SRuslan Bilovol 				return 0;
68517156f23SRuslan Bilovol 		}
686c0dbbdadSGustavo A. R. Silva 		fallthrough;
68717156f23SRuslan Bilovol 	case UAC_VERSION_2:
688953a446bSTakashi Iwai 		return set_sample_rate_v2v3(chip, fmt, rate);
68979f920fbSDaniel Mack 	}
69079f920fbSDaniel Mack }
69179f920fbSDaniel Mack 
692