xref: /openbmc/linux/sound/usb/clock.c (revision cab941b7)
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,
155*cab941b7STakashi 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,
218*cab941b7STakashi 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,
267*cab941b7STakashi 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 
30179f920fbSDaniel Mack 		/* the entity ID we are looking for is a selector.
30279f920fbSDaniel Mack 		 * find out what it currently selects */
30379f920fbSDaniel Mack 		ret = uac_clock_selector_get_val(chip, selector->bClockID);
30479f920fbSDaniel Mack 		if (ret < 0)
30579f920fbSDaniel Mack 			return ret;
30679f920fbSDaniel Mack 
307157a57b6SDaniel Mack 		/* Selector values are one-based */
308157a57b6SDaniel Mack 
30979f920fbSDaniel Mack 		if (ret > selector->bNrInPins || ret < 1) {
3100ba41d91STakashi Iwai 			usb_audio_err(chip,
31179f920fbSDaniel Mack 				"%s(): selector reported illegal value, id %d, ret %d\n",
31279f920fbSDaniel Mack 				__func__, selector->bClockID, ret);
31379f920fbSDaniel Mack 
31479f920fbSDaniel Mack 			return -EINVAL;
31579f920fbSDaniel Mack 		}
31679f920fbSDaniel Mack 
3178c55af3fSEldad Zack 		cur = ret;
3189f35a312SAlexander Tsoy 		ret = __uac_clock_find_source(chip, fmt,
3199f35a312SAlexander Tsoy 					      selector->baCSourceID[ret - 1],
32006ffc1ebSEldad Zack 					      visited, validate);
321ef02e29bSEldad Zack 		if (!validate || ret > 0 || !chip->autoclock)
3228c55af3fSEldad Zack 			return ret;
3238c55af3fSEldad Zack 
3248c55af3fSEldad Zack 		/* The current clock source is invalid, try others. */
3258c55af3fSEldad Zack 		for (i = 1; i <= selector->bNrInPins; i++) {
3268c55af3fSEldad Zack 			int err;
3278c55af3fSEldad Zack 
3288c55af3fSEldad Zack 			if (i == cur)
3298c55af3fSEldad Zack 				continue;
3308c55af3fSEldad Zack 
3319f35a312SAlexander Tsoy 			ret = __uac_clock_find_source(chip, fmt,
3329f35a312SAlexander Tsoy 						      selector->baCSourceID[i - 1],
3338c55af3fSEldad Zack 						      visited, true);
3348c55af3fSEldad Zack 			if (ret < 0)
3358c55af3fSEldad Zack 				continue;
3368c55af3fSEldad Zack 
3378c55af3fSEldad Zack 			err = uac_clock_selector_set_val(chip, entity_id, i);
3388c55af3fSEldad Zack 			if (err < 0)
3398c55af3fSEldad Zack 				continue;
3408c55af3fSEldad Zack 
3410ba41d91STakashi Iwai 			usb_audio_info(chip,
3420ba41d91STakashi Iwai 				 "found and selected valid clock source %d\n",
3430ba41d91STakashi Iwai 				 ret);
3448c55af3fSEldad Zack 			return ret;
3458c55af3fSEldad Zack 		}
3468c55af3fSEldad Zack 
3478c55af3fSEldad Zack 		return -ENXIO;
34879f920fbSDaniel Mack 	}
34979f920fbSDaniel Mack 
35079f920fbSDaniel Mack 	/* FIXME: multipliers only act as pass-thru element for now */
3513d8d4dcfSDaniel Mack 	multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
35279f920fbSDaniel Mack 	if (multiplier)
3539f35a312SAlexander Tsoy 		return __uac_clock_find_source(chip, fmt,
3549f35a312SAlexander Tsoy 					       multiplier->bCSourceID,
35506ffc1ebSEldad Zack 					       visited, validate);
35679f920fbSDaniel Mack 
35779f920fbSDaniel Mack 	return -EINVAL;
35879f920fbSDaniel Mack }
35979f920fbSDaniel Mack 
3609f35a312SAlexander Tsoy static int __uac3_clock_find_source(struct snd_usb_audio *chip,
361*cab941b7STakashi Iwai 				    const struct audioformat *fmt, int entity_id,
3629a2fe9b8SRuslan Bilovol 				    unsigned long *visited, bool validate)
3639a2fe9b8SRuslan Bilovol {
3649a2fe9b8SRuslan Bilovol 	struct uac3_clock_source_descriptor *source;
3659a2fe9b8SRuslan Bilovol 	struct uac3_clock_selector_descriptor *selector;
3669a2fe9b8SRuslan Bilovol 	struct uac3_clock_multiplier_descriptor *multiplier;
3679a2fe9b8SRuslan Bilovol 
3689a2fe9b8SRuslan Bilovol 	entity_id &= 0xff;
3699a2fe9b8SRuslan Bilovol 
3709a2fe9b8SRuslan Bilovol 	if (test_and_set_bit(entity_id, visited)) {
3719a2fe9b8SRuslan Bilovol 		usb_audio_warn(chip,
3729a2fe9b8SRuslan Bilovol 			 "%s(): recursive clock topology detected, id %d.\n",
3739a2fe9b8SRuslan Bilovol 			 __func__, entity_id);
3749a2fe9b8SRuslan Bilovol 		return -EINVAL;
3759a2fe9b8SRuslan Bilovol 	}
3769a2fe9b8SRuslan Bilovol 
3779a2fe9b8SRuslan Bilovol 	/* first, see if the ID we're looking for is a clock source already */
3789a2fe9b8SRuslan Bilovol 	source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
3799a2fe9b8SRuslan Bilovol 	if (source) {
3809a2fe9b8SRuslan Bilovol 		entity_id = source->bClockID;
3819f35a312SAlexander Tsoy 		if (validate && !uac_clock_source_is_valid(chip, fmt,
3829a2fe9b8SRuslan Bilovol 								entity_id)) {
3839a2fe9b8SRuslan Bilovol 			usb_audio_err(chip,
3849a2fe9b8SRuslan Bilovol 				"clock source %d is not valid, cannot use\n",
3859a2fe9b8SRuslan Bilovol 				entity_id);
3869a2fe9b8SRuslan Bilovol 			return -ENXIO;
3879a2fe9b8SRuslan Bilovol 		}
3889a2fe9b8SRuslan Bilovol 		return entity_id;
3899a2fe9b8SRuslan Bilovol 	}
3909a2fe9b8SRuslan Bilovol 
3919a2fe9b8SRuslan Bilovol 	selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id);
3929a2fe9b8SRuslan Bilovol 	if (selector) {
3939a2fe9b8SRuslan Bilovol 		int ret, i, cur;
3949a2fe9b8SRuslan Bilovol 
3959a2fe9b8SRuslan Bilovol 		/* the entity ID we are looking for is a selector.
3969a2fe9b8SRuslan Bilovol 		 * find out what it currently selects */
3979a2fe9b8SRuslan Bilovol 		ret = uac_clock_selector_get_val(chip, selector->bClockID);
3989a2fe9b8SRuslan Bilovol 		if (ret < 0)
3999a2fe9b8SRuslan Bilovol 			return ret;
4009a2fe9b8SRuslan Bilovol 
4019a2fe9b8SRuslan Bilovol 		/* Selector values are one-based */
4029a2fe9b8SRuslan Bilovol 
4039a2fe9b8SRuslan Bilovol 		if (ret > selector->bNrInPins || ret < 1) {
4049a2fe9b8SRuslan Bilovol 			usb_audio_err(chip,
4059a2fe9b8SRuslan Bilovol 				"%s(): selector reported illegal value, id %d, ret %d\n",
4069a2fe9b8SRuslan Bilovol 				__func__, selector->bClockID, ret);
4079a2fe9b8SRuslan Bilovol 
4089a2fe9b8SRuslan Bilovol 			return -EINVAL;
4099a2fe9b8SRuslan Bilovol 		}
4109a2fe9b8SRuslan Bilovol 
4119a2fe9b8SRuslan Bilovol 		cur = ret;
4129f35a312SAlexander Tsoy 		ret = __uac3_clock_find_source(chip, fmt,
4139f35a312SAlexander Tsoy 					       selector->baCSourceID[ret - 1],
4149a2fe9b8SRuslan Bilovol 					       visited, validate);
4159a2fe9b8SRuslan Bilovol 		if (!validate || ret > 0 || !chip->autoclock)
4169a2fe9b8SRuslan Bilovol 			return ret;
4179a2fe9b8SRuslan Bilovol 
4189a2fe9b8SRuslan Bilovol 		/* The current clock source is invalid, try others. */
4199a2fe9b8SRuslan Bilovol 		for (i = 1; i <= selector->bNrInPins; i++) {
4209a2fe9b8SRuslan Bilovol 			int err;
4219a2fe9b8SRuslan Bilovol 
4229a2fe9b8SRuslan Bilovol 			if (i == cur)
4239a2fe9b8SRuslan Bilovol 				continue;
4249a2fe9b8SRuslan Bilovol 
4259f35a312SAlexander Tsoy 			ret = __uac3_clock_find_source(chip, fmt,
4269f35a312SAlexander Tsoy 						       selector->baCSourceID[i - 1],
4279a2fe9b8SRuslan Bilovol 						       visited, true);
4289a2fe9b8SRuslan Bilovol 			if (ret < 0)
4299a2fe9b8SRuslan Bilovol 				continue;
4309a2fe9b8SRuslan Bilovol 
4319a2fe9b8SRuslan Bilovol 			err = uac_clock_selector_set_val(chip, entity_id, i);
4329a2fe9b8SRuslan Bilovol 			if (err < 0)
4339a2fe9b8SRuslan Bilovol 				continue;
4349a2fe9b8SRuslan Bilovol 
4359a2fe9b8SRuslan Bilovol 			usb_audio_info(chip,
4369a2fe9b8SRuslan Bilovol 				 "found and selected valid clock source %d\n",
4379a2fe9b8SRuslan Bilovol 				 ret);
4389a2fe9b8SRuslan Bilovol 			return ret;
4399a2fe9b8SRuslan Bilovol 		}
4409a2fe9b8SRuslan Bilovol 
4419a2fe9b8SRuslan Bilovol 		return -ENXIO;
4429a2fe9b8SRuslan Bilovol 	}
4439a2fe9b8SRuslan Bilovol 
4449a2fe9b8SRuslan Bilovol 	/* FIXME: multipliers only act as pass-thru element for now */
4459a2fe9b8SRuslan Bilovol 	multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
4469a2fe9b8SRuslan Bilovol 						      entity_id);
4479a2fe9b8SRuslan Bilovol 	if (multiplier)
4489f35a312SAlexander Tsoy 		return __uac3_clock_find_source(chip, fmt,
4499f35a312SAlexander Tsoy 						multiplier->bCSourceID,
4509a2fe9b8SRuslan Bilovol 						visited, validate);
4519a2fe9b8SRuslan Bilovol 
4529a2fe9b8SRuslan Bilovol 	return -EINVAL;
4539a2fe9b8SRuslan Bilovol }
4549a2fe9b8SRuslan Bilovol 
455157a57b6SDaniel Mack /*
456157a57b6SDaniel Mack  * For all kinds of sample rate settings and other device queries,
457157a57b6SDaniel Mack  * the clock source (end-leaf) must be used. However, clock selectors,
458157a57b6SDaniel Mack  * clock multipliers and sample rate converters may be specified as
459157a57b6SDaniel Mack  * clock source input to terminal. This functions walks the clock path
460157a57b6SDaniel Mack  * to its end and tries to find the source.
461157a57b6SDaniel Mack  *
462157a57b6SDaniel Mack  * The 'visited' bitfield is used internally to detect recursive loops.
463157a57b6SDaniel Mack  *
464157a57b6SDaniel Mack  * Returns the clock source UnitID (>=0) on success, or an error.
465157a57b6SDaniel Mack  */
4669f35a312SAlexander Tsoy int snd_usb_clock_find_source(struct snd_usb_audio *chip,
467*cab941b7STakashi Iwai 			      const struct audioformat *fmt, bool validate)
46879f920fbSDaniel Mack {
46979f920fbSDaniel Mack 	DECLARE_BITMAP(visited, 256);
47079f920fbSDaniel Mack 	memset(visited, 0, sizeof(visited));
4719a2fe9b8SRuslan Bilovol 
4729f35a312SAlexander Tsoy 	switch (fmt->protocol) {
4739a2fe9b8SRuslan Bilovol 	case UAC_VERSION_2:
4749f35a312SAlexander Tsoy 		return __uac_clock_find_source(chip, fmt, fmt->clock, visited,
4759a2fe9b8SRuslan Bilovol 					       validate);
4769a2fe9b8SRuslan Bilovol 	case UAC_VERSION_3:
4779f35a312SAlexander Tsoy 		return __uac3_clock_find_source(chip, fmt, fmt->clock, visited,
4789a2fe9b8SRuslan Bilovol 					       validate);
4799a2fe9b8SRuslan Bilovol 	default:
4809a2fe9b8SRuslan Bilovol 		return -EINVAL;
4819a2fe9b8SRuslan Bilovol 	}
48279f920fbSDaniel Mack }
48379f920fbSDaniel Mack 
484953a446bSTakashi Iwai static int set_sample_rate_v1(struct snd_usb_audio *chip,
485*cab941b7STakashi Iwai 			      const struct audioformat *fmt, int rate)
48679f920fbSDaniel Mack {
48779f920fbSDaniel Mack 	struct usb_device *dev = chip->dev;
488953a446bSTakashi Iwai 	struct usb_host_interface *alts;
48979f920fbSDaniel Mack 	unsigned int ep;
49079f920fbSDaniel Mack 	unsigned char data[3];
49179f920fbSDaniel Mack 	int err, crate;
49279f920fbSDaniel Mack 
493953a446bSTakashi Iwai 	alts = snd_usb_get_host_interface(chip, fmt->iface, fmt->altsetting);
494953a446bSTakashi Iwai 	if (!alts)
495953a446bSTakashi Iwai 		return -EINVAL;
496447d6275STakashi Iwai 	if (get_iface_desc(alts)->bNumEndpoints < 1)
497447d6275STakashi Iwai 		return -EINVAL;
49879f920fbSDaniel Mack 	ep = get_endpoint(alts, 0)->bEndpointAddress;
49979f920fbSDaniel Mack 
50079f920fbSDaniel Mack 	/* if endpoint doesn't have sampling rate control, bail out */
501d32d552eSClemens Ladisch 	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
50279f920fbSDaniel Mack 		return 0;
50379f920fbSDaniel Mack 
50479f920fbSDaniel Mack 	data[0] = rate;
50579f920fbSDaniel Mack 	data[1] = rate >> 8;
50679f920fbSDaniel Mack 	data[2] = rate >> 16;
507f25ecf8fSTakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
50879f920fbSDaniel Mack 			      USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
50979f920fbSDaniel Mack 			      UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
510f25ecf8fSTakashi Iwai 			      data, sizeof(data));
511f25ecf8fSTakashi Iwai 	if (err < 0) {
5120ba41d91STakashi Iwai 		dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
513953a446bSTakashi Iwai 			fmt->iface, fmt->altsetting, rate, ep);
51479f920fbSDaniel Mack 		return err;
51579f920fbSDaniel Mack 	}
51679f920fbSDaniel Mack 
517b62b9980SJoe Turner 	/* Don't check the sample rate for devices which we know don't
518b62b9980SJoe Turner 	 * support reading */
519b62b9980SJoe Turner 	if (snd_usb_get_sample_rate_quirk(chip))
520b62b9980SJoe Turner 		return 0;
52157dd5414STakashi Iwai 	/* the firmware is likely buggy, don't repeat to fail too many times */
52257dd5414STakashi Iwai 	if (chip->sample_rate_read_error > 2)
52357dd5414STakashi Iwai 		return 0;
524b62b9980SJoe Turner 
525f25ecf8fSTakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
52679f920fbSDaniel Mack 			      USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
52779f920fbSDaniel Mack 			      UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
528f25ecf8fSTakashi Iwai 			      data, sizeof(data));
529f25ecf8fSTakashi Iwai 	if (err < 0) {
5300ba41d91STakashi Iwai 		dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
531953a446bSTakashi Iwai 			fmt->iface, fmt->altsetting, ep);
53257dd5414STakashi Iwai 		chip->sample_rate_read_error++;
53379f920fbSDaniel Mack 		return 0; /* some devices don't support reading */
53479f920fbSDaniel Mack 	}
53579f920fbSDaniel Mack 
53679f920fbSDaniel Mack 	crate = data[0] | (data[1] << 8) | (data[2] << 16);
53779f920fbSDaniel Mack 	if (crate != rate) {
5380ba41d91STakashi Iwai 		dev_warn(&dev->dev, "current rate %d is different from the runtime rate %d\n", crate, rate);
53979f920fbSDaniel Mack 		// runtime->rate = crate;
54079f920fbSDaniel Mack 	}
54179f920fbSDaniel Mack 
54279f920fbSDaniel Mack 	return 0;
54379f920fbSDaniel Mack }
54479f920fbSDaniel Mack 
5459a2fe9b8SRuslan Bilovol static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
5467c517465STakashi Iwai 			      int altsetting, int clock)
5477c517465STakashi Iwai {
5487c517465STakashi Iwai 	struct usb_device *dev = chip->dev;
549f6a8bc70SEldad Zack 	__le32 data;
5507c517465STakashi Iwai 	int err;
5517c517465STakashi Iwai 
5527c517465STakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
5537c517465STakashi Iwai 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
5547c517465STakashi Iwai 			      UAC2_CS_CONTROL_SAM_FREQ << 8,
5557c517465STakashi Iwai 			      snd_usb_ctrl_intf(chip) | (clock << 8),
556f6a8bc70SEldad Zack 			      &data, sizeof(data));
5577c517465STakashi Iwai 	if (err < 0) {
5589a2fe9b8SRuslan Bilovol 		dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n",
5590ba41d91STakashi Iwai 			 iface, altsetting, err);
5607c517465STakashi Iwai 		return 0;
5617c517465STakashi Iwai 	}
5627c517465STakashi Iwai 
563f6a8bc70SEldad Zack 	return le32_to_cpu(data);
5647c517465STakashi Iwai }
5657c517465STakashi Iwai 
56693db51d0STakashi Iwai /*
56793db51d0STakashi Iwai  * Try to set the given sample rate:
56893db51d0STakashi Iwai  *
56993db51d0STakashi Iwai  * Return 0 if the clock source is read-only, the actual rate on success,
57093db51d0STakashi Iwai  * or a negative error code.
57193db51d0STakashi Iwai  *
57293db51d0STakashi Iwai  * This function gets called from format.c to validate each sample rate, too.
57393db51d0STakashi Iwai  * Hence no message is shown upon error
57493db51d0STakashi Iwai  */
57593db51d0STakashi Iwai int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip,
57693db51d0STakashi Iwai 				 const struct audioformat *fmt,
57793db51d0STakashi Iwai 				 int clock, int rate)
57893db51d0STakashi Iwai {
57993db51d0STakashi Iwai 	bool writeable;
58093db51d0STakashi Iwai 	u32 bmControls;
58193db51d0STakashi Iwai 	__le32 data;
58293db51d0STakashi Iwai 	int err;
58393db51d0STakashi Iwai 
58493db51d0STakashi Iwai 	if (fmt->protocol == UAC_VERSION_3) {
58593db51d0STakashi Iwai 		struct uac3_clock_source_descriptor *cs_desc;
58693db51d0STakashi Iwai 
58793db51d0STakashi Iwai 		cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock);
58893db51d0STakashi Iwai 		bmControls = le32_to_cpu(cs_desc->bmControls);
58993db51d0STakashi Iwai 	} else {
59093db51d0STakashi Iwai 		struct uac_clock_source_descriptor *cs_desc;
59193db51d0STakashi Iwai 
59293db51d0STakashi Iwai 		cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
59393db51d0STakashi Iwai 		bmControls = cs_desc->bmControls;
59493db51d0STakashi Iwai 	}
59593db51d0STakashi Iwai 
59693db51d0STakashi Iwai 	writeable = uac_v2v3_control_is_writeable(bmControls,
59793db51d0STakashi Iwai 						  UAC2_CS_CONTROL_SAM_FREQ);
59893db51d0STakashi Iwai 	if (!writeable)
59993db51d0STakashi Iwai 		return 0;
60093db51d0STakashi Iwai 
60193db51d0STakashi Iwai 	data = cpu_to_le32(rate);
60293db51d0STakashi Iwai 	err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC2_CS_CUR,
60393db51d0STakashi Iwai 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
60493db51d0STakashi Iwai 			      UAC2_CS_CONTROL_SAM_FREQ << 8,
60593db51d0STakashi Iwai 			      snd_usb_ctrl_intf(chip) | (clock << 8),
60693db51d0STakashi Iwai 			      &data, sizeof(data));
60793db51d0STakashi Iwai 	if (err < 0)
60893db51d0STakashi Iwai 		return err;
60993db51d0STakashi Iwai 
61093db51d0STakashi Iwai 	return get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock);
61193db51d0STakashi Iwai }
61293db51d0STakashi Iwai 
613953a446bSTakashi Iwai static int set_sample_rate_v2v3(struct snd_usb_audio *chip,
614*cab941b7STakashi Iwai 				const struct audioformat *fmt, int rate)
61579f920fbSDaniel Mack {
61693db51d0STakashi Iwai 	int cur_rate, prev_rate;
6178c55af3fSEldad Zack 	int clock;
61879f920fbSDaniel Mack 
61958cabe87SAdam Goode 	/* First, try to find a valid clock. This may trigger
62058cabe87SAdam Goode 	 * automatic clock selection if the current clock is not
62158cabe87SAdam Goode 	 * valid.
62258cabe87SAdam Goode 	 */
6239f35a312SAlexander Tsoy 	clock = snd_usb_clock_find_source(chip, fmt, true);
62458cabe87SAdam Goode 	if (clock < 0) {
62558cabe87SAdam Goode 		/* We did not find a valid clock, but that might be
62658cabe87SAdam Goode 		 * because the current sample rate does not match an
62758cabe87SAdam Goode 		 * external clock source. Try again without validation
62858cabe87SAdam Goode 		 * and we will do another validation after setting the
62958cabe87SAdam Goode 		 * rate.
63058cabe87SAdam Goode 		 */
6319f35a312SAlexander Tsoy 		clock = snd_usb_clock_find_source(chip, fmt, false);
63279f920fbSDaniel Mack 		if (clock < 0)
63379f920fbSDaniel Mack 			return clock;
63458cabe87SAdam Goode 	}
63579f920fbSDaniel Mack 
636953a446bSTakashi Iwai 	prev_rate = get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock);
637fa92dd77SDavid Henningsson 	if (prev_rate == rate)
63858cabe87SAdam Goode 		goto validation;
639690a863fSTorstein Hegge 
64093db51d0STakashi Iwai 	cur_rate = snd_usb_set_sample_rate_v2v3(chip, fmt, clock, rate);
64193db51d0STakashi Iwai 	if (cur_rate < 0) {
6420ba41d91STakashi Iwai 		usb_audio_err(chip,
6439a2fe9b8SRuslan Bilovol 			      "%d:%d: cannot set freq %d (v2/v3): err %d\n",
644953a446bSTakashi Iwai 			      fmt->iface, fmt->altsetting, rate, cur_rate);
64593db51d0STakashi Iwai 		return cur_rate;
64679f920fbSDaniel Mack 	}
64779f920fbSDaniel Mack 
64893db51d0STakashi Iwai 	if (!cur_rate)
6491dc669feSEldad Zack 		cur_rate = prev_rate;
65079f920fbSDaniel Mack 
651690a863fSTorstein Hegge 	if (cur_rate != rate) {
6520ba41d91STakashi Iwai 		usb_audio_warn(chip,
6530ba41d91STakashi Iwai 			       "%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n",
65493db51d0STakashi Iwai 			       fmt->iface, fmt->altsetting, rate, cur_rate);
6551dc669feSEldad Zack 		return -ENXIO;
6561dc669feSEldad Zack 	}
657690a863fSTorstein Hegge 
65858cabe87SAdam Goode validation:
65958cabe87SAdam Goode 	/* validate clock after rate change */
6609f35a312SAlexander Tsoy 	if (!uac_clock_source_is_valid(chip, fmt, clock))
66158cabe87SAdam Goode 		return -ENXIO;
66279f920fbSDaniel Mack 	return 0;
66379f920fbSDaniel Mack }
66479f920fbSDaniel Mack 
665953a446bSTakashi Iwai int snd_usb_init_sample_rate(struct snd_usb_audio *chip,
666*cab941b7STakashi Iwai 			     const struct audioformat *fmt, int rate)
66779f920fbSDaniel Mack {
668bf6313a0STakashi Iwai 	usb_audio_dbg(chip, "%d:%d Set sample rate %d, clock %d\n",
669bf6313a0STakashi Iwai 		      fmt->iface, fmt->altsetting, rate, fmt->clock);
670bf6313a0STakashi Iwai 
6718f898e92SClemens Ladisch 	switch (fmt->protocol) {
67279f920fbSDaniel Mack 	case UAC_VERSION_1:
673a2acad82SClemens Ladisch 	default:
674953a446bSTakashi Iwai 		return set_sample_rate_v1(chip, fmt, rate);
67579f920fbSDaniel Mack 
6769a2fe9b8SRuslan Bilovol 	case UAC_VERSION_3:
67717156f23SRuslan Bilovol 		if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
67817156f23SRuslan Bilovol 			if (rate != UAC3_BADD_SAMPLING_RATE)
67917156f23SRuslan Bilovol 				return -ENXIO;
68017156f23SRuslan Bilovol 			else
68117156f23SRuslan Bilovol 				return 0;
68217156f23SRuslan Bilovol 		}
683c0dbbdadSGustavo A. R. Silva 		fallthrough;
68417156f23SRuslan Bilovol 	case UAC_VERSION_2:
685953a446bSTakashi Iwai 		return set_sample_rate_v2v3(chip, fmt, rate);
68679f920fbSDaniel Mack 	}
68779f920fbSDaniel Mack }
68879f920fbSDaniel Mack 
689