Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: ALSA: hda-intel - fix wallclk variable update and condition ALSA: asihpi - Fix uninitialized variable ALSA: hda: Use LPIB for ASUS M2V usb/gadget: Replace the old USB audio FU definitions in f_audio.c ASoC: MX31ads sound support should depend on MACH_MX31ADS_WM1133_EV1 ASoC: Add missing Kconfig entry for Phytec boards ALSA: usb-audio: export UAC2 clock selectors as mixer controls ALSA: usb-audio: clean up find_audio_control_unit() ALSA: usb-audio: add UAC2 sepecific Feature Unit controls ALSA: usb-audio: unify constants from specification ALSA: usb-audio: parse clock topology of UAC2 devices ALSA: usb-audio: fix selector unit string index accessor include/linux/usb/audio-v2.h: add more UAC2 details ALSA: usb-audio: support partially write-protected UAC2 controls ALSA: usb-audio: UAC2: clean up parsing of bmaControls ALSA: hda: Use LPIB for another mainboard ALSA: hda: Use mb31 quirk for an iMac model ALSA: hda: Use LPIB for an ASUS device
This commit is contained in:
commit
bc23416cd4
|
@ -101,7 +101,7 @@ static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
|
|||
static struct usb_audio_control mute_control = {
|
||||
.list = LIST_HEAD_INIT(mute_control.list),
|
||||
.name = "Mute Control",
|
||||
.type = UAC_MUTE_CONTROL,
|
||||
.type = UAC_FU_MUTE,
|
||||
/* Todo: add real Mute control code */
|
||||
.set = generic_set_cmd,
|
||||
.get = generic_get_cmd,
|
||||
|
@ -110,7 +110,7 @@ static struct usb_audio_control mute_control = {
|
|||
static struct usb_audio_control volume_control = {
|
||||
.list = LIST_HEAD_INIT(volume_control.list),
|
||||
.name = "Volume Control",
|
||||
.type = UAC_VOLUME_CONTROL,
|
||||
.type = UAC_FU_VOLUME,
|
||||
/* Todo: add real Volume control code */
|
||||
.set = generic_set_cmd,
|
||||
.get = generic_get_cmd,
|
||||
|
|
|
@ -18,6 +18,16 @@
|
|||
/* v1.0 and v2.0 of this standard have many things in common. For the rest
|
||||
* of the definitions, please refer to audio.h */
|
||||
|
||||
static inline bool uac2_control_is_readable(u32 bmControls, u8 control)
|
||||
{
|
||||
return (bmControls >> (control * 2)) & 0x1;
|
||||
}
|
||||
|
||||
static inline bool uac2_control_is_writeable(u32 bmControls, u8 control)
|
||||
{
|
||||
return (bmControls >> (control * 2)) & 0x2;
|
||||
}
|
||||
|
||||
/* 4.7.2.1 Clock Source Descriptor */
|
||||
|
||||
struct uac_clock_source_descriptor {
|
||||
|
@ -31,6 +41,13 @@ struct uac_clock_source_descriptor {
|
|||
__u8 iClockSource;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* bmAttribute fields */
|
||||
#define UAC_CLOCK_SOURCE_TYPE_EXT 0x0
|
||||
#define UAC_CLOCK_SOURCE_TYPE_INT_FIXED 0x1
|
||||
#define UAC_CLOCK_SOURCE_TYPE_INT_VAR 0x2
|
||||
#define UAC_CLOCK_SOURCE_TYPE_INT_PROG 0x3
|
||||
#define UAC_CLOCK_SOURCE_SYNCED_TO_SOF (1 << 2)
|
||||
|
||||
/* 4.7.2.2 Clock Source Descriptor */
|
||||
|
||||
struct uac_clock_selector_descriptor {
|
||||
|
@ -39,8 +56,20 @@ struct uac_clock_selector_descriptor {
|
|||
__u8 bDescriptorSubtype;
|
||||
__u8 bClockID;
|
||||
__u8 bNrInPins;
|
||||
__u8 bmControls;
|
||||
__u8 baCSourceID[];
|
||||
/* bmControls, bAssocTerminal and iClockSource omitted */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.7.2.3 Clock Multiplier Descriptor */
|
||||
|
||||
struct uac_clock_multiplier_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bClockID;
|
||||
__u8 bCSourceID;
|
||||
__u8 bmControls;
|
||||
__u8 iClockMultiplier;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.7.2.4 Input terminal descriptor */
|
||||
|
|
|
@ -47,6 +47,15 @@
|
|||
#define UAC_FORMAT_TYPE 0x02
|
||||
#define UAC_FORMAT_SPECIFIC 0x03
|
||||
|
||||
/* A.7 Processing Unit Process Types */
|
||||
#define UAC_PROCESS_UNDEFINED 0x00
|
||||
#define UAC_PROCESS_UP_DOWNMIX 0x01
|
||||
#define UAC_PROCESS_DOLBY_PROLOGIC 0x02
|
||||
#define UAC_PROCESS_STEREO_EXTENDER 0x03
|
||||
#define UAC_PROCESS_REVERB 0x04
|
||||
#define UAC_PROCESS_CHORUS 0x05
|
||||
#define UAC_PROCESS_DYN_RANGE_COMP 0x06
|
||||
|
||||
/* A.8 Audio Class-Specific Endpoint Descriptor Subtypes */
|
||||
#define UAC_EP_GENERAL 0x01
|
||||
|
||||
|
@ -73,6 +82,60 @@
|
|||
|
||||
#define UAC_GET_STAT 0xff
|
||||
|
||||
/* A.10 Control Selector Codes */
|
||||
|
||||
/* A.10.1 Terminal Control Selectors */
|
||||
#define UAC_TERM_COPY_PROTECT 0x01
|
||||
|
||||
/* A.10.2 Feature Unit Control Selectors */
|
||||
#define UAC_FU_MUTE 0x01
|
||||
#define UAC_FU_VOLUME 0x02
|
||||
#define UAC_FU_BASS 0x03
|
||||
#define UAC_FU_MID 0x04
|
||||
#define UAC_FU_TREBLE 0x05
|
||||
#define UAC_FU_GRAPHIC_EQUALIZER 0x06
|
||||
#define UAC_FU_AUTOMATIC_GAIN 0x07
|
||||
#define UAC_FU_DELAY 0x08
|
||||
#define UAC_FU_BASS_BOOST 0x09
|
||||
#define UAC_FU_LOUDNESS 0x0a
|
||||
|
||||
#define UAC_CONTROL_BIT(CS) (1 << ((CS) - 1))
|
||||
|
||||
/* A.10.3.1 Up/Down-mix Processing Unit Controls Selectors */
|
||||
#define UAC_UD_ENABLE 0x01
|
||||
#define UAC_UD_MODE_SELECT 0x02
|
||||
|
||||
/* A.10.3.2 Dolby Prologic (tm) Processing Unit Controls Selectors */
|
||||
#define UAC_DP_ENABLE 0x01
|
||||
#define UAC_DP_MODE_SELECT 0x02
|
||||
|
||||
/* A.10.3.3 3D Stereo Extender Processing Unit Control Selectors */
|
||||
#define UAC_3D_ENABLE 0x01
|
||||
#define UAC_3D_SPACE 0x02
|
||||
|
||||
/* A.10.3.4 Reverberation Processing Unit Control Selectors */
|
||||
#define UAC_REVERB_ENABLE 0x01
|
||||
#define UAC_REVERB_LEVEL 0x02
|
||||
#define UAC_REVERB_TIME 0x03
|
||||
#define UAC_REVERB_FEEDBACK 0x04
|
||||
|
||||
/* A.10.3.5 Chorus Processing Unit Control Selectors */
|
||||
#define UAC_CHORUS_ENABLE 0x01
|
||||
#define UAC_CHORUS_LEVEL 0x02
|
||||
#define UAC_CHORUS_RATE 0x03
|
||||
#define UAC_CHORUS_DEPTH 0x04
|
||||
|
||||
/* A.10.3.6 Dynamic Range Compressor Unit Control Selectors */
|
||||
#define UAC_DCR_ENABLE 0x01
|
||||
#define UAC_DCR_RATE 0x02
|
||||
#define UAC_DCR_MAXAMPL 0x03
|
||||
#define UAC_DCR_THRESHOLD 0x04
|
||||
#define UAC_DCR_ATTACK_TIME 0x05
|
||||
#define UAC_DCR_RELEASE_TIME 0x06
|
||||
|
||||
/* A.10.4 Extension Unit Control Selectors */
|
||||
#define UAC_XU_ENABLE 0x01
|
||||
|
||||
/* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
|
||||
#define UAC_MS_HEADER 0x01
|
||||
#define UAC_MIDI_IN_JACK 0x02
|
||||
|
@ -244,7 +307,7 @@ struct uac_selector_unit_descriptor {
|
|||
static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc)
|
||||
{
|
||||
__u8 *raw = (__u8 *) desc;
|
||||
return raw[9 + desc->bLength - 1];
|
||||
return raw[desc->bLength - 1];
|
||||
}
|
||||
|
||||
/* 4.3.2.5 Feature Unit Descriptor */
|
||||
|
@ -463,31 +526,6 @@ struct uac_iso_endpoint_descriptor {
|
|||
#define UAC_EP_CS_ATTR_PITCH_CONTROL 0x02
|
||||
#define UAC_EP_CS_ATTR_FILL_MAX 0x80
|
||||
|
||||
/* A.10.2 Feature Unit Control Selectors */
|
||||
|
||||
#define UAC_FU_CONTROL_UNDEFINED 0x00
|
||||
#define UAC_MUTE_CONTROL 0x01
|
||||
#define UAC_VOLUME_CONTROL 0x02
|
||||
#define UAC_BASS_CONTROL 0x03
|
||||
#define UAC_MID_CONTROL 0x04
|
||||
#define UAC_TREBLE_CONTROL 0x05
|
||||
#define UAC_GRAPHIC_EQUALIZER_CONTROL 0x06
|
||||
#define UAC_AUTOMATIC_GAIN_CONTROL 0x07
|
||||
#define UAC_DELAY_CONTROL 0x08
|
||||
#define UAC_BASS_BOOST_CONTROL 0x09
|
||||
#define UAC_LOUDNESS_CONTROL 0x0a
|
||||
|
||||
#define UAC_FU_MUTE (1 << (UAC_MUTE_CONTROL - 1))
|
||||
#define UAC_FU_VOLUME (1 << (UAC_VOLUME_CONTROL - 1))
|
||||
#define UAC_FU_BASS (1 << (UAC_BASS_CONTROL - 1))
|
||||
#define UAC_FU_MID (1 << (UAC_MID_CONTROL - 1))
|
||||
#define UAC_FU_TREBLE (1 << (UAC_TREBLE_CONTROL - 1))
|
||||
#define UAC_FU_GRAPHIC_EQ (1 << (UAC_GRAPHIC_EQUALIZER_CONTROL - 1))
|
||||
#define UAC_FU_AUTO_GAIN (1 << (UAC_AUTOMATIC_GAIN_CONTROL - 1))
|
||||
#define UAC_FU_DELAY (1 << (UAC_DELAY_CONTROL - 1))
|
||||
#define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1))
|
||||
#define UAC_FU_LOUDNESS (1 << (UAC_LOUDNESS_CONTROL - 1))
|
||||
|
||||
/* status word format (3.7.1.1) */
|
||||
|
||||
#define UAC1_STATUS_TYPE_ORIG_MASK 0x0f
|
||||
|
|
|
@ -2578,6 +2578,9 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
|
|||
if (err)
|
||||
return -err;
|
||||
|
||||
memset(&prev_ctl, 0, sizeof(prev_ctl));
|
||||
prev_ctl.control_type = -1;
|
||||
|
||||
for (idx = 0; idx < 2000; idx++) {
|
||||
err = hpi_mixer_get_control_by_index(
|
||||
ss, asihpi->h_mixer,
|
||||
|
|
|
@ -1913,11 +1913,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
|
|||
if (WARN_ONCE(!azx_dev->period_bytes,
|
||||
"hda-intel: zero azx_dev->period_bytes"))
|
||||
return -1; /* this shouldn't happen! */
|
||||
if (wallclk <= azx_dev->period_wallclk &&
|
||||
if (wallclk < (azx_dev->period_wallclk * 5) / 4 &&
|
||||
pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
|
||||
/* NG - it's below the first next period boundary */
|
||||
return bdl_pos_adj[chip->dev_index] ? 0 : -1;
|
||||
azx_dev->start_wallclk = wallclk;
|
||||
azx_dev->start_wallclk += wallclk;
|
||||
return 1; /* OK, it's fine */
|
||||
}
|
||||
|
||||
|
@ -2288,6 +2288,8 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = {
|
|||
SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB),
|
||||
|
@ -2296,6 +2298,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = {
|
|||
SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB),
|
||||
{}
|
||||
|
|
|
@ -9476,6 +9476,7 @@ static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
|
|||
SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24),
|
||||
SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24),
|
||||
SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3),
|
||||
SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31),
|
||||
SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
|
||||
SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
|
||||
SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
|
||||
|
|
|
@ -13,9 +13,18 @@ config SND_MXC_SOC_SSI
|
|||
|
||||
config SND_MXC_SOC_WM1133_EV1
|
||||
tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted"
|
||||
depends on SND_IMX_SOC && EXPERIMENTAL
|
||||
depends on SND_IMX_SOC && MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
|
||||
select SND_SOC_WM8350
|
||||
select SND_MXC_SOC_SSI
|
||||
help
|
||||
Enable support for audio on the i.MX31ADS with the WM1133-EV1
|
||||
PMIC board with WM8835x fitted.
|
||||
|
||||
config SND_SOC_PHYCORE_AC97
|
||||
tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
|
||||
depends on MACH_PCM043 || MACH_PCA100
|
||||
select SND_MXC_SOC_SSI
|
||||
select SND_SOC_WM9712
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Phytec phyCORE
|
||||
and phyCARD boards in AC97 mode
|
||||
|
|
|
@ -11,7 +11,8 @@ snd-usb-audio-objs := card.o \
|
|||
endpoint.o \
|
||||
urb.o \
|
||||
pcm.o \
|
||||
helper.o
|
||||
helper.o \
|
||||
clock.o
|
||||
|
||||
snd-usbmidi-lib-objs := midi.o
|
||||
|
||||
|
|
|
@ -236,7 +236,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|||
}
|
||||
|
||||
case UAC_VERSION_2: {
|
||||
struct uac_clock_source_descriptor *cs;
|
||||
struct usb_interface_assoc_descriptor *assoc =
|
||||
usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
|
||||
|
||||
|
@ -245,21 +244,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: for now, we expect there is at least one clock source
|
||||
* descriptor and we always take the first one.
|
||||
* We should properly support devices with multiple clock sources,
|
||||
* clock selectors and sample rate conversion units. */
|
||||
|
||||
cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
|
||||
NULL, UAC2_CLOCK_SOURCE);
|
||||
|
||||
if (!cs) {
|
||||
snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->clock_id = cs->bClockID;
|
||||
|
||||
for (i = 0; i < assoc->bInterfaceCount; i++) {
|
||||
int intf = assoc->bFirstInterface + i;
|
||||
|
||||
|
@ -481,6 +465,8 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
|
|||
goto __error;
|
||||
}
|
||||
|
||||
chip->ctrl_intf = alts;
|
||||
|
||||
if (err > 0) {
|
||||
/* create normal USB audio interfaces */
|
||||
if (snd_usb_create_streams(chip, ifnum) < 0 ||
|
||||
|
|
|
@ -25,6 +25,7 @@ struct audioformat {
|
|||
unsigned int rate_min, rate_max; /* min/max rates */
|
||||
unsigned int nr_rates; /* number of rate table entries */
|
||||
unsigned int *rate_table; /* rate table */
|
||||
unsigned char clock; /* associated clock */
|
||||
};
|
||||
|
||||
struct snd_usb_substream;
|
||||
|
|
311
sound/usb/clock.c
Normal file
311
sound/usb/clock.c
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* 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/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mutex.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 <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include "usbaudio.h"
|
||||
#include "card.h"
|
||||
#include "midi.h"
|
||||
#include "mixer.h"
|
||||
#include "proc.h"
|
||||
#include "quirks.h"
|
||||
#include "endpoint.h"
|
||||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
#include "pcm.h"
|
||||
#include "urb.h"
|
||||
#include "format.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->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->bClockID == clock_id)
|
||||
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->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, selector_id << 8,
|
||||
&buf, sizeof(buf), 1000);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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, source_id << 8,
|
||||
&data, sizeof(data), 1000);
|
||||
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
|
||||
__func__, source_id);
|
||||
return err;
|
||||
}
|
||||
|
||||
return !!data;
|
||||
}
|
||||
|
||||
/* Try to find the clock source ID of a given clock entity */
|
||||
|
||||
static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
||||
struct usb_host_interface *host_iface,
|
||||
int entity_id, unsigned long *visited)
|
||||
{
|
||||
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)) {
|
||||
snd_printk(KERN_WARNING
|
||||
"%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(host_iface, entity_id);
|
||||
if (source)
|
||||
return source->bClockID;
|
||||
|
||||
selector = snd_usb_find_clock_selector(host_iface, entity_id);
|
||||
if (selector) {
|
||||
int ret;
|
||||
|
||||
/* 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;
|
||||
|
||||
if (ret > selector->bNrInPins || ret < 1) {
|
||||
printk(KERN_ERR
|
||||
"%s(): selector reported illegal value, id %d, ret %d\n",
|
||||
__func__, selector->bClockID, ret);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return __uac_clock_find_source(chip, host_iface,
|
||||
selector->baCSourceID[ret-1],
|
||||
visited);
|
||||
}
|
||||
|
||||
/* FIXME: multipliers only act as pass-thru element for now */
|
||||
multiplier = snd_usb_find_clock_multiplier(host_iface, entity_id);
|
||||
if (multiplier)
|
||||
return __uac_clock_find_source(chip, host_iface,
|
||||
multiplier->bCSourceID, visited);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int snd_usb_clock_find_source(struct snd_usb_audio *chip,
|
||||
struct usb_host_interface *host_iface,
|
||||
int entity_id)
|
||||
{
|
||||
DECLARE_BITMAP(visited, 256);
|
||||
memset(visited, 0, sizeof(visited));
|
||||
return __uac_clock_find_source(chip, host_iface, entity_id, visited);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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)) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
|
||||
dev->devnum, iface, fmt->altsetting);
|
||||
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), 1000)) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
|
||||
dev->devnum, iface, fmt->altsetting, rate, ep);
|
||||
return err;
|
||||
}
|
||||
|
||||
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), 1000)) < 0) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
|
||||
dev->devnum, iface, fmt->altsetting, ep);
|
||||
return 0; /* some devices don't support reading */
|
||||
}
|
||||
|
||||
crate = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
if (crate != rate) {
|
||||
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
|
||||
// runtime->rate = crate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned char data[4];
|
||||
int err, crate;
|
||||
int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fmt->clock);
|
||||
|
||||
if (clock < 0)
|
||||
return clock;
|
||||
|
||||
if (!uac_clock_source_is_valid(chip, clock)) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n",
|
||||
dev->devnum, iface, fmt->altsetting, clock);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
data[0] = rate;
|
||||
data[1] = rate >> 8;
|
||||
data[2] = rate >> 16;
|
||||
data[3] = rate >> 24;
|
||||
if ((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, clock << 8,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
|
||||
dev->devnum, iface, fmt->altsetting, rate);
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((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, clock << 8,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
|
||||
dev->devnum, iface, fmt->altsetting);
|
||||
return err;
|
||||
}
|
||||
|
||||
crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
||||
if (crate != rate)
|
||||
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
|
||||
|
||||
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)
|
||||
{
|
||||
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
|
||||
|
||||
switch (altsd->bInterfaceProtocol) {
|
||||
case UAC_VERSION_1:
|
||||
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
|
||||
|
||||
case UAC_VERSION_2:
|
||||
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
12
sound/usb/clock.h
Normal file
12
sound/usb/clock.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef __USBAUDIO_CLOCK_H
|
||||
#define __USBAUDIO_CLOCK_H
|
||||
|
||||
int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
||||
struct usb_host_interface *alts,
|
||||
struct audioformat *fmt, int rate);
|
||||
|
||||
int snd_usb_clock_find_source(struct snd_usb_audio *chip,
|
||||
struct usb_host_interface *host_iface,
|
||||
int entity_id);
|
||||
|
||||
#endif /* __USBAUDIO_CLOCK_H */
|
|
@ -190,6 +190,38 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
|
|||
return attributes;
|
||||
}
|
||||
|
||||
static struct uac2_input_terminal_descriptor *
|
||||
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
|
||||
int terminal_id)
|
||||
{
|
||||
struct uac2_input_terminal_descriptor *term = NULL;
|
||||
|
||||
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||
ctrl_iface->extralen,
|
||||
term, UAC_INPUT_TERMINAL))) {
|
||||
if (term->bTerminalID == terminal_id)
|
||||
return term;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct uac2_output_terminal_descriptor *
|
||||
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
|
||||
int terminal_id)
|
||||
{
|
||||
struct uac2_output_terminal_descriptor *term = NULL;
|
||||
|
||||
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||
ctrl_iface->extralen,
|
||||
term, UAC_OUTPUT_TERMINAL))) {
|
||||
if (term->bTerminalID == terminal_id)
|
||||
return term;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
||||
{
|
||||
struct usb_device *dev;
|
||||
|
@ -199,7 +231,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
int i, altno, err, stream;
|
||||
int format = 0, num_channels = 0;
|
||||
struct audioformat *fp = NULL;
|
||||
int num, protocol;
|
||||
int num, protocol, clock = 0;
|
||||
struct uac_format_type_i_continuous_descriptor *fmt;
|
||||
|
||||
dev = chip->dev;
|
||||
|
@ -263,6 +295,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
}
|
||||
|
||||
case UAC_VERSION_2: {
|
||||
struct uac2_input_terminal_descriptor *input_term;
|
||||
struct uac2_output_terminal_descriptor *output_term;
|
||||
struct uac_as_header_descriptor_v2 *as =
|
||||
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
|
||||
|
||||
|
@ -281,7 +315,25 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
num_channels = as->bNrChannels;
|
||||
format = le32_to_cpu(as->bmFormats);
|
||||
|
||||
break;
|
||||
/* lookup the terminal associated to this interface
|
||||
* to extract the clock */
|
||||
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
if (input_term) {
|
||||
clock = input_term->bCSourceID;
|
||||
break;
|
||||
}
|
||||
|
||||
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
if (output_term) {
|
||||
clock = output_term->bCSourceID;
|
||||
break;
|
||||
}
|
||||
|
||||
snd_printk(KERN_ERR "%d:%u:%d : bogus bTerminalLink %d\n",
|
||||
dev->devnum, iface_no, altno, as->bTerminalLink);
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -338,6 +390,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
|
||||
* (fp->maxpacksize & 0x7ff);
|
||||
fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
|
||||
fp->clock = clock;
|
||||
|
||||
/* some quirks for attributes here */
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "quirks.h"
|
||||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
#include "clock.h"
|
||||
|
||||
/*
|
||||
* parse the audio format type I descriptor
|
||||
|
@ -215,15 +216,17 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
|||
struct usb_device *dev = chip->dev;
|
||||
unsigned char tmp[2], *data;
|
||||
int i, nr_rates, data_size, ret = 0;
|
||||
int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
|
||||
|
||||
/* get the number of sample rates first by only fetching 2 bytes */
|
||||
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
|
||||
tmp, sizeof(tmp), 1000);
|
||||
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
|
||||
snd_printk(KERN_ERR "%s(): unable to retrieve number of sample rates (clock %d)\n",
|
||||
__func__, clock);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -237,12 +240,13 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
|||
|
||||
/* now get the full information */
|
||||
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
|
||||
data, data_size, 1000);
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
|
||||
data, data_size, 1000);
|
||||
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
|
||||
snd_printk(KERN_ERR "%s(): unable to retrieve sample rate range (clock %d)\n",
|
||||
__func__, clock);
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
|
|
@ -78,39 +78,6 @@ enum {
|
|||
USB_MIXER_U16,
|
||||
};
|
||||
|
||||
enum {
|
||||
USB_PROC_UPDOWN = 1,
|
||||
USB_PROC_UPDOWN_SWITCH = 1,
|
||||
USB_PROC_UPDOWN_MODE_SEL = 2,
|
||||
|
||||
USB_PROC_PROLOGIC = 2,
|
||||
USB_PROC_PROLOGIC_SWITCH = 1,
|
||||
USB_PROC_PROLOGIC_MODE_SEL = 2,
|
||||
|
||||
USB_PROC_3DENH = 3,
|
||||
USB_PROC_3DENH_SWITCH = 1,
|
||||
USB_PROC_3DENH_SPACE = 2,
|
||||
|
||||
USB_PROC_REVERB = 4,
|
||||
USB_PROC_REVERB_SWITCH = 1,
|
||||
USB_PROC_REVERB_LEVEL = 2,
|
||||
USB_PROC_REVERB_TIME = 3,
|
||||
USB_PROC_REVERB_DELAY = 4,
|
||||
|
||||
USB_PROC_CHORUS = 5,
|
||||
USB_PROC_CHORUS_SWITCH = 1,
|
||||
USB_PROC_CHORUS_LEVEL = 2,
|
||||
USB_PROC_CHORUS_RATE = 3,
|
||||
USB_PROC_CHORUS_DEPTH = 4,
|
||||
|
||||
USB_PROC_DCR = 6,
|
||||
USB_PROC_DCR_SWITCH = 1,
|
||||
USB_PROC_DCR_RATIO = 2,
|
||||
USB_PROC_DCR_MAX_AMP = 3,
|
||||
USB_PROC_DCR_THRESHOLD = 4,
|
||||
USB_PROC_DCR_ATTACK = 5,
|
||||
USB_PROC_DCR_RELEASE = 6,
|
||||
};
|
||||
|
||||
/*E-mu 0202(0404) eXtension Unit(XU) control*/
|
||||
enum {
|
||||
|
@ -198,22 +165,24 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
|
|||
|
||||
/*
|
||||
* find an audio control unit with the given unit id
|
||||
* this doesn't return any clock related units, so they need to be handled elsewhere
|
||||
*/
|
||||
static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit)
|
||||
{
|
||||
unsigned char *p;
|
||||
/* we just parse the header */
|
||||
struct uac_feature_unit_descriptor *hdr = NULL;
|
||||
|
||||
p = NULL;
|
||||
while ((p = snd_usb_find_desc(state->buffer, state->buflen, p,
|
||||
USB_DT_CS_INTERFACE)) != NULL) {
|
||||
if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit)
|
||||
return p;
|
||||
while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
|
||||
USB_DT_CS_INTERFACE)) != NULL) {
|
||||
if (hdr->bLength >= 4 &&
|
||||
hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
|
||||
hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER &&
|
||||
hdr->bUnitID == unit)
|
||||
return hdr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* copy a string with the given id
|
||||
*/
|
||||
|
@ -344,8 +313,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
|
|||
buf, sizeof(buf), 1000);
|
||||
|
||||
if (ret < 0) {
|
||||
snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
|
||||
request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
|
||||
snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
|
||||
request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -462,6 +431,16 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
|
|||
int index, int value)
|
||||
{
|
||||
int err;
|
||||
unsigned int read_only = (channel == 0) ?
|
||||
cval->master_readonly :
|
||||
cval->ch_readonly & (1 << (channel - 1));
|
||||
|
||||
if (read_only) {
|
||||
snd_printdd(KERN_INFO "%s(): channel %d of control %d is read_only\n",
|
||||
__func__, channel, cval->control);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
|
||||
value);
|
||||
if (err < 0)
|
||||
|
@ -631,6 +610,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
|
|||
*/
|
||||
static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
|
||||
{
|
||||
int err;
|
||||
void *p1;
|
||||
|
||||
memset(term, 0, sizeof(*term));
|
||||
|
@ -651,6 +631,11 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
|
|||
term->channels = d->bNrChannels;
|
||||
term->chconfig = le32_to_cpu(d->bmChannelConfig);
|
||||
term->name = d->iTerminal;
|
||||
|
||||
/* call recursively to get the clock selectors */
|
||||
err = check_input_term(state, d->bCSourceID, term);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
case UAC_FEATURE_UNIT: {
|
||||
|
@ -667,7 +652,8 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
|
|||
term->name = uac_mixer_unit_iMixer(d);
|
||||
return 0;
|
||||
}
|
||||
case UAC_SELECTOR_UNIT: {
|
||||
case UAC_SELECTOR_UNIT:
|
||||
case UAC2_CLOCK_SELECTOR: {
|
||||
struct uac_selector_unit_descriptor *d = p1;
|
||||
/* call recursively to retrieve the channel info */
|
||||
if (check_input_term(state, d->baSourceID[0], term) < 0)
|
||||
|
@ -690,6 +676,13 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
|
|||
term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
|
||||
return 0;
|
||||
}
|
||||
case UAC2_CLOCK_SOURCE: {
|
||||
struct uac_clock_source_descriptor *d = p1;
|
||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||
term->id = id;
|
||||
term->name = d->iClockSource;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -709,16 +702,20 @@ struct usb_feature_control_info {
|
|||
};
|
||||
|
||||
static struct usb_feature_control_info audio_feature_info[] = {
|
||||
{ "Mute", USB_MIXER_INV_BOOLEAN },
|
||||
{ "Volume", USB_MIXER_S16 },
|
||||
{ "Mute", USB_MIXER_INV_BOOLEAN },
|
||||
{ "Volume", USB_MIXER_S16 },
|
||||
{ "Tone Control - Bass", USB_MIXER_S8 },
|
||||
{ "Tone Control - Mid", USB_MIXER_S8 },
|
||||
{ "Tone Control - Treble", USB_MIXER_S8 },
|
||||
{ "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */
|
||||
{ "Auto Gain Control", USB_MIXER_BOOLEAN },
|
||||
{ "Delay Control", USB_MIXER_U16 },
|
||||
{ "Bass Boost", USB_MIXER_BOOLEAN },
|
||||
{ "Loudness", USB_MIXER_BOOLEAN },
|
||||
{ "Auto Gain Control", USB_MIXER_BOOLEAN },
|
||||
{ "Delay Control", USB_MIXER_U16 },
|
||||
{ "Bass Boost", USB_MIXER_BOOLEAN },
|
||||
{ "Loudness", USB_MIXER_BOOLEAN },
|
||||
/* UAC2 specific */
|
||||
{ "Input Gain Control", USB_MIXER_U16 },
|
||||
{ "Input Gain Pad Control", USB_MIXER_BOOLEAN },
|
||||
{ "Phase Inverter Control", USB_MIXER_BOOLEAN },
|
||||
};
|
||||
|
||||
|
||||
|
@ -958,7 +955,7 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
|
|||
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||
unsigned int ctl_mask, int control,
|
||||
struct usb_audio_term *iterm, int unitid,
|
||||
int read_only)
|
||||
int readonly_mask)
|
||||
{
|
||||
struct uac_feature_unit_descriptor *desc = raw_desc;
|
||||
unsigned int len = 0;
|
||||
|
@ -970,7 +967,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
|
||||
control++; /* change from zero-based to 1-based value */
|
||||
|
||||
if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) {
|
||||
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
|
||||
/* FIXME: not supported yet */
|
||||
return;
|
||||
}
|
||||
|
@ -989,20 +986,25 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
cval->control = control;
|
||||
cval->cmask = ctl_mask;
|
||||
cval->val_type = audio_feature_info[control-1].type;
|
||||
if (ctl_mask == 0)
|
||||
if (ctl_mask == 0) {
|
||||
cval->channels = 1; /* master channel */
|
||||
else {
|
||||
cval->master_readonly = readonly_mask;
|
||||
} else {
|
||||
int i, c = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
if (ctl_mask & (1 << i))
|
||||
c++;
|
||||
cval->channels = c;
|
||||
cval->ch_readonly = readonly_mask;
|
||||
}
|
||||
|
||||
/* get min/max values */
|
||||
get_min_max(cval, 0);
|
||||
|
||||
if (read_only)
|
||||
/* if all channels in the mask are marked read-only, make the control
|
||||
* read-only. set_cur_mix_value() will check the mask again and won't
|
||||
* issue write commands to read-only channels. */
|
||||
if (cval->channels == readonly_mask)
|
||||
kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
|
||||
else
|
||||
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
|
||||
|
@ -1021,8 +1023,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
kctl->id.name, sizeof(kctl->id.name));
|
||||
|
||||
switch (control) {
|
||||
case UAC_MUTE_CONTROL:
|
||||
case UAC_VOLUME_CONTROL:
|
||||
case UAC_FU_MUTE:
|
||||
case UAC_FU_VOLUME:
|
||||
/* determine the control name. the rule is:
|
||||
* - if a name id is given in descriptor, use it.
|
||||
* - if the connected input can be determined, then use the name
|
||||
|
@ -1049,9 +1051,9 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|||
len = append_ctl_name(kctl, " Playback");
|
||||
}
|
||||
}
|
||||
append_ctl_name(kctl, control == UAC_MUTE_CONTROL ?
|
||||
append_ctl_name(kctl, control == UAC_FU_MUTE ?
|
||||
" Switch" : " Volume");
|
||||
if (control == UAC_VOLUME_CONTROL) {
|
||||
if (control == UAC_FU_VOLUME) {
|
||||
kctl->tlv.c = mixer_vol_tlv;
|
||||
kctl->vd[0].access |=
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||
|
@ -1150,7 +1152,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
|
|||
snd_printk(KERN_INFO
|
||||
"usbmixer: master volume quirk for PCM2702 chip\n");
|
||||
/* disable non-functional volume control */
|
||||
master_bits &= ~UAC_FU_VOLUME;
|
||||
master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME);
|
||||
break;
|
||||
}
|
||||
if (channels > 0)
|
||||
|
@ -1188,19 +1190,22 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
|
|||
|
||||
for (j = 0; j < channels; j++) {
|
||||
unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
|
||||
if (mask & (1 << (i * 2))) {
|
||||
if (uac2_control_is_readable(mask, i)) {
|
||||
ch_bits |= (1 << j);
|
||||
if (~mask & (1 << ((i * 2) + 1)))
|
||||
if (!uac2_control_is_writeable(mask, i))
|
||||
ch_read_only |= (1 << j);
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: the whole unit is read-only if any of the channels is marked read-only */
|
||||
/* NOTE: build_feature_ctl() will mark the control read-only if all channels
|
||||
* are marked read-only in the descriptors. Otherwise, the control will be
|
||||
* reported as writeable, but the driver will not actually issue a write
|
||||
* command for read-only channels */
|
||||
if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
|
||||
build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, !!ch_read_only);
|
||||
if (master_bits & (1 << i * 2))
|
||||
build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, ch_read_only);
|
||||
if (uac2_control_is_readable(master_bits, i))
|
||||
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
|
||||
~master_bits & (1 << ((i * 2) + 1)));
|
||||
!uac2_control_is_writeable(master_bits, i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1392,51 +1397,51 @@ struct procunit_info {
|
|||
};
|
||||
|
||||
static struct procunit_value_info updown_proc_info[] = {
|
||||
{ USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 },
|
||||
{ UAC_UD_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ UAC_UD_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 },
|
||||
{ 0 }
|
||||
};
|
||||
static struct procunit_value_info prologic_proc_info[] = {
|
||||
{ USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 },
|
||||
{ UAC_DP_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ UAC_DP_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 },
|
||||
{ 0 }
|
||||
};
|
||||
static struct procunit_value_info threed_enh_proc_info[] = {
|
||||
{ USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 },
|
||||
{ UAC_3D_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ UAC_3D_SPACE, "Spaciousness", USB_MIXER_U8 },
|
||||
{ 0 }
|
||||
};
|
||||
static struct procunit_value_info reverb_proc_info[] = {
|
||||
{ USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 },
|
||||
{ USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 },
|
||||
{ USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 },
|
||||
{ UAC_REVERB_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ UAC_REVERB_LEVEL, "Level", USB_MIXER_U8 },
|
||||
{ UAC_REVERB_TIME, "Time", USB_MIXER_U16 },
|
||||
{ UAC_REVERB_FEEDBACK, "Feedback", USB_MIXER_U8 },
|
||||
{ 0 }
|
||||
};
|
||||
static struct procunit_value_info chorus_proc_info[] = {
|
||||
{ USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 },
|
||||
{ USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 },
|
||||
{ USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 },
|
||||
{ UAC_CHORUS_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ UAC_CHORUS_LEVEL, "Level", USB_MIXER_U8 },
|
||||
{ UAC_CHORUS_RATE, "Rate", USB_MIXER_U16 },
|
||||
{ UAC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 },
|
||||
{ 0 }
|
||||
};
|
||||
static struct procunit_value_info dcr_proc_info[] = {
|
||||
{ USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 },
|
||||
{ USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 },
|
||||
{ USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 },
|
||||
{ USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 },
|
||||
{ USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 },
|
||||
{ UAC_DCR_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
||||
{ UAC_DCR_RATE, "Ratio", USB_MIXER_U16 },
|
||||
{ UAC_DCR_MAXAMPL, "Max Amp", USB_MIXER_S16 },
|
||||
{ UAC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 },
|
||||
{ UAC_DCR_ATTACK_TIME, "Attack Time", USB_MIXER_U16 },
|
||||
{ UAC_DCR_RELEASE_TIME, "Release Time", USB_MIXER_U16 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct procunit_info procunits[] = {
|
||||
{ USB_PROC_UPDOWN, "Up Down", updown_proc_info },
|
||||
{ USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info },
|
||||
{ USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info },
|
||||
{ USB_PROC_REVERB, "Reverb", reverb_proc_info },
|
||||
{ USB_PROC_CHORUS, "Chorus", chorus_proc_info },
|
||||
{ USB_PROC_DCR, "DCR", dcr_proc_info },
|
||||
{ UAC_PROCESS_UP_DOWNMIX, "Up Down", updown_proc_info },
|
||||
{ UAC_PROCESS_DOLBY_PROLOGIC, "Dolby Prologic", prologic_proc_info },
|
||||
{ UAC_PROCESS_STEREO_EXTENDER, "3D Stereo Extender", threed_enh_proc_info },
|
||||
{ UAC_PROCESS_REVERB, "Reverb", reverb_proc_info },
|
||||
{ UAC_PROCESS_CHORUS, "Chorus", chorus_proc_info },
|
||||
{ UAC_PROCESS_DYN_RANGE_COMP, "DCR", dcr_proc_info },
|
||||
{ 0 },
|
||||
};
|
||||
/*
|
||||
|
@ -1524,7 +1529,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
|
|||
cval->channels = 1;
|
||||
|
||||
/* get min/max values */
|
||||
if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
|
||||
if (type == UAC_PROCESS_UP_DOWNMIX && cval->control == UAC_UD_MODE_SELECT) {
|
||||
__u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol);
|
||||
/* FIXME: hard-coded */
|
||||
cval->min = 1;
|
||||
|
@ -1619,7 +1624,7 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
|
|||
struct usb_mixer_elem_info *cval = kcontrol->private_data;
|
||||
int val, err;
|
||||
|
||||
err = get_cur_ctl_value(cval, 0, &val);
|
||||
err = get_cur_ctl_value(cval, cval->control << 8, &val);
|
||||
if (err < 0) {
|
||||
if (cval->mixer->ignore_ctl_error) {
|
||||
ucontrol->value.enumerated.item[0] = 0;
|
||||
|
@ -1638,7 +1643,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
|
|||
struct usb_mixer_elem_info *cval = kcontrol->private_data;
|
||||
int val, oval, err;
|
||||
|
||||
err = get_cur_ctl_value(cval, 0, &oval);
|
||||
err = get_cur_ctl_value(cval, cval->control << 8, &oval);
|
||||
if (err < 0) {
|
||||
if (cval->mixer->ignore_ctl_error)
|
||||
return 0;
|
||||
|
@ -1647,7 +1652,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
|
|||
val = ucontrol->value.enumerated.item[0];
|
||||
val = get_abs_value(cval, val);
|
||||
if (val != oval) {
|
||||
set_cur_ctl_value(cval, 0, val);
|
||||
set_cur_ctl_value(cval, cval->control << 8, val);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1729,6 +1734,11 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
|
|||
cval->res = 1;
|
||||
cval->initialized = 1;
|
||||
|
||||
if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
|
||||
cval->control = UAC2_CX_CLOCK_SELECTOR;
|
||||
else
|
||||
cval->control = 0;
|
||||
|
||||
namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
|
||||
if (! namelist) {
|
||||
snd_printk(KERN_ERR "cannot malloc\n");
|
||||
|
@ -1778,7 +1788,9 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
|
|||
if (! len)
|
||||
strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name));
|
||||
|
||||
if ((state->oterm.type & 0xff00) == 0x0100)
|
||||
if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
|
||||
append_ctl_name(kctl, " Clock Source");
|
||||
else if ((state->oterm.type & 0xff00) == 0x0100)
|
||||
append_ctl_name(kctl, " Capture Source");
|
||||
else
|
||||
append_ctl_name(kctl, " Playback Source");
|
||||
|
@ -1812,10 +1824,12 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
|||
|
||||
switch (p1[2]) {
|
||||
case UAC_INPUT_TERMINAL:
|
||||
case UAC2_CLOCK_SOURCE:
|
||||
return 0; /* NOP */
|
||||
case UAC_MIXER_UNIT:
|
||||
return parse_audio_mixer_unit(state, unitid, p1);
|
||||
case UAC_SELECTOR_UNIT:
|
||||
case UAC2_CLOCK_SELECTOR:
|
||||
return parse_audio_selector_unit(state, unitid, p1);
|
||||
case UAC_FEATURE_UNIT:
|
||||
return parse_audio_feature_unit(state, unitid, p1);
|
||||
|
@ -1912,6 +1926,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
|||
err = parse_audio_unit(&state, desc->bSourceID);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* for UAC2, use the same approach to also add the clock selectors */
|
||||
err = parse_audio_unit(&state, desc->bCSourceID);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ struct usb_mixer_elem_info {
|
|||
unsigned int id;
|
||||
unsigned int control; /* CS or ICN (high byte) */
|
||||
unsigned int cmask; /* channel mask bitmap: 0 = master */
|
||||
unsigned int ch_readonly;
|
||||
unsigned int master_readonly;
|
||||
int channels;
|
||||
int val_type;
|
||||
int min, max, res;
|
||||
|
|
|
@ -85,8 +85,8 @@ static struct usbmix_name_map extigy_map[] = {
|
|||
/* 16: MU (w/o controls) */
|
||||
{ 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */
|
||||
{ 17, "Channel Routing", 2 }, /* PU: mode select */
|
||||
{ 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */
|
||||
{ 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */
|
||||
{ 18, "Tone Control - Bass", UAC_FU_BASS }, /* FU */
|
||||
{ 18, "Tone Control - Treble", UAC_FU_TREBLE }, /* FU */
|
||||
{ 18, "Master Playback" }, /* FU; others */
|
||||
/* 19: OT speaker */
|
||||
/* 20: OT headphone */
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "urb.h"
|
||||
#include "helper.h"
|
||||
#include "pcm.h"
|
||||
#include "clock.h"
|
||||
|
||||
/*
|
||||
* return the current pcm pointer. just based on the hwptr_done value.
|
||||
|
@ -181,103 +182,6 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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)) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
|
||||
dev->devnum, iface, fmt->altsetting);
|
||||
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), 1000)) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
|
||||
dev->devnum, iface, fmt->altsetting, rate, ep);
|
||||
return err;
|
||||
}
|
||||
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), 1000)) < 0) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
|
||||
dev->devnum, iface, fmt->altsetting, ep);
|
||||
return 0; /* some devices don't support reading */
|
||||
}
|
||||
crate = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
if (crate != rate) {
|
||||
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
|
||||
// runtime->rate = crate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned char data[4];
|
||||
int err, crate;
|
||||
|
||||
data[0] = rate;
|
||||
data[1] = rate >> 8;
|
||||
data[2] = rate >> 16;
|
||||
data[3] = rate >> 24;
|
||||
if ((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, chip->clock_id << 8,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
|
||||
dev->devnum, iface, fmt->altsetting, rate);
|
||||
return err;
|
||||
}
|
||||
if ((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, chip->clock_id << 8,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
|
||||
dev->devnum, iface, fmt->altsetting);
|
||||
return err;
|
||||
}
|
||||
crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
||||
if (crate != rate)
|
||||
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
|
||||
|
||||
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)
|
||||
{
|
||||
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
|
||||
|
||||
switch (altsd->bInterfaceProtocol) {
|
||||
case UAC_VERSION_1:
|
||||
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
|
||||
|
||||
case UAC_VERSION_2:
|
||||
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* find a matching format and set up the interface
|
||||
*/
|
||||
|
|
|
@ -40,9 +40,6 @@ struct snd_usb_audio {
|
|||
int num_interfaces;
|
||||
int num_suspended_intf;
|
||||
|
||||
/* for audio class v2 */
|
||||
int clock_id;
|
||||
|
||||
struct list_head pcm_list; /* list of pcm streams */
|
||||
int pcm_devs;
|
||||
|
||||
|
@ -53,6 +50,8 @@ struct snd_usb_audio {
|
|||
int setup; /* from the 'device_setup' module param */
|
||||
int nrpacks; /* from the 'nrpacks' module param */
|
||||
int async_unlink; /* from the 'async_unlink' module param */
|
||||
|
||||
struct usb_host_interface *ctrl_intf; /* the audio control interface */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue