18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Clock domain and sample rate management functions 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/bitops.h> 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/string.h> 98c2ecf20Sopenharmony_ci#include <linux/usb.h> 108c2ecf20Sopenharmony_ci#include <linux/usb/audio.h> 118c2ecf20Sopenharmony_ci#include <linux/usb/audio-v2.h> 128c2ecf20Sopenharmony_ci#include <linux/usb/audio-v3.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/info.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "usbaudio.h" 198c2ecf20Sopenharmony_ci#include "card.h" 208c2ecf20Sopenharmony_ci#include "helper.h" 218c2ecf20Sopenharmony_ci#include "clock.h" 228c2ecf20Sopenharmony_ci#include "quirks.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void *find_uac_clock_desc(struct usb_host_interface *iface, int id, 258c2ecf20Sopenharmony_ci bool (*validator)(void *, int), u8 type) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci void *cs = NULL; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci while ((cs = snd_usb_find_csint_desc(iface->extra, iface->extralen, 308c2ecf20Sopenharmony_ci cs, type))) { 318c2ecf20Sopenharmony_ci if (validator(cs, id)) 328c2ecf20Sopenharmony_ci return cs; 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci return NULL; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic bool validate_clock_source_v2(void *p, int id) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct uac_clock_source_descriptor *cs = p; 418c2ecf20Sopenharmony_ci return cs->bClockID == id; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic bool validate_clock_source_v3(void *p, int id) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct uac3_clock_source_descriptor *cs = p; 478c2ecf20Sopenharmony_ci return cs->bClockID == id; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic bool validate_clock_selector_v2(void *p, int id) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct uac_clock_selector_descriptor *cs = p; 538c2ecf20Sopenharmony_ci return cs->bClockID == id; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic bool validate_clock_selector_v3(void *p, int id) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct uac3_clock_selector_descriptor *cs = p; 598c2ecf20Sopenharmony_ci return cs->bClockID == id; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic bool validate_clock_multiplier_v2(void *p, int id) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct uac_clock_multiplier_descriptor *cs = p; 658c2ecf20Sopenharmony_ci return cs->bClockID == id; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic bool validate_clock_multiplier_v3(void *p, int id) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct uac3_clock_multiplier_descriptor *cs = p; 718c2ecf20Sopenharmony_ci return cs->bClockID == id; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define DEFINE_FIND_HELPER(name, obj, validator, type) \ 758c2ecf20Sopenharmony_cistatic obj *name(struct usb_host_interface *iface, int id) \ 768c2ecf20Sopenharmony_ci{ \ 778c2ecf20Sopenharmony_ci return find_uac_clock_desc(iface, id, validator, type); \ 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ciDEFINE_FIND_HELPER(snd_usb_find_clock_source, 818c2ecf20Sopenharmony_ci struct uac_clock_source_descriptor, 828c2ecf20Sopenharmony_ci validate_clock_source_v2, UAC2_CLOCK_SOURCE); 838c2ecf20Sopenharmony_ciDEFINE_FIND_HELPER(snd_usb_find_clock_source_v3, 848c2ecf20Sopenharmony_ci struct uac3_clock_source_descriptor, 858c2ecf20Sopenharmony_ci validate_clock_source_v3, UAC3_CLOCK_SOURCE); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciDEFINE_FIND_HELPER(snd_usb_find_clock_selector, 888c2ecf20Sopenharmony_ci struct uac_clock_selector_descriptor, 898c2ecf20Sopenharmony_ci validate_clock_selector_v2, UAC2_CLOCK_SELECTOR); 908c2ecf20Sopenharmony_ciDEFINE_FIND_HELPER(snd_usb_find_clock_selector_v3, 918c2ecf20Sopenharmony_ci struct uac3_clock_selector_descriptor, 928c2ecf20Sopenharmony_ci validate_clock_selector_v3, UAC3_CLOCK_SELECTOR); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciDEFINE_FIND_HELPER(snd_usb_find_clock_multiplier, 958c2ecf20Sopenharmony_ci struct uac_clock_multiplier_descriptor, 968c2ecf20Sopenharmony_ci validate_clock_multiplier_v2, UAC2_CLOCK_MULTIPLIER); 978c2ecf20Sopenharmony_ciDEFINE_FIND_HELPER(snd_usb_find_clock_multiplier_v3, 988c2ecf20Sopenharmony_ci struct uac3_clock_multiplier_descriptor, 998c2ecf20Sopenharmony_ci validate_clock_multiplier_v3, UAC3_CLOCK_MULTIPLIER); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci unsigned char buf; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), 1078c2ecf20Sopenharmony_ci UAC2_CS_CUR, 1088c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 1098c2ecf20Sopenharmony_ci UAC2_CX_CLOCK_SELECTOR << 8, 1108c2ecf20Sopenharmony_ci snd_usb_ctrl_intf(chip) | (selector_id << 8), 1118c2ecf20Sopenharmony_ci &buf, sizeof(buf)); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (ret < 0) 1148c2ecf20Sopenharmony_ci return ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return buf; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id, 1208c2ecf20Sopenharmony_ci unsigned char pin) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), 1258c2ecf20Sopenharmony_ci UAC2_CS_CUR, 1268c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, 1278c2ecf20Sopenharmony_ci UAC2_CX_CLOCK_SELECTOR << 8, 1288c2ecf20Sopenharmony_ci snd_usb_ctrl_intf(chip) | (selector_id << 8), 1298c2ecf20Sopenharmony_ci &pin, sizeof(pin)); 1308c2ecf20Sopenharmony_ci if (ret < 0) 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (ret != sizeof(pin)) { 1348c2ecf20Sopenharmony_ci usb_audio_err(chip, 1358c2ecf20Sopenharmony_ci "setting selector (id %d) unexpected length %d\n", 1368c2ecf20Sopenharmony_ci selector_id, ret); 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ret = uac_clock_selector_get_val(chip, selector_id); 1418c2ecf20Sopenharmony_ci if (ret < 0) 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (ret != pin) { 1458c2ecf20Sopenharmony_ci usb_audio_err(chip, 1468c2ecf20Sopenharmony_ci "setting selector (id %d) to %x failed (current: %d)\n", 1478c2ecf20Sopenharmony_ci selector_id, pin, ret); 1488c2ecf20Sopenharmony_ci return -EINVAL; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, 1558c2ecf20Sopenharmony_ci struct audioformat *fmt, 1568c2ecf20Sopenharmony_ci int source_id) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci bool ret = false; 1598c2ecf20Sopenharmony_ci int count; 1608c2ecf20Sopenharmony_ci unsigned char data; 1618c2ecf20Sopenharmony_ci struct usb_device *dev = chip->dev; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (fmt->protocol == UAC_VERSION_2) { 1648c2ecf20Sopenharmony_ci struct uac_clock_source_descriptor *cs_desc = 1658c2ecf20Sopenharmony_ci snd_usb_find_clock_source(chip->ctrl_intf, source_id); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!cs_desc) 1688c2ecf20Sopenharmony_ci return false; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * Assume the clock is valid if clock source supports only one 1728c2ecf20Sopenharmony_ci * single sample rate, the terminal is connected directly to it 1738c2ecf20Sopenharmony_ci * (there is no clock selector) and clock type is internal. 1748c2ecf20Sopenharmony_ci * This is to deal with some Denon DJ controllers that always 1758c2ecf20Sopenharmony_ci * reports that clock is invalid. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci if (fmt->nr_rates == 1 && 1788c2ecf20Sopenharmony_ci (fmt->clock & 0xff) == cs_desc->bClockID && 1798c2ecf20Sopenharmony_ci (cs_desc->bmAttributes & 0x3) != 1808c2ecf20Sopenharmony_ci UAC_CLOCK_SOURCE_TYPE_EXT) 1818c2ecf20Sopenharmony_ci return true; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* 1858c2ecf20Sopenharmony_ci * MOTU MicroBook IIc 1868c2ecf20Sopenharmony_ci * Sample rate changes takes more than 2 seconds for this device. Clock 1878c2ecf20Sopenharmony_ci * validity request returns false during that period. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci if (chip->usb_id == USB_ID(0x07fd, 0x0004)) { 1908c2ecf20Sopenharmony_ci count = 0; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci while ((!ret) && (count < 50)) { 1938c2ecf20Sopenharmony_ci int err; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci msleep(100); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, 1988c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, 1998c2ecf20Sopenharmony_ci UAC2_CS_CONTROL_CLOCK_VALID << 8, 2008c2ecf20Sopenharmony_ci snd_usb_ctrl_intf(chip) | (source_id << 8), 2018c2ecf20Sopenharmony_ci &data, sizeof(data)); 2028c2ecf20Sopenharmony_ci if (err < 0) { 2038c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 2048c2ecf20Sopenharmony_ci "%s(): cannot get clock validity for id %d\n", 2058c2ecf20Sopenharmony_ci __func__, source_id); 2068c2ecf20Sopenharmony_ci return false; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ret = !!data; 2108c2ecf20Sopenharmony_ci count++; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic bool uac_clock_source_is_valid(struct snd_usb_audio *chip, 2188c2ecf20Sopenharmony_ci struct audioformat *fmt, 2198c2ecf20Sopenharmony_ci int source_id) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int err; 2228c2ecf20Sopenharmony_ci unsigned char data; 2238c2ecf20Sopenharmony_ci struct usb_device *dev = chip->dev; 2248c2ecf20Sopenharmony_ci u32 bmControls; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (fmt->protocol == UAC_VERSION_3) { 2278c2ecf20Sopenharmony_ci struct uac3_clock_source_descriptor *cs_desc = 2288c2ecf20Sopenharmony_ci snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!cs_desc) 2318c2ecf20Sopenharmony_ci return false; 2328c2ecf20Sopenharmony_ci bmControls = le32_to_cpu(cs_desc->bmControls); 2338c2ecf20Sopenharmony_ci } else { /* UAC_VERSION_1/2 */ 2348c2ecf20Sopenharmony_ci struct uac_clock_source_descriptor *cs_desc = 2358c2ecf20Sopenharmony_ci snd_usb_find_clock_source(chip->ctrl_intf, source_id); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!cs_desc) 2388c2ecf20Sopenharmony_ci return false; 2398c2ecf20Sopenharmony_ci bmControls = cs_desc->bmControls; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* If a clock source can't tell us whether it's valid, we assume it is */ 2438c2ecf20Sopenharmony_ci if (!uac_v2v3_control_is_readable(bmControls, 2448c2ecf20Sopenharmony_ci UAC2_CS_CONTROL_CLOCK_VALID)) 2458c2ecf20Sopenharmony_ci return true; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, 2488c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, 2498c2ecf20Sopenharmony_ci UAC2_CS_CONTROL_CLOCK_VALID << 8, 2508c2ecf20Sopenharmony_ci snd_usb_ctrl_intf(chip) | (source_id << 8), 2518c2ecf20Sopenharmony_ci &data, sizeof(data)); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (err < 0) { 2548c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 2558c2ecf20Sopenharmony_ci "%s(): cannot get clock validity for id %d\n", 2568c2ecf20Sopenharmony_ci __func__, source_id); 2578c2ecf20Sopenharmony_ci return false; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (data) 2618c2ecf20Sopenharmony_ci return true; 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci return uac_clock_source_is_valid_quirk(chip, fmt, source_id); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int __uac_clock_find_source(struct snd_usb_audio *chip, 2678c2ecf20Sopenharmony_ci struct audioformat *fmt, int entity_id, 2688c2ecf20Sopenharmony_ci unsigned long *visited, bool validate) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct uac_clock_source_descriptor *source; 2718c2ecf20Sopenharmony_ci struct uac_clock_selector_descriptor *selector; 2728c2ecf20Sopenharmony_ci struct uac_clock_multiplier_descriptor *multiplier; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci entity_id &= 0xff; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (test_and_set_bit(entity_id, visited)) { 2778c2ecf20Sopenharmony_ci usb_audio_warn(chip, 2788c2ecf20Sopenharmony_ci "%s(): recursive clock topology detected, id %d.\n", 2798c2ecf20Sopenharmony_ci __func__, entity_id); 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* first, see if the ID we're looking for is a clock source already */ 2848c2ecf20Sopenharmony_ci source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); 2858c2ecf20Sopenharmony_ci if (source) { 2868c2ecf20Sopenharmony_ci entity_id = source->bClockID; 2878c2ecf20Sopenharmony_ci if (validate && !uac_clock_source_is_valid(chip, fmt, 2888c2ecf20Sopenharmony_ci entity_id)) { 2898c2ecf20Sopenharmony_ci usb_audio_err(chip, 2908c2ecf20Sopenharmony_ci "clock source %d is not valid, cannot use\n", 2918c2ecf20Sopenharmony_ci entity_id); 2928c2ecf20Sopenharmony_ci return -ENXIO; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci return entity_id; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id); 2988c2ecf20Sopenharmony_ci if (selector) { 2998c2ecf20Sopenharmony_ci int ret, i, cur, err; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* the entity ID we are looking for is a selector. 3028c2ecf20Sopenharmony_ci * find out what it currently selects */ 3038c2ecf20Sopenharmony_ci ret = uac_clock_selector_get_val(chip, selector->bClockID); 3048c2ecf20Sopenharmony_ci if (ret < 0) 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* Selector values are one-based */ 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (ret > selector->bNrInPins || ret < 1) { 3108c2ecf20Sopenharmony_ci usb_audio_err(chip, 3118c2ecf20Sopenharmony_ci "%s(): selector reported illegal value, id %d, ret %d\n", 3128c2ecf20Sopenharmony_ci __func__, selector->bClockID, ret); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci cur = ret; 3188c2ecf20Sopenharmony_ci ret = __uac_clock_find_source(chip, fmt, 3198c2ecf20Sopenharmony_ci selector->baCSourceID[ret - 1], 3208c2ecf20Sopenharmony_ci visited, validate); 3218c2ecf20Sopenharmony_ci if (ret > 0) { 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * For Samsung USBC Headset (AKG), setting clock selector again 3248c2ecf20Sopenharmony_ci * will result in incorrect default clock setting problems 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci if (chip->usb_id == USB_ID(0x04e8, 0xa051)) 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci err = uac_clock_selector_set_val(chip, entity_id, cur); 3298c2ecf20Sopenharmony_ci if (err < 0) 3308c2ecf20Sopenharmony_ci return err; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!validate || ret > 0 || !chip->autoclock) 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* The current clock source is invalid, try others. */ 3378c2ecf20Sopenharmony_ci for (i = 1; i <= selector->bNrInPins; i++) { 3388c2ecf20Sopenharmony_ci if (i == cur) 3398c2ecf20Sopenharmony_ci continue; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = __uac_clock_find_source(chip, fmt, 3428c2ecf20Sopenharmony_ci selector->baCSourceID[i - 1], 3438c2ecf20Sopenharmony_ci visited, true); 3448c2ecf20Sopenharmony_ci if (ret < 0) 3458c2ecf20Sopenharmony_ci continue; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci err = uac_clock_selector_set_val(chip, entity_id, i); 3488c2ecf20Sopenharmony_ci if (err < 0) 3498c2ecf20Sopenharmony_ci continue; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci usb_audio_info(chip, 3528c2ecf20Sopenharmony_ci "found and selected valid clock source %d\n", 3538c2ecf20Sopenharmony_ci ret); 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return -ENXIO; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* FIXME: multipliers only act as pass-thru element for now */ 3618c2ecf20Sopenharmony_ci multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id); 3628c2ecf20Sopenharmony_ci if (multiplier) 3638c2ecf20Sopenharmony_ci return __uac_clock_find_source(chip, fmt, 3648c2ecf20Sopenharmony_ci multiplier->bCSourceID, 3658c2ecf20Sopenharmony_ci visited, validate); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return -EINVAL; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int __uac3_clock_find_source(struct snd_usb_audio *chip, 3718c2ecf20Sopenharmony_ci struct audioformat *fmt, int entity_id, 3728c2ecf20Sopenharmony_ci unsigned long *visited, bool validate) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct uac3_clock_source_descriptor *source; 3758c2ecf20Sopenharmony_ci struct uac3_clock_selector_descriptor *selector; 3768c2ecf20Sopenharmony_ci struct uac3_clock_multiplier_descriptor *multiplier; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci entity_id &= 0xff; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (test_and_set_bit(entity_id, visited)) { 3818c2ecf20Sopenharmony_ci usb_audio_warn(chip, 3828c2ecf20Sopenharmony_ci "%s(): recursive clock topology detected, id %d.\n", 3838c2ecf20Sopenharmony_ci __func__, entity_id); 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* first, see if the ID we're looking for is a clock source already */ 3888c2ecf20Sopenharmony_ci source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id); 3898c2ecf20Sopenharmony_ci if (source) { 3908c2ecf20Sopenharmony_ci entity_id = source->bClockID; 3918c2ecf20Sopenharmony_ci if (validate && !uac_clock_source_is_valid(chip, fmt, 3928c2ecf20Sopenharmony_ci entity_id)) { 3938c2ecf20Sopenharmony_ci usb_audio_err(chip, 3948c2ecf20Sopenharmony_ci "clock source %d is not valid, cannot use\n", 3958c2ecf20Sopenharmony_ci entity_id); 3968c2ecf20Sopenharmony_ci return -ENXIO; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci return entity_id; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id); 4028c2ecf20Sopenharmony_ci if (selector) { 4038c2ecf20Sopenharmony_ci int ret, i, cur, err; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* the entity ID we are looking for is a selector. 4068c2ecf20Sopenharmony_ci * find out what it currently selects */ 4078c2ecf20Sopenharmony_ci ret = uac_clock_selector_get_val(chip, selector->bClockID); 4088c2ecf20Sopenharmony_ci if (ret < 0) 4098c2ecf20Sopenharmony_ci return ret; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Selector values are one-based */ 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (ret > selector->bNrInPins || ret < 1) { 4148c2ecf20Sopenharmony_ci usb_audio_err(chip, 4158c2ecf20Sopenharmony_ci "%s(): selector reported illegal value, id %d, ret %d\n", 4168c2ecf20Sopenharmony_ci __func__, selector->bClockID, ret); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return -EINVAL; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci cur = ret; 4228c2ecf20Sopenharmony_ci ret = __uac3_clock_find_source(chip, fmt, 4238c2ecf20Sopenharmony_ci selector->baCSourceID[ret - 1], 4248c2ecf20Sopenharmony_ci visited, validate); 4258c2ecf20Sopenharmony_ci if (ret > 0) { 4268c2ecf20Sopenharmony_ci err = uac_clock_selector_set_val(chip, entity_id, cur); 4278c2ecf20Sopenharmony_ci if (err < 0) 4288c2ecf20Sopenharmony_ci return err; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!validate || ret > 0 || !chip->autoclock) 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* The current clock source is invalid, try others. */ 4358c2ecf20Sopenharmony_ci for (i = 1; i <= selector->bNrInPins; i++) { 4368c2ecf20Sopenharmony_ci int err; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (i == cur) 4398c2ecf20Sopenharmony_ci continue; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ret = __uac3_clock_find_source(chip, fmt, 4428c2ecf20Sopenharmony_ci selector->baCSourceID[i - 1], 4438c2ecf20Sopenharmony_ci visited, true); 4448c2ecf20Sopenharmony_ci if (ret < 0) 4458c2ecf20Sopenharmony_ci continue; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci err = uac_clock_selector_set_val(chip, entity_id, i); 4488c2ecf20Sopenharmony_ci if (err < 0) 4498c2ecf20Sopenharmony_ci continue; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci usb_audio_info(chip, 4528c2ecf20Sopenharmony_ci "found and selected valid clock source %d\n", 4538c2ecf20Sopenharmony_ci ret); 4548c2ecf20Sopenharmony_ci return ret; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return -ENXIO; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* FIXME: multipliers only act as pass-thru element for now */ 4618c2ecf20Sopenharmony_ci multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf, 4628c2ecf20Sopenharmony_ci entity_id); 4638c2ecf20Sopenharmony_ci if (multiplier) 4648c2ecf20Sopenharmony_ci return __uac3_clock_find_source(chip, fmt, 4658c2ecf20Sopenharmony_ci multiplier->bCSourceID, 4668c2ecf20Sopenharmony_ci visited, validate); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return -EINVAL; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/* 4728c2ecf20Sopenharmony_ci * For all kinds of sample rate settings and other device queries, 4738c2ecf20Sopenharmony_ci * the clock source (end-leaf) must be used. However, clock selectors, 4748c2ecf20Sopenharmony_ci * clock multipliers and sample rate converters may be specified as 4758c2ecf20Sopenharmony_ci * clock source input to terminal. This functions walks the clock path 4768c2ecf20Sopenharmony_ci * to its end and tries to find the source. 4778c2ecf20Sopenharmony_ci * 4788c2ecf20Sopenharmony_ci * The 'visited' bitfield is used internally to detect recursive loops. 4798c2ecf20Sopenharmony_ci * 4808c2ecf20Sopenharmony_ci * Returns the clock source UnitID (>=0) on success, or an error. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ciint snd_usb_clock_find_source(struct snd_usb_audio *chip, 4838c2ecf20Sopenharmony_ci struct audioformat *fmt, bool validate) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci DECLARE_BITMAP(visited, 256); 4868c2ecf20Sopenharmony_ci memset(visited, 0, sizeof(visited)); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci switch (fmt->protocol) { 4898c2ecf20Sopenharmony_ci case UAC_VERSION_2: 4908c2ecf20Sopenharmony_ci return __uac_clock_find_source(chip, fmt, fmt->clock, visited, 4918c2ecf20Sopenharmony_ci validate); 4928c2ecf20Sopenharmony_ci case UAC_VERSION_3: 4938c2ecf20Sopenharmony_ci return __uac3_clock_find_source(chip, fmt, fmt->clock, visited, 4948c2ecf20Sopenharmony_ci validate); 4958c2ecf20Sopenharmony_ci default: 4968c2ecf20Sopenharmony_ci return -EINVAL; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, 5018c2ecf20Sopenharmony_ci struct usb_host_interface *alts, 5028c2ecf20Sopenharmony_ci struct audioformat *fmt, int rate) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct usb_device *dev = chip->dev; 5058c2ecf20Sopenharmony_ci unsigned int ep; 5068c2ecf20Sopenharmony_ci unsigned char data[3]; 5078c2ecf20Sopenharmony_ci int err, crate; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (get_iface_desc(alts)->bNumEndpoints < 1) 5108c2ecf20Sopenharmony_ci return -EINVAL; 5118c2ecf20Sopenharmony_ci ep = get_endpoint(alts, 0)->bEndpointAddress; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* if endpoint doesn't have sampling rate control, bail out */ 5148c2ecf20Sopenharmony_ci if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci data[0] = rate; 5188c2ecf20Sopenharmony_ci data[1] = rate >> 8; 5198c2ecf20Sopenharmony_ci data[2] = rate >> 16; 5208c2ecf20Sopenharmony_ci err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, 5218c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, 5228c2ecf20Sopenharmony_ci UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, 5238c2ecf20Sopenharmony_ci data, sizeof(data)); 5248c2ecf20Sopenharmony_ci if (err < 0) { 5258c2ecf20Sopenharmony_ci dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n", 5268c2ecf20Sopenharmony_ci iface, fmt->altsetting, rate, ep); 5278c2ecf20Sopenharmony_ci return err; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Don't check the sample rate for devices which we know don't 5318c2ecf20Sopenharmony_ci * support reading */ 5328c2ecf20Sopenharmony_ci if (snd_usb_get_sample_rate_quirk(chip)) 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci /* the firmware is likely buggy, don't repeat to fail too many times */ 5358c2ecf20Sopenharmony_ci if (chip->sample_rate_read_error > 2) 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, 5398c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, 5408c2ecf20Sopenharmony_ci UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, 5418c2ecf20Sopenharmony_ci data, sizeof(data)); 5428c2ecf20Sopenharmony_ci if (err < 0) { 5438c2ecf20Sopenharmony_ci dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n", 5448c2ecf20Sopenharmony_ci iface, fmt->altsetting, ep); 5458c2ecf20Sopenharmony_ci chip->sample_rate_read_error++; 5468c2ecf20Sopenharmony_ci return 0; /* some devices don't support reading */ 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci crate = data[0] | (data[1] << 8) | (data[2] << 16); 5508c2ecf20Sopenharmony_ci if (!crate) { 5518c2ecf20Sopenharmony_ci dev_info(&dev->dev, "failed to read current rate; disabling the check\n"); 5528c2ecf20Sopenharmony_ci chip->sample_rate_read_error = 3; /* three strikes, see above */ 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (crate != rate) { 5578c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "current rate %d is different from the runtime rate %d\n", crate, rate); 5588c2ecf20Sopenharmony_ci // runtime->rate = crate; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, 5658c2ecf20Sopenharmony_ci int altsetting, int clock) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct usb_device *dev = chip->dev; 5688c2ecf20Sopenharmony_ci __le32 data; 5698c2ecf20Sopenharmony_ci int err; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, 5728c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, 5738c2ecf20Sopenharmony_ci UAC2_CS_CONTROL_SAM_FREQ << 8, 5748c2ecf20Sopenharmony_ci snd_usb_ctrl_intf(chip) | (clock << 8), 5758c2ecf20Sopenharmony_ci &data, sizeof(data)); 5768c2ecf20Sopenharmony_ci if (err < 0) { 5778c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n", 5788c2ecf20Sopenharmony_ci iface, altsetting, err); 5798c2ecf20Sopenharmony_ci return 0; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return le32_to_cpu(data); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, 5868c2ecf20Sopenharmony_ci struct usb_host_interface *alts, 5878c2ecf20Sopenharmony_ci struct audioformat *fmt, int rate) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct usb_device *dev = chip->dev; 5908c2ecf20Sopenharmony_ci __le32 data; 5918c2ecf20Sopenharmony_ci int err, cur_rate, prev_rate; 5928c2ecf20Sopenharmony_ci int clock; 5938c2ecf20Sopenharmony_ci bool writeable; 5948c2ecf20Sopenharmony_ci u32 bmControls; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* First, try to find a valid clock. This may trigger 5978c2ecf20Sopenharmony_ci * automatic clock selection if the current clock is not 5988c2ecf20Sopenharmony_ci * valid. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci clock = snd_usb_clock_find_source(chip, fmt, true); 6018c2ecf20Sopenharmony_ci if (clock < 0) { 6028c2ecf20Sopenharmony_ci /* We did not find a valid clock, but that might be 6038c2ecf20Sopenharmony_ci * because the current sample rate does not match an 6048c2ecf20Sopenharmony_ci * external clock source. Try again without validation 6058c2ecf20Sopenharmony_ci * and we will do another validation after setting the 6068c2ecf20Sopenharmony_ci * rate. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci clock = snd_usb_clock_find_source(chip, fmt, false); 6098c2ecf20Sopenharmony_ci if (clock < 0) 6108c2ecf20Sopenharmony_ci return clock; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci prev_rate = get_sample_rate_v2v3(chip, iface, fmt->altsetting, clock); 6148c2ecf20Sopenharmony_ci if (prev_rate == rate) 6158c2ecf20Sopenharmony_ci goto validation; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (fmt->protocol == UAC_VERSION_3) { 6188c2ecf20Sopenharmony_ci struct uac3_clock_source_descriptor *cs_desc; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock); 6218c2ecf20Sopenharmony_ci bmControls = le32_to_cpu(cs_desc->bmControls); 6228c2ecf20Sopenharmony_ci } else { 6238c2ecf20Sopenharmony_ci struct uac_clock_source_descriptor *cs_desc; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); 6268c2ecf20Sopenharmony_ci bmControls = cs_desc->bmControls; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci writeable = uac_v2v3_control_is_writeable(bmControls, 6308c2ecf20Sopenharmony_ci UAC2_CS_CONTROL_SAM_FREQ); 6318c2ecf20Sopenharmony_ci if (writeable) { 6328c2ecf20Sopenharmony_ci data = cpu_to_le32(rate); 6338c2ecf20Sopenharmony_ci err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, 6348c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, 6358c2ecf20Sopenharmony_ci UAC2_CS_CONTROL_SAM_FREQ << 8, 6368c2ecf20Sopenharmony_ci snd_usb_ctrl_intf(chip) | (clock << 8), 6378c2ecf20Sopenharmony_ci &data, sizeof(data)); 6388c2ecf20Sopenharmony_ci if (err < 0) { 6398c2ecf20Sopenharmony_ci usb_audio_err(chip, 6408c2ecf20Sopenharmony_ci "%d:%d: cannot set freq %d (v2/v3): err %d\n", 6418c2ecf20Sopenharmony_ci iface, fmt->altsetting, rate, err); 6428c2ecf20Sopenharmony_ci return err; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci cur_rate = get_sample_rate_v2v3(chip, iface, 6468c2ecf20Sopenharmony_ci fmt->altsetting, clock); 6478c2ecf20Sopenharmony_ci } else { 6488c2ecf20Sopenharmony_ci cur_rate = prev_rate; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (cur_rate != rate) { 6528c2ecf20Sopenharmony_ci if (!writeable) { 6538c2ecf20Sopenharmony_ci usb_audio_warn(chip, 6548c2ecf20Sopenharmony_ci "%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n", 6558c2ecf20Sopenharmony_ci iface, fmt->altsetting, rate, cur_rate); 6568c2ecf20Sopenharmony_ci return -ENXIO; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci usb_audio_dbg(chip, 6598c2ecf20Sopenharmony_ci "current rate %d is different from the runtime rate %d\n", 6608c2ecf20Sopenharmony_ci cur_rate, rate); 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* Some devices doesn't respond to sample rate changes while the 6648c2ecf20Sopenharmony_ci * interface is active. */ 6658c2ecf20Sopenharmony_ci if (rate != prev_rate) { 6668c2ecf20Sopenharmony_ci usb_set_interface(dev, iface, 0); 6678c2ecf20Sopenharmony_ci snd_usb_set_interface_quirk(dev); 6688c2ecf20Sopenharmony_ci usb_set_interface(dev, iface, fmt->altsetting); 6698c2ecf20Sopenharmony_ci snd_usb_set_interface_quirk(dev); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_civalidation: 6738c2ecf20Sopenharmony_ci /* validate clock after rate change */ 6748c2ecf20Sopenharmony_ci if (!uac_clock_source_is_valid(chip, fmt, clock)) 6758c2ecf20Sopenharmony_ci return -ENXIO; 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ciint snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, 6808c2ecf20Sopenharmony_ci struct usb_host_interface *alts, 6818c2ecf20Sopenharmony_ci struct audioformat *fmt, int rate) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci switch (fmt->protocol) { 6848c2ecf20Sopenharmony_ci case UAC_VERSION_1: 6858c2ecf20Sopenharmony_ci default: 6868c2ecf20Sopenharmony_ci return set_sample_rate_v1(chip, iface, alts, fmt, rate); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci case UAC_VERSION_3: 6898c2ecf20Sopenharmony_ci if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { 6908c2ecf20Sopenharmony_ci if (rate != UAC3_BADD_SAMPLING_RATE) 6918c2ecf20Sopenharmony_ci return -ENXIO; 6928c2ecf20Sopenharmony_ci else 6938c2ecf20Sopenharmony_ci return 0; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci fallthrough; 6968c2ecf20Sopenharmony_ci case UAC_VERSION_2: 6978c2ecf20Sopenharmony_ci return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 701