summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/aoa/soundbus/i2sbus/core.c8
-rw-r--r--sound/atmel/ac97c.c2
-rw-r--r--sound/core/pcm_misc.c16
-rw-r--r--sound/pci/asihpi/asihpi.c3
-rw-r--r--sound/pci/asihpi/hpi6205.c22
-rw-r--r--sound/pci/hda/hda_intel.c3
-rw-r--r--sound/pci/hda/patch_realtek.c1
-rw-r--r--sound/pci/riptide/riptide.c12
-rw-r--r--sound/soc/imx/Kconfig11
-rw-r--r--sound/spi/at73c213.c1
-rw-r--r--sound/usb/Makefile3
-rw-r--r--sound/usb/card.c20
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/clock.c309
-rw-r--r--sound/usb/clock.h10
-rw-r--r--sound/usb/endpoint.c63
-rw-r--r--sound/usb/format.c123
-rw-r--r--sound/usb/helper.h4
-rw-r--r--sound/usb/midi.c14
-rw-r--r--sound/usb/mixer.c320
-rw-r--r--sound/usb/mixer.h3
-rw-r--r--sound/usb/mixer_maps.c4
-rw-r--r--sound/usb/pcm.c98
-rw-r--r--sound/usb/pcm.h3
-rw-r--r--sound/usb/quirks.c1
-rw-r--r--sound/usb/usbaudio.h5
26 files changed, 738 insertions, 322 deletions
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index 6789337..3ff8cc5 100644
--- a/sound/aoa/soundbus/i2sbus/core.c
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -437,9 +437,11 @@ static int i2sbus_shutdown(struct macio_dev* dev)
}
static struct macio_driver i2sbus_drv = {
- .name = "soundbus-i2s",
- .owner = THIS_MODULE,
- .match_table = i2sbus_match,
+ .driver = {
+ .name = "soundbus-i2s",
+ .owner = THIS_MODULE,
+ .of_match_table = i2sbus_match,
+ },
.probe = i2sbus_probe,
.remove = i2sbus_remove,
#ifdef CONFIG_PM
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 428121a..10c3a87 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -657,7 +657,7 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
if (sr & AC97C_SR_CAEVT) {
struct snd_pcm_runtime *runtime;
int offset, next_period, block_size;
- dev_info(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
+ dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
casr & AC97C_CSR_OVRUN ? " OVRUN" : "",
casr & AC97C_CSR_RXRDY ? " RXRDY" : "",
casr & AC97C_CSR_UNRUN ? " UNRUN" : "",
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index ea2bf82..434af3c 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -128,6 +128,14 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
.width = 4, .phys = 4, .le = -1, .signd = -1,
.silence = {},
},
+ [SNDRV_PCM_FORMAT_G723_24] = {
+ .width = 3, .phys = 3, .le = -1, .signd = -1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_G723_40] = {
+ .width = 5, .phys = 5, .le = -1, .signd = -1,
+ .silence = {},
+ },
/* FIXME: the following three formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1,
@@ -186,6 +194,14 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
.width = 18, .phys = 24, .le = 0, .signd = 0,
.silence = { 0x02, 0x00, 0x00 },
},
+ [SNDRV_PCM_FORMAT_G723_24_1B] = {
+ .width = 3, .phys = 8, .le = -1, .signd = -1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_G723_40_1B] = {
+ .width = 5, .phys = 8, .le = -1, .signd = -1,
+ .silence = {},
+ },
};
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index f74c737..1db586a 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -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,
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index e89991e..3b44134 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -941,11 +941,11 @@ static void outstream_host_buffer_free(struct hpi_adapter_obj *pao,
}
-static long outstream_get_space_available(struct hpi_hostbuffer_status
+static u32 outstream_get_space_available(struct hpi_hostbuffer_status
*status)
{
- return status->size_in_bytes - ((long)(status->host_index) -
- (long)(status->dSP_index));
+ return status->size_in_bytes - (status->host_index -
+ status->dSP_index);
}
static void outstream_write(struct hpi_adapter_obj *pao,
@@ -954,7 +954,7 @@ static void outstream_write(struct hpi_adapter_obj *pao,
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
- long space_available;
+ u32 space_available;
if (!phw->outstream_host_buffer_size[phm->obj_index]) {
/* there is no BBM buffer, write via message */
@@ -1007,7 +1007,7 @@ static void outstream_write(struct hpi_adapter_obj *pao,
}
space_available = outstream_get_space_available(status);
- if (space_available < (long)phm->u.d.u.data.data_size) {
+ if (space_available < phm->u.d.u.data.data_size) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
}
@@ -1018,7 +1018,7 @@ static void outstream_write(struct hpi_adapter_obj *pao,
&& hpios_locked_mem_valid(&phw->outstream_host_buffers[phm->
obj_index])) {
u8 *p_bbm_data;
- long l_first_write;
+ u32 l_first_write;
u8 *p_app_data = (u8 *)phm->u.d.u.data.pb_data;
if (hpios_locked_mem_get_virt_addr(&phw->
@@ -1248,9 +1248,9 @@ static void instream_start(struct hpi_adapter_obj *pao,
hw_message(pao, phm, phr);
}
-static long instream_get_bytes_available(struct hpi_hostbuffer_status *status)
+static u32 instream_get_bytes_available(struct hpi_hostbuffer_status *status)
{
- return (long)(status->dSP_index) - (long)(status->host_index);
+ return status->dSP_index - status->host_index;
}
static void instream_read(struct hpi_adapter_obj *pao,
@@ -1259,9 +1259,9 @@ static void instream_read(struct hpi_adapter_obj *pao,
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
struct hpi_hostbuffer_status *status;
- long data_available;
+ u32 data_available;
u8 *p_bbm_data;
- long l_first_read;
+ u32 l_first_read;
u8 *p_app_data = (u8 *)phm->u.d.u.data.pb_data;
if (!phw->instream_host_buffer_size[phm->obj_index]) {
@@ -1272,7 +1272,7 @@ static void instream_read(struct hpi_adapter_obj *pao,
status = &interface->instream_host_buffer_status[phm->obj_index];
data_available = instream_get_bytes_available(status);
- if (data_available < (long)phm->u.d.u.data.data_size) {
+ if (data_available < phm->u.d.u.data.data_size) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index af701a8..1df25cf 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -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),
{}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 17d4548..d792cdd 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -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),
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index ad44626..59d7996 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -97,6 +97,7 @@
#include <linux/gameport.h>
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/kernel.h>
#include <asm/io.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -667,13 +668,12 @@ static u32 atoh(const unsigned char *in, unsigned int len)
unsigned char c;
while (len) {
+ int value;
+
c = in[len - 1];
- if ((c >= '0') && (c <= '9'))
- sum += mult * (c - '0');
- else if ((c >= 'A') && (c <= 'F'))
- sum += mult * (c - ('A' - 10));
- else if ((c >= 'a') && (c <= 'f'))
- sum += mult * (c - ('a' - 10));
+ value = hex_to_bin(c);
+ if (value >= 0)
+ sum += mult * value;
mult *= 16;
--len;
}
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index eba9b9d..252defe 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -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
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
index 4c7b051..1bc56b2 100644
--- a/sound/spi/at73c213.c
+++ b/sound/spi/at73c213.c
@@ -69,7 +69,6 @@ struct snd_at73c213 {
int irq;
int period;
unsigned long bitrate;
- struct clk *bitclk;
struct ssc_device *ssc;
struct spi_device *spi;
u8 spi_wbuffer[2];
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index e7ac7f4..1e362bf 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -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
diff --git a/sound/usb/card.c b/sound/usb/card.c
index da1346b..9feb00c 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -217,7 +217,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
switch (protocol) {
case UAC_VERSION_1: {
- struct uac_ac_header_descriptor_v1 *h1 = control_header;
+ struct uac1_ac_header_descriptor *h1 = control_header;
if (!h1->bInCollection) {
snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
@@ -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 ||
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ed92420..1febf2f 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -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;
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
new file mode 100644
index 0000000..66bd157
--- /dev/null
+++ b/sound/usb/clock.c
@@ -0,0 +1,309 @@
+/*
+ * 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"
+
+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,
+ snd_usb_ctrl_intf(chip) | (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,
+ snd_usb_ctrl_intf(chip) | (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;
+}
+
+static int __uac_clock_find_source(struct snd_usb_audio *chip,
+ 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(chip->ctrl_intf, entity_id);
+ if (source)
+ return source->bClockID;
+
+ selector = snd_usb_find_clock_selector(chip->ctrl_intf, 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;
+
+ /* Selector values are one-based */
+
+ 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, selector->baCSourceID[ret-1],
+ visited);
+ }
+
+ /* 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);
+
+ 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)
+{
+ DECLARE_BITMAP(visited, 256);
+ memset(visited, 0, sizeof(visited));
+ return __uac_clock_find_source(chip, 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, fmt->clock);
+
+ if (clock < 0)
+ return clock;
+
+ if (!uac_clock_source_is_valid(chip, clock)) {
+ /* TODO: should we try to find valid clock setups by ourself? */
+ 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,
+ snd_usb_ctrl_intf(chip) | (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,
+ snd_usb_ctrl_intf(chip) | (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;
+}
+
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
new file mode 100644
index 0000000..4663093
--- /dev/null
+++ b/sound/usb/clock.h
@@ -0,0 +1,10 @@
+#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, int entity_id);
+
+#endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 28ee1ce..1a701f1 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -33,6 +33,7 @@
#include "pcm.h"
#include "helper.h"
#include "format.h"
+#include "clock.h"
/*
* free a substream
@@ -190,6 +191,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 +232,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;
@@ -243,7 +276,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
/* get audio formats */
switch (protocol) {
case UAC_VERSION_1: {
- struct uac_as_header_descriptor_v1 *as =
+ struct uac1_as_header_descriptor *as =
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
if (!as) {
@@ -263,7 +296,9 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
}
case UAC_VERSION_2: {
- struct uac_as_header_descriptor_v2 *as =
+ struct uac2_input_terminal_descriptor *input_term;
+ struct uac2_output_terminal_descriptor *output_term;
+ struct uac2_as_header_descriptor *as =
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
if (!as) {
@@ -281,7 +316,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 +391,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 */
@@ -374,6 +428,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
kfree(fp->rate_table);
kfree(fp);
+ fp = NULL;
continue;
}
diff --git a/sound/usb/format.c b/sound/usb/format.c
index fe29d61..4387f54 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -29,6 +29,7 @@
#include "quirks.h"
#include "helper.h"
#include "debug.h"
+#include "clock.h"
/*
* parse the audio format type I descriptor
@@ -205,30 +206,92 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
}
/*
+ * Helper function to walk the array of sample rate triplets reported by
+ * the device. The problem is that we need to parse whole array first to
+ * get to know how many sample rates we have to expect.
+ * Then fp->rate_table can be allocated and filled.
+ */
+static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets,
+ const unsigned char *data)
+{
+ int i, nr_rates = 0;
+
+ fp->rates = fp->rate_min = fp->rate_max = 0;
+
+ for (i = 0; i < nr_triplets; i++) {
+ int min = combine_quad(&data[2 + 12 * i]);
+ int max = combine_quad(&data[6 + 12 * i]);
+ int res = combine_quad(&data[10 + 12 * i]);
+ int rate;
+
+ if ((max < 0) || (min < 0) || (res < 0) || (max < min))
+ continue;
+
+ /*
+ * for ranges with res == 1, we announce a continuous sample
+ * rate range, and this function should return 0 for no further
+ * parsing.
+ */
+ if (res == 1) {
+ fp->rate_min = min;
+ fp->rate_max = max;
+ fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+ return 0;
+ }
+
+ for (rate = min; rate <= max; rate += res) {
+ if (fp->rate_table)
+ fp->rate_table[nr_rates] = rate;
+ if (!fp->rate_min || rate < fp->rate_min)
+ fp->rate_min = rate;
+ if (!fp->rate_max || rate > fp->rate_max)
+ fp->rate_max = rate;
+ fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+
+ nr_rates++;
+
+ /* avoid endless loop */
+ if (res == 0)
+ break;
+ }
+ }
+
+ return nr_rates;
+}
+
+/*
* parse the format descriptor and stores the possible sample rates
* on the audioformat table (audio class v2).
*/
static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
- struct audioformat *fp,
- struct usb_host_interface *iface)
+ struct audioformat *fp)
{
struct usb_device *dev = chip->dev;
unsigned char tmp[2], *data;
- int i, nr_rates, data_size, ret = 0;
+ int nr_triplets, data_size, ret = 0;
+ int clock = snd_usb_clock_find_source(chip, fp->clock);
+
+ if (clock < 0) {
+ snd_printk(KERN_ERR "%s(): unable to find clock source (clock %d)\n",
+ __func__, clock);
+ goto err;
+ }
/* 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,
+ snd_usb_ctrl_intf(chip) | (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;
}
- nr_rates = (tmp[1] << 8) | tmp[0];
- data_size = 2 + 12 * nr_rates;
+ nr_triplets = (tmp[1] << 8) | tmp[0];
+ data_size = 2 + 12 * nr_triplets;
data = kzalloc(data_size, GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
@@ -237,36 +300,40 @@ 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,
+ snd_usb_ctrl_intf(chip) | (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;
}
- fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+ /* Call the triplet parser, and make sure fp->rate_table is NULL.
+ * We just use the return value to know how many sample rates we
+ * will have to deal with. */
+ kfree(fp->rate_table);
+ fp->rate_table = NULL;
+ fp->nr_rates = parse_uac2_sample_rate_range(fp, nr_triplets, data);
+
+ if (fp->nr_rates == 0) {
+ /* SNDRV_PCM_RATE_CONTINUOUS */
+ ret = 0;
+ goto err_free;
+ }
+
+ fp->rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
if (!fp->rate_table) {
ret = -ENOMEM;
goto err_free;
}
- fp->nr_rates = 0;
- fp->rate_min = fp->rate_max = 0;
-
- for (i = 0; i < nr_rates; i++) {
- int rate = combine_quad(&data[2 + 12 * i]);
-
- fp->rate_table[fp->nr_rates] = rate;
- if (!fp->rate_min || rate < fp->rate_min)
- fp->rate_min = rate;
- if (!fp->rate_max || rate > fp->rate_max)
- fp->rate_max = rate;
- fp->rates |= snd_pcm_rate_to_rate_bit(rate);
- fp->nr_rates++;
- }
+ /* Call the triplet parser again, but this time, fp->rate_table is
+ * allocated, so the rates will be stored */
+ parse_uac2_sample_rate_range(fp, nr_triplets, data);
err_free:
kfree(data);
@@ -323,7 +390,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
break;
case UAC_VERSION_2:
/* fp->channels is already set in this case */
- ret = parse_audio_format_rates_v2(chip, fp, iface);
+ ret = parse_audio_format_rates_v2(chip, fp);
break;
}
@@ -382,7 +449,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
fp->frame_size = framesize;
- ret = parse_audio_format_rates_v2(chip, fp, iface);
+ ret = parse_audio_format_rates_v2(chip, fp);
break;
}
}
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
index a6b0e51..09bd943 100644
--- a/sound/usb/helper.h
+++ b/sound/usb/helper.h
@@ -28,5 +28,9 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
#define snd_usb_get_speed(dev) ((dev)->speed)
#endif
+static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
+{
+ return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber;
+}
#endif /* __USBAUDIO_HELPER_H */
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 4678564..b9c2bc6 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -434,7 +434,7 @@ static void snd_usbmidi_maudio_broken_running_status_input(
u8 cin = buffer[i] & 0x0f;
struct usbmidi_in_port *port = &ep->ports[cable];
int length;
-
+
length = snd_usbmidi_cin_length[cin];
if (cin == 0xf && buffer[i + 1] >= 0xf8)
; /* realtime msg: no running status change */
@@ -628,13 +628,13 @@ static struct usb_protocol_ops snd_usbmidi_standard_ops = {
static struct usb_protocol_ops snd_usbmidi_midiman_ops = {
.input = snd_usbmidi_midiman_input,
- .output = snd_usbmidi_standard_output,
+ .output = snd_usbmidi_standard_output,
.output_packet = snd_usbmidi_output_midiman_packet,
};
static struct usb_protocol_ops snd_usbmidi_maudio_broken_running_status_ops = {
.input = snd_usbmidi_maudio_broken_running_status_input,
- .output = snd_usbmidi_standard_output,
+ .output = snd_usbmidi_standard_output,
.output_packet = snd_usbmidi_output_standard_packet,
};
@@ -1248,7 +1248,7 @@ static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint *ep
*/
static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
struct snd_usb_midi_endpoint_info* ep_info,
- struct snd_usb_midi_endpoint* rep)
+ struct snd_usb_midi_endpoint* rep)
{
struct snd_usb_midi_out_endpoint* ep;
unsigned int i;
@@ -1398,7 +1398,7 @@ static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi)
}
static struct snd_rawmidi_substream *snd_usbmidi_find_substream(struct snd_usb_midi* umidi,
- int stream, int number)
+ int stream, int number)
{
struct list_head* list;
@@ -1811,7 +1811,7 @@ static int snd_usbmidi_detect_endpoints(struct snd_usb_midi* umidi,
snd_usbmidi_switch_roland_altsetting(umidi);
if (endpoint[0].out_ep || endpoint[0].in_ep)
- return 0;
+ return 0;
intf = umidi->iface;
if (!intf || intf->num_altsetting < 1)
@@ -1849,7 +1849,7 @@ static int snd_usbmidi_detect_per_port_endpoints(struct snd_usb_midi* umidi,
struct snd_usb_midi_endpoint_info* endpoints)
{
int err, i;
-
+
err = snd_usbmidi_detect_endpoints(umidi, endpoints, MIDI_MAX_ENDPOINTS);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
if (endpoints[i].out_ep)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 03ce971..c166db0 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -26,6 +26,22 @@
*
*/
+/*
+ * TODOs, for both the mixer and the streaming interfaces:
+ *
+ * - support for UAC2 effect units
+ * - support for graphical equalizers
+ * - RANGE and MEM set commands (UAC2)
+ * - RANGE and MEM interrupt dispatchers (UAC2)
+ * - audio channel clustering (UAC2)
+ * - audio sample rate converter units (UAC2)
+ * - proper handling of clock multipliers (UAC2)
+ * - dispatch clock change notifications (UAC2)
+ * - stop PCM streams which use a clock that became invalid
+ * - stop PCM streams which use a clock selector that has changed
+ * - parse available sample rates again when clock sources changed
+ */
+
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/list.h>
@@ -78,39 +94,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 +181,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
*/
@@ -306,49 +291,56 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
{
+ struct snd_usb_audio *chip = cval->mixer->chip;
unsigned char buf[2];
int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
int timeout = 10;
while (timeout-- > 0) {
- if (snd_usb_ctl_msg(cval->mixer->chip->dev,
- usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
- request,
+ if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- validx, cval->mixer->ctrlif | (cval->id << 8),
+ validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
buf, val_len, 100) >= val_len) {
*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
return 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);
+ request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
return -EINVAL;
}
static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
{
- unsigned char buf[14]; /* enough space for one range of 4 bytes */
+ struct snd_usb_audio *chip = cval->mixer->chip;
+ unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */
unsigned char *val;
- int ret;
+ int ret, size;
__u8 bRequest;
- bRequest = (request == UAC_GET_CUR) ?
- UAC2_CS_CUR : UAC2_CS_RANGE;
+ if (request == UAC_GET_CUR) {
+ bRequest = UAC2_CS_CUR;
+ size = sizeof(__u16);
+ } else {
+ bRequest = UAC2_CS_RANGE;
+ size = sizeof(buf);
+ }
+
+ memset(buf, 0, sizeof(buf));
- ret = snd_usb_ctl_msg(cval->mixer->chip->dev,
- usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
- bRequest,
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- validx, cval->mixer->ctrlif | (cval->id << 8),
- buf, sizeof(buf), 1000);
+ validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
+ buf, size, 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, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
return ret;
}
+ /* FIXME: how should we handle multiple triplets here? */
+
switch (request) {
case UAC_GET_CUR:
val = buf;
@@ -419,6 +411,7 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
int request, int validx, int value_set)
{
+ struct snd_usb_audio *chip = cval->mixer->chip;
unsigned char buf[2];
int val_len, timeout = 10;
@@ -441,15 +434,14 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
buf[0] = value_set & 0xff;
buf[1] = (value_set >> 8) & 0xff;
while (timeout-- > 0)
- if (snd_usb_ctl_msg(cval->mixer->chip->dev,
- usb_sndctrlpipe(cval->mixer->chip->dev, 0),
- request,
+ if (snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- validx, cval->mixer->ctrlif | (cval->id << 8),
+ validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
buf, val_len, 100) >= 0)
return 0;
snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
- request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]);
+ request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type, buf[0], buf[1]);
return -EINVAL;
}
@@ -462,6 +454,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)
@@ -594,9 +596,9 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
switch (iterm->type >> 16) {
case UAC_SELECTOR_UNIT:
strcpy(name, "Selector"); return 8;
- case UAC_PROCESSING_UNIT_V1:
+ case UAC1_PROCESSING_UNIT:
strcpy(name, "Process Unit"); return 12;
- case UAC_EXTENSION_UNIT_V1:
+ case UAC1_EXTENSION_UNIT:
strcpy(name, "Ext Unit"); return 8;
case UAC_MIXER_UNIT:
strcpy(name, "Mixer"); return 5;
@@ -631,6 +633,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 +654,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 +675,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)
@@ -677,8 +686,8 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
term->name = uac_selector_unit_iSelector(d);
return 0;
}
- case UAC_PROCESSING_UNIT_V1:
- case UAC_EXTENSION_UNIT_V1: {
+ case UAC1_PROCESSING_UNIT:
+ case UAC1_EXTENSION_UNIT: {
struct uac_processing_unit_descriptor *d = p1;
if (d->bNrInPins) {
id = d->baSourceID[0];
@@ -690,6 +699,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 +725,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 },
};
@@ -739,6 +759,8 @@ static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
*/
static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
{
+ struct snd_usb_audio *chip = cval->mixer->chip;
+
/* for failsafe */
cval->min = default_min;
cval->max = cval->min + 1;
@@ -761,7 +783,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n",
- cval->id, cval->mixer->ctrlif, cval->control, cval->id);
+ cval->id, snd_usb_ctrl_intf(chip), cval->control, cval->id);
return -EINVAL;
}
if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) {
@@ -958,7 +980,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 +992,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 +1011,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 +1048,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 +1076,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 |
@@ -1096,6 +1123,19 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
}
break;
+ case USB_ID(0x046d, 0x0809):
+ case USB_ID(0x046d, 0x0991):
+ /* Most audio usb devices lie about volume resolution.
+ * Most Logitech webcams have res = 384.
+ * Proboly there is some logitech magic behind this number --fishor
+ */
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ snd_printk(KERN_INFO
+ "set resolution quirk: cval->res = 384\n");
+ cval->res = 384;
+ }
+ break;
+
}
snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
@@ -1150,7 +1190,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)
@@ -1175,32 +1215,27 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
}
} else { /* UAC_VERSION_2 */
for (i = 0; i < 30/2; i++) {
- /* From the USB Audio spec v2.0:
- bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
- each containing a set of bit pairs. If a Control is present,
- it must be Host readable. If a certain Control is not
- present then the bit pair must be set to 0b00.
- If a Control is present but read-only, the bit pair must be
- set to 0b01. If a Control is also Host programmable, the bit
- pair must be set to 0b11. The value 0b10 is not allowed. */
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
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 +1427,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 +1559,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 +1654,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 +1673,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 +1682,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 +1764,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 +1818,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,20 +1854,22 @@ 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);
- case UAC_PROCESSING_UNIT_V1:
+ case UAC1_PROCESSING_UNIT:
/* UAC2_EFFECT_UNIT has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_processing_unit(state, unitid, p1);
else
return 0; /* FIXME - effect units not implemented yet */
- case UAC_EXTENSION_UNIT_V1:
+ case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_extension_unit(state, unitid, p1);
@@ -1869,7 +1913,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
struct usb_host_interface *hostif;
void *p;
- hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
+ hostif = mixer->chip->ctrl_intf;
memset(&state, 0, sizeof(state));
state.chip = mixer->chip;
state.mixer = mixer;
@@ -1889,7 +1933,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
p = NULL;
while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) {
if (mixer->protocol == UAC_VERSION_1) {
- struct uac_output_terminal_descriptor_v1 *desc = p;
+ struct uac1_output_terminal_descriptor *desc = p;
if (desc->bLength < sizeof(*desc))
continue; /* invalid descriptor? */
@@ -1912,6 +1956,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;
}
}
@@ -1956,7 +2005,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_iprintf(buffer,
"USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n",
- chip->usb_id, mixer->ctrlif,
+ chip->usb_id, snd_usb_ctrl_intf(chip),
mixer->ignore_ctl_error);
snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
@@ -2074,7 +2123,7 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
int buffer_length;
unsigned int epnum;
- hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
+ hostif = mixer->chip->ctrl_intf;
/* we need one interrupt input endpoint */
if (get_iface_desc(hostif)->bNumEndpoints < 1)
return 0;
@@ -2117,7 +2166,6 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
if (!mixer)
return -ENOMEM;
mixer->chip = chip;
- mixer->ctrlif = ctrlif;
mixer->ignore_ctl_error = ignore_error;
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems),
GFP_KERNEL);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 1301238..26c636c 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -3,7 +3,6 @@
struct usb_mixer_interface {
struct snd_usb_audio *chip;
- unsigned int ctrlif;
struct list_head list;
unsigned int ignore_ctl_error;
struct urb *urb;
@@ -34,6 +33,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;
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index d93fc89..f1324c4 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -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 */
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 056587d..4568298 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -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
*/
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index 1c931b6..ed3e283 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -7,8 +7,5 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt);
-int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
- struct usb_host_interface *alts,
- struct audioformat *fmt, int rate);
#endif /* __USBAUDIO_PCM_H */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index b45e54c..9a9da09 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -32,6 +32,7 @@
#include "helper.h"
#include "endpoint.h"
#include "pcm.h"
+#include "clock.h"
/*
* handle the quirks for the contained interfaces
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 06ebf24..24d3319 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -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 */
};
/*