* refs/heads/tmp-9fbf3d7 Linux 4.4.103 Revert "sctp: do not peel off an assoc from one netns to another one" xen: xenbus driver must not accept invalid transaction ids s390/kbuild: enable modversions for symbols exported from asm ASoC: wm_adsp: Don't overrun firmware file buffer when reading region data btrfs: return the actual error value from from btrfs_uuid_tree_iterate ASoC: rsnd: don't double free kctrl netfilter: nf_tables: fix oob access netfilter: nft_queue: use raw_smp_processor_id() spi: SPI_FSL_DSPI should depend on HAS_DMA staging: iio: cdc: fix improper return value iio: light: fix improper return value mac80211: Suppress NEW_PEER_CANDIDATE event if no room mac80211: Remove invalid flag operations in mesh TSF synchronization drm: Apply range restriction after color adjustment when allocation ALSA: hda - Apply ALC269_FIXUP_NO_SHUTUP on HDA_FIXUP_ACT_PROBE ath10k: set CTS protection VDEV param only if VDEV is up ath10k: fix potential memory leak in ath10k_wmi_tlv_op_pull_fw_stats() ath10k: ignore configuring the incorrect board_id ath10k: fix incorrect txpower set by P2P_DEVICE interface drm/armada: Fix compile fail net: 3com: typhoon: typhoon_init_one: fix incorrect return values net: 3com: typhoon: typhoon_init_one: make return values more specific net: Allow IP_MULTICAST_IF to set index to L3 slave dmaengine: zx: set DMA_CYCLIC cap_mask bit PCI: Apply _HPX settings only to relevant devices RDS: RDMA: return appropriate error on rdma map failures e1000e: Separate signaling for link check/link up e1000e: Fix return value test e1000e: Fix error path in link detection PM / OPP: Add missing of_node_put(np) net/9p: Switch to wait_event_killable() fscrypt: lock mutex before checking for bounce page pool sched/rt: Simplify the IPI based RT balancing logic media: v4l2-ctrl: Fix flags field on Control events cx231xx-cards: fix NULL-deref on missing association descriptor media: rc: check for integer overflow media: Don't do DMA on stack for firmware upload in the AS102 driver powerpc/signal: Properly handle return value from uprobe_deny_signal() parisc: Fix validity check of pointer size argument in new CAS implementation ixgbe: Fix skb list corruption on Power systems fm10k: Use smp_rmb rather than read_barrier_depends i40evf: Use smp_rmb rather than read_barrier_depends ixgbevf: Use smp_rmb rather than read_barrier_depends igbvf: Use smp_rmb rather than read_barrier_depends igb: Use smp_rmb rather than read_barrier_depends i40e: Use smp_rmb rather than read_barrier_depends NFC: fix device-allocation error return IB/srp: Avoid that a cable pull can trigger a kernel crash IB/srpt: Do not accept invalid initiator port names libnvdimm, namespace: make 'resource' attribute only readable by root libnvdimm, namespace: fix label initialization to use valid seq numbers clk: ti: dra7-atl-clock: fix child-node lookups clk: ti: dra7-atl-clock: Fix of_node reference counting SUNRPC: Fix tracepoint storage issues with svc_recv and svc_rqst_status KVM: SVM: obey guest PAT KVM: nVMX: set IDTR and GDTR limits when loading L1 host state target: Fix QUEUE_FULL + SCSI task attribute handling iscsi-target: Fix non-immediate TMR reference leak fs/9p: Compare qid.path in v9fs_test_inode fix a page leak in vhost_scsi_iov_to_sgl() error recovery ALSA: hda/realtek - Fix ALC700 family no sound issue ALSA: timer: Remove kernel warning at compat ioctl error paths ALSA: usb-audio: Add sanity checks in v2 clock parsers ALSA: usb-audio: Fix potential out-of-bound access at parsing SU ALSA: usb-audio: Add sanity checks to FE parser ALSA: pcm: update tstamp only if audio_tstamp changed ext4: fix interaction between i_size, fallocate, and delalloc after a crash ata: fixes kernel crash while tracing ata_eh_link_autopsy event rtlwifi: fix uninitialized rtlhal->last_suspend_sec time rtlwifi: rtl8192ee: Fix memory leak when loading firmware nfsd: deal with revoked delegations appropriately nfs: Fix ugly referral attributes NFS: Fix typo in nomigration mount option isofs: fix timestamps beyond 2027 bcache: check ca->alloc_thread initialized before wake up it eCryptfs: use after free in ecryptfs_release_messaging() nilfs2: fix race condition that causes file system corruption autofs: don't fail mount for transient error MIPS: BCM47XX: Fix LED inversion for WRT54GSv1 MIPS: Fix an n32 core file generation regset support regression dm: fix race between dm_get_from_kobject() and __dm_destroy() dm bufio: fix integer overflow when limiting maximum cache size ALSA: hda: Add Raven PCI ID MIPS: ralink: Fix typo in mt7628 pinmux function MIPS: ralink: Fix MT7628 pinmux ARM: 8721/1: mm: dump: check hardware RO bit for LPAE ARM: 8722/1: mm: make STRICT_KERNEL_RWX effective for LPAE x86/decoder: Add new TEST instruction pattern lib/mpi: call cond_resched() from mpi_powm() loop sched: Make resched_cpu() unconditional vsock: use new wait API for vsock_stream_sendmsg() AF_VSOCK: Shrink the area influenced by prepare_to_wait ipv6: only call ip6_route_dev_notify() once for NETDEV_UNREGISTER s390/disassembler: increase show_code buffer size s390/disassembler: add missing end marker for e7 table s390/runtime instrumention: fix possible memory corruption s390: fix transactional execution control register handling BACKPORT: time: Clean up CLOCK_MONOTONIC_RAW time handling BACKPORT: time: Fix CLOCK_MONOTONIC_RAW sub-nanosecond accounting UPSTREAM: arm64: vdso: fix clock_getres for 4GiB-aligned res f2fs: updates on 4.15-rc1 UPSTREAM: android: binder: fix type mismatch warning Linux 4.4.102 mm, hwpoison: fixup "mm: check the return value of lookup_page_ext for all call sites" Conflicts: fs/ext4/crypto_key.c mm/debug-pagealloc.c Change-Id: Ibe35d78bd0397f3ff2049e0a1dda20fcb06f2f75 Signed-off-by: Srinivasarao P <spathi@codeaurora.org>
436 lines
12 KiB
C
436 lines
12 KiB
C
/*
|
|
* Clock domain and sample rate management functions
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/init.h>
|
|
#include <linux/string.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/audio.h>
|
|
#include <linux/usb/audio-v2.h>
|
|
|
|
#include <sound/core.h>
|
|
#include <sound/info.h>
|
|
#include <sound/pcm.h>
|
|
|
|
#include "usbaudio.h"
|
|
#include "card.h"
|
|
#include "helper.h"
|
|
#include "clock.h"
|
|
#include "quirks.h"
|
|
|
|
static struct uac_clock_source_descriptor *
|
|
snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
|
|
int clock_id)
|
|
{
|
|
struct uac_clock_source_descriptor *cs = NULL;
|
|
|
|
while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
|
|
ctrl_iface->extralen,
|
|
cs, UAC2_CLOCK_SOURCE))) {
|
|
if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id)
|
|
return cs;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct uac_clock_selector_descriptor *
|
|
snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
|
|
int clock_id)
|
|
{
|
|
struct uac_clock_selector_descriptor *cs = NULL;
|
|
|
|
while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
|
|
ctrl_iface->extralen,
|
|
cs, UAC2_CLOCK_SELECTOR))) {
|
|
if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id) {
|
|
if (cs->bLength < 5 + cs->bNrInPins)
|
|
return NULL;
|
|
return cs;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct uac_clock_multiplier_descriptor *
|
|
snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
|
|
int clock_id)
|
|
{
|
|
struct uac_clock_multiplier_descriptor *cs = NULL;
|
|
|
|
while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
|
|
ctrl_iface->extralen,
|
|
cs, UAC2_CLOCK_MULTIPLIER))) {
|
|
if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id)
|
|
return cs;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
|
|
{
|
|
unsigned char buf;
|
|
int ret;
|
|
|
|
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
|
|
UAC2_CS_CUR,
|
|
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
|
UAC2_CX_CLOCK_SELECTOR << 8,
|
|
snd_usb_ctrl_intf(chip) | (selector_id << 8),
|
|
&buf, sizeof(buf));
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id,
|
|
unsigned char pin)
|
|
{
|
|
int ret;
|
|
|
|
ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
|
|
UAC2_CS_CUR,
|
|
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
|
|
UAC2_CX_CLOCK_SELECTOR << 8,
|
|
snd_usb_ctrl_intf(chip) | (selector_id << 8),
|
|
&pin, sizeof(pin));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ret != sizeof(pin)) {
|
|
usb_audio_err(chip,
|
|
"setting selector (id %d) unexpected length %d\n",
|
|
selector_id, ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = uac_clock_selector_get_val(chip, selector_id);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ret != pin) {
|
|
usb_audio_err(chip,
|
|
"setting selector (id %d) to %x failed (current: %d)\n",
|
|
selector_id, pin, ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
|
|
{
|
|
int err;
|
|
unsigned char data;
|
|
struct usb_device *dev = chip->dev;
|
|
struct uac_clock_source_descriptor *cs_desc =
|
|
snd_usb_find_clock_source(chip->ctrl_intf, source_id);
|
|
|
|
if (!cs_desc)
|
|
return 0;
|
|
|
|
/* If a clock source can't tell us whether it's valid, we assume it is */
|
|
if (!uac2_control_is_readable(cs_desc->bmControls,
|
|
UAC2_CS_CONTROL_CLOCK_VALID - 1))
|
|
return 1;
|
|
|
|
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
|
UAC2_CS_CONTROL_CLOCK_VALID << 8,
|
|
snd_usb_ctrl_intf(chip) | (source_id << 8),
|
|
&data, sizeof(data));
|
|
|
|
if (err < 0) {
|
|
dev_warn(&dev->dev,
|
|
"%s(): cannot get clock validity for id %d\n",
|
|
__func__, source_id);
|
|
return 0;
|
|
}
|
|
|
|
return !!data;
|
|
}
|
|
|
|
static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
|
int entity_id, unsigned long *visited,
|
|
bool validate)
|
|
{
|
|
struct uac_clock_source_descriptor *source;
|
|
struct uac_clock_selector_descriptor *selector;
|
|
struct uac_clock_multiplier_descriptor *multiplier;
|
|
|
|
entity_id &= 0xff;
|
|
|
|
if (test_and_set_bit(entity_id, visited)) {
|
|
usb_audio_warn(chip,
|
|
"%s(): recursive clock topology detected, id %d.\n",
|
|
__func__, entity_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* first, see if the ID we're looking for is a clock source already */
|
|
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
|
|
if (source) {
|
|
entity_id = source->bClockID;
|
|
if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
|
|
usb_audio_err(chip,
|
|
"clock source %d is not valid, cannot use\n",
|
|
entity_id);
|
|
return -ENXIO;
|
|
}
|
|
return entity_id;
|
|
}
|
|
|
|
selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
|
|
if (selector) {
|
|
int ret, i, cur;
|
|
|
|
/* the entity ID we are looking for is a selector.
|
|
* find out what it currently selects */
|
|
ret = uac_clock_selector_get_val(chip, selector->bClockID);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Selector values are one-based */
|
|
|
|
if (ret > selector->bNrInPins || ret < 1) {
|
|
usb_audio_err(chip,
|
|
"%s(): selector reported illegal value, id %d, ret %d\n",
|
|
__func__, selector->bClockID, ret);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
cur = ret;
|
|
ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
|
|
visited, validate);
|
|
if (!validate || ret > 0 || !chip->autoclock)
|
|
return ret;
|
|
|
|
/* The current clock source is invalid, try others. */
|
|
for (i = 1; i <= selector->bNrInPins; i++) {
|
|
int err;
|
|
|
|
if (i == cur)
|
|
continue;
|
|
|
|
ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
|
|
visited, true);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
err = uac_clock_selector_set_val(chip, entity_id, i);
|
|
if (err < 0)
|
|
continue;
|
|
|
|
usb_audio_info(chip,
|
|
"found and selected valid clock source %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* FIXME: multipliers only act as pass-thru element for now */
|
|
multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
|
|
if (multiplier)
|
|
return __uac_clock_find_source(chip, multiplier->bCSourceID,
|
|
visited, validate);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* For all kinds of sample rate settings and other device queries,
|
|
* the clock source (end-leaf) must be used. However, clock selectors,
|
|
* clock multipliers and sample rate converters may be specified as
|
|
* clock source input to terminal. This functions walks the clock path
|
|
* to its end and tries to find the source.
|
|
*
|
|
* The 'visited' bitfield is used internally to detect recursive loops.
|
|
*
|
|
* Returns the clock source UnitID (>=0) on success, or an error.
|
|
*/
|
|
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
|
|
bool validate)
|
|
{
|
|
DECLARE_BITMAP(visited, 256);
|
|
memset(visited, 0, sizeof(visited));
|
|
return __uac_clock_find_source(chip, entity_id, visited, validate);
|
|
}
|
|
|
|
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
|
struct usb_host_interface *alts,
|
|
struct audioformat *fmt, int rate)
|
|
{
|
|
struct usb_device *dev = chip->dev;
|
|
unsigned int ep;
|
|
unsigned char data[3];
|
|
int err, crate;
|
|
|
|
if (get_iface_desc(alts)->bNumEndpoints < 1)
|
|
return -EINVAL;
|
|
ep = get_endpoint(alts, 0)->bEndpointAddress;
|
|
|
|
/* if endpoint doesn't have sampling rate control, bail out */
|
|
if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
|
|
return 0;
|
|
|
|
data[0] = rate;
|
|
data[1] = rate >> 8;
|
|
data[2] = rate >> 16;
|
|
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
|
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
|
|
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
|
data, sizeof(data))) < 0) {
|
|
dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
|
|
iface, fmt->altsetting, rate, ep);
|
|
return err;
|
|
}
|
|
|
|
/* Don't check the sample rate for devices which we know don't
|
|
* support reading */
|
|
if (snd_usb_get_sample_rate_quirk(chip))
|
|
return 0;
|
|
|
|
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
|
|
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
|
|
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
|
data, sizeof(data))) < 0) {
|
|
dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
|
|
iface, fmt->altsetting, ep);
|
|
return 0; /* some devices don't support reading */
|
|
}
|
|
|
|
crate = data[0] | (data[1] << 8) | (data[2] << 16);
|
|
if (crate != rate) {
|
|
dev_warn(&dev->dev, "current rate %d is different from the runtime rate %d\n", crate, rate);
|
|
// runtime->rate = crate;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
|
int altsetting, int clock)
|
|
{
|
|
struct usb_device *dev = chip->dev;
|
|
__le32 data;
|
|
int err;
|
|
|
|
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
|
UAC2_CS_CONTROL_SAM_FREQ << 8,
|
|
snd_usb_ctrl_intf(chip) | (clock << 8),
|
|
&data, sizeof(data));
|
|
if (err < 0) {
|
|
dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n",
|
|
iface, altsetting, err);
|
|
return 0;
|
|
}
|
|
|
|
return le32_to_cpu(data);
|
|
}
|
|
|
|
static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
|
struct usb_host_interface *alts,
|
|
struct audioformat *fmt, int rate)
|
|
{
|
|
struct usb_device *dev = chip->dev;
|
|
__le32 data;
|
|
int err, cur_rate, prev_rate;
|
|
int clock;
|
|
bool writeable;
|
|
struct uac_clock_source_descriptor *cs_desc;
|
|
|
|
clock = snd_usb_clock_find_source(chip, fmt->clock, true);
|
|
if (clock < 0)
|
|
return clock;
|
|
|
|
prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
|
|
if (prev_rate == rate)
|
|
return 0;
|
|
|
|
cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
|
|
writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
|
|
if (writeable) {
|
|
data = cpu_to_le32(rate);
|
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
|
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
|
UAC2_CS_CONTROL_SAM_FREQ << 8,
|
|
snd_usb_ctrl_intf(chip) | (clock << 8),
|
|
&data, sizeof(data));
|
|
if (err < 0) {
|
|
usb_audio_err(chip,
|
|
"%d:%d: cannot set freq %d (v2): err %d\n",
|
|
iface, fmt->altsetting, rate, err);
|
|
return err;
|
|
}
|
|
|
|
cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
|
|
} else {
|
|
cur_rate = prev_rate;
|
|
}
|
|
|
|
if (cur_rate != rate) {
|
|
if (!writeable) {
|
|
usb_audio_warn(chip,
|
|
"%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n",
|
|
iface, fmt->altsetting, rate, cur_rate);
|
|
return -ENXIO;
|
|
}
|
|
usb_audio_dbg(chip,
|
|
"current rate %d is different from the runtime rate %d\n",
|
|
cur_rate, rate);
|
|
}
|
|
|
|
/* Some devices doesn't respond to sample rate changes while the
|
|
* interface is active. */
|
|
if (rate != prev_rate) {
|
|
usb_set_interface(dev, iface, 0);
|
|
snd_usb_set_interface_quirk(dev);
|
|
usb_set_interface(dev, iface, fmt->altsetting);
|
|
snd_usb_set_interface_quirk(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
|
struct usb_host_interface *alts,
|
|
struct audioformat *fmt, int rate)
|
|
{
|
|
switch (fmt->protocol) {
|
|
case UAC_VERSION_1:
|
|
default:
|
|
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
|
|
|
|
case UAC_VERSION_2:
|
|
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
|
|
|
|
/* Clock rate is fixed at 48 kHz for BADD devices */
|
|
case UAC_VERSION_3:
|
|
return 0;
|
|
}
|
|
}
|
|
|