summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/line6/midi.c2
-rw-r--r--sound/usb/mixer.c26
-rw-r--r--sound/usb/mixer_maps.c3
-rw-r--r--sound/usb/pcm.c9
-rw-r--r--sound/usb/quirks-table.h47
-rw-r--r--sound/usb/usb_audio_qmi_svc.c91
-rw-r--r--sound/usb/usb_audio_qmi_v01.c60
-rw-r--r--sound/usb/usb_audio_qmi_v01.h23
8 files changed, 134 insertions, 127 deletions
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index cebea9b7f769..6a9be1df7851 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -125,7 +125,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
}
usb_fill_int_urb(urb, line6->usbdev,
- usb_sndbulkpipe(line6->usbdev,
+ usb_sndintpipe(line6->usbdev,
line6->properties->ep_ctrl_w),
transfer_buffer, length, midi_sent, line6,
line6->interval);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index deb6baf4f2ca..81e7d4717194 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -355,17 +355,20 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
int validx, int *value_ret)
{
struct snd_usb_audio *chip = cval->head.mixer->chip;
- unsigned char buf[4 + 3 * sizeof(__u32)]; /* enough space for one range */
+ /* enough space for one range */
+ unsigned char buf[sizeof(__u16) + 3 * sizeof(__u32)];
unsigned char *val;
- int idx = 0, ret, size;
+ int idx = 0, ret, val_size, size;
__u8 bRequest;
+ val_size = uac2_ctl_value_size(cval->val_type);
+
if (request == UAC_GET_CUR) {
bRequest = UAC2_CS_CUR;
- size = uac2_ctl_value_size(cval->val_type);
+ size = val_size;
} else {
bRequest = UAC2_CS_RANGE;
- size = sizeof(buf);
+ size = sizeof(__u16) + 3 * val_size;
}
memset(buf, 0, sizeof(buf));
@@ -398,16 +401,17 @@ error:
val = buf + sizeof(__u16);
break;
case UAC_GET_MAX:
- val = buf + sizeof(__u16) * 2;
+ val = buf + sizeof(__u16) + val_size;
break;
case UAC_GET_RES:
- val = buf + sizeof(__u16) * 3;
+ val = buf + sizeof(__u16) + val_size * 2;
break;
default:
return -EINVAL;
}
- *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(val, sizeof(__u16)));
+ *value_ret = convert_signed_value(cval,
+ snd_usb_combine_bytes(val, val_size));
return 0;
}
@@ -970,6 +974,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
}
break;
+ case USB_ID(0x0d8c, 0x0103):
+ if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
+ usb_audio_info(chip,
+ "set volume quirk for CM102-A+/102S+\n");
+ cval->min = -256;
+ }
+ break;
+
case USB_ID(0x0471, 0x0101):
case USB_ID(0x0471, 0x0104):
case USB_ID(0x0471, 0x0105):
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 1f8fb0d904e0..f5cf23ffb35b 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -351,8 +351,11 @@ static struct usbmix_name_map bose_companion5_map[] = {
/*
* Dell usb dock with ALC4020 codec had a firmware problem where it got
* screwed up when zero volume is passed; just skip it as a workaround
+ *
+ * Also the extension unit gives an access error, so skip it as well.
*/
static const struct usbmix_name_map dell_alc4020_map[] = {
+ { 4, NULL }, /* extension unit */
{ 16, NULL },
{ 19, NULL },
{ 0 }
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 4c929106a1bf..07d9ae103988 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -348,6 +348,15 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
alts = &iface->altsetting[1];
goto add_sync_ep;
+ case USB_ID(0x1397, 0x0002):
+ ep = 0x81;
+ iface = usb_ifnum_to_if(dev, 1);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
}
if (attr == USB_ENDPOINT_SYNC_ASYNC &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 8a59d4782a0f..69bf5cf1e91e 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3277,4 +3277,51 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
}
},
+{
+ /*
+ * Bower's & Wilkins PX headphones only support the 48 kHz sample rate
+ * even though it advertises more. The capture interface doesn't work
+ * even on windows.
+ */
+ USB_DEVICE(0x19b5, 0x0021),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
+ },
+ /* Capture */
+ {
+ .ifnum = 1,
+ .type = QUIRK_IGNORE_INTERFACE,
+ },
+ /* Playback */
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 2,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_FILL_MAX |
+ UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x03,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) {
+ 48000
+ }
+ }
+ },
+ }
+ }
+},
+
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index 6969b7f4d409..dc66b6016f8d 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,7 +46,8 @@
/* event ring iova base address */
#define IOVA_BASE 0x1000
-#define IOVA_XFER_RING_BASE (IOVA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
+#define IOVA_DCBA_BASE 0x2000
+#define IOVA_XFER_RING_BASE (IOVA_DCBA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
#define IOVA_XFER_BUF_BASE (IOVA_XFER_RING_BASE + PAGE_SIZE * SNDRV_CARDS * 32)
#define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE)
#define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE)
@@ -81,9 +82,10 @@ struct uaudio_dev {
/* audio control interface */
struct usb_host_interface *ctrl_intf;
unsigned int card_num;
- unsigned int usb_core_id;
atomic_t in_use;
struct kref kref;
+ unsigned long dcba_iova;
+ size_t dcba_size;
wait_queue_head_t disconnect_wq;
/* interface specific */
@@ -100,6 +102,9 @@ struct uaudio_qmi_dev {
struct iommu_domain *domain;
/* list to keep track of available iova */
+ struct list_head dcba_list;
+ size_t dcba_iova_size;
+ unsigned long curr_dcba_iova;
struct list_head xfer_ring_list;
size_t xfer_ring_iova_size;
unsigned long curr_xfer_ring_iova;
@@ -146,6 +151,7 @@ static struct msg_desc uaudio_stream_ind_desc = {
enum mem_type {
MEM_EVENT_RING,
+ MEM_DCBA,
MEM_XFER_RING,
MEM_XFER_BUF,
};
@@ -171,26 +177,6 @@ enum usb_qmi_audio_format {
USB_QMI_PCM_FORMAT_U32_BE,
};
-static enum usb_audio_device_speed_enum_v01
-get_speed_info(enum usb_device_speed udev_speed)
-{
- switch (udev_speed) {
- case USB_SPEED_LOW:
- return USB_AUDIO_DEVICE_SPEED_LOW_V01;
- case USB_SPEED_FULL:
- return USB_AUDIO_DEVICE_SPEED_FULL_V01;
- case USB_SPEED_HIGH:
- return USB_AUDIO_DEVICE_SPEED_HIGH_V01;
- case USB_SPEED_SUPER:
- return USB_AUDIO_DEVICE_SPEED_SUPER_V01;
- case USB_SPEED_SUPER_PLUS:
- return USB_AUDIO_DEVICE_SPEED_SUPER_PLUS_V01;
- default:
- pr_err("%s: udev speed %d\n", __func__, udev_speed);
- return USB_AUDIO_DEVICE_SPEED_INVALID_V01;
- }
-}
-
static unsigned long uaudio_get_iova(unsigned long *curr_iova,
size_t *curr_iova_size, struct list_head *head, size_t size)
{
@@ -290,6 +276,10 @@ static unsigned long uaudio_iommu_map(enum mem_type mtype, phys_addr_t pa,
if (uaudio_qdev->er_phys_addr == pa)
map = false;
break;
+ case MEM_DCBA:
+ va = uaudio_get_iova(&uaudio_qdev->curr_dcba_iova,
+ &uaudio_qdev->dcba_iova_size, &uaudio_qdev->dcba_list, size);
+ break;
case MEM_XFER_RING:
va = uaudio_get_iova(&uaudio_qdev->curr_xfer_ring_iova,
&uaudio_qdev->xfer_ring_iova_size, &uaudio_qdev->xfer_ring_list,
@@ -374,7 +364,10 @@ static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
else
unmap = false;
break;
-
+ case MEM_DCBA:
+ uaudio_put_iova(va, size, &uaudio_qdev->dcba_list,
+ &uaudio_qdev->dcba_iova_size);
+ break;
case MEM_XFER_RING:
uaudio_put_iova(va, size, &uaudio_qdev->xfer_ring_list,
&uaudio_qdev->xfer_ring_iova_size);
@@ -416,7 +409,8 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
void *hdr_ptr;
u8 *xfer_buf;
u32 len, mult, remainder, xfer_buf_len;
- unsigned long va, tr_data_va = 0, tr_sync_va = 0, xfer_buf_va = 0;
+ unsigned long va, tr_data_va = 0, tr_sync_va = 0, dcba_va = 0,
+ xfer_buf_va = 0;
phys_addr_t xhci_pa, xfer_buf_pa;
iface = usb_ifnum_to_if(subs->dev, subs->interface);
@@ -561,13 +555,6 @@ skip_sync_ep:
resp->interrupter_num = uaudio_qdev->intr_num;
resp->interrupter_num_valid = 1;
- ret = usb_get_controller_id(subs->dev);
- if (ret < 0)
- goto err;
-
- resp->controller_num = ret;
- resp->controller_num_valid = 1;
-
/* map xhci data structures PA memory to iova */
/* event ring */
@@ -595,17 +582,33 @@ skip_sync_ep:
resp->xhci_mem_info.evt_ring.size = PAGE_SIZE;
uaudio_qdev->er_phys_addr = xhci_pa;
- resp->speed_info = get_speed_info(subs->dev->speed);
- if (resp->speed_info == USB_AUDIO_DEVICE_SPEED_INVALID_V01)
+ /* dcba */
+ xhci_pa = usb_get_dcba_dma_addr(subs->dev);
+ if (!xhci_pa) {
+ pr_err("%s:failed to get dcba dma address\n", __func__);
goto unmap_er;
+ }
+
+ if (!uadev[card_num].dcba_iova) { /* mappped per usb device */
+ va = uaudio_iommu_map(MEM_DCBA, xhci_pa, PAGE_SIZE);
+ if (!va)
+ goto unmap_er;
+
+ uadev[card_num].dcba_iova = va;
+ uadev[card_num].dcba_size = PAGE_SIZE;
+ }
- resp->speed_info_valid = 1;
+ dcba_va = uadev[card_num].dcba_iova;
+ resp->xhci_mem_info.dcba.va = PREPEND_SID_TO_IOVA(dcba_va,
+ uaudio_qdev->sid);
+ resp->xhci_mem_info.dcba.pa = xhci_pa;
+ resp->xhci_mem_info.dcba.size = PAGE_SIZE;
/* data transfer ring */
xhci_pa = resp->xhci_mem_info.tr_data.pa;
va = uaudio_iommu_map(MEM_XFER_RING, xhci_pa, PAGE_SIZE);
if (!va)
- goto unmap_er;
+ goto unmap_dcba;
tr_data_va = va;
resp->xhci_mem_info.tr_data.va = PREPEND_SID_TO_IOVA(va,
@@ -678,7 +681,6 @@ skip_sync:
}
uadev[card_num].card_num = card_num;
- uadev[card_num].usb_core_id = resp->controller_num;
/* cache intf specific info to use it for unmap and free xfer buf */
uadev[card_num].info[info_idx].data_xfer_ring_va = tr_data_va;
@@ -706,6 +708,8 @@ unmap_sync:
uaudio_iommu_unmap(MEM_XFER_RING, tr_sync_va, PAGE_SIZE);
unmap_data:
uaudio_iommu_unmap(MEM_XFER_RING, tr_data_va, PAGE_SIZE);
+unmap_dcba:
+ uaudio_iommu_unmap(MEM_DCBA, dcba_va, PAGE_SIZE);
unmap_er:
uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
err:
@@ -751,6 +755,11 @@ static void uaudio_dev_cleanup(struct uaudio_dev *dev)
dev->info[if_idx].intf_num, dev->card_num);
}
+ /* iommu_unmap dcba iova for a usb device */
+ uaudio_iommu_unmap(MEM_DCBA, dev->dcba_iova, dev->dcba_size);
+
+ dev->dcba_iova = 0;
+ dev->dcba_size = 0;
dev->num_intf = 0;
/* free interface info */
@@ -799,8 +808,6 @@ static void uaudio_disconnect_cb(struct snd_usb_audio *chip)
pr_debug("%s: sending qmi indication disconnect\n", __func__);
disconnect_ind.dev_event = USB_AUDIO_DEV_DISCONNECT_V01;
disconnect_ind.slot_id = dev->udev->slot_id;
- disconnect_ind.controller_num = dev->usb_core_id;
- disconnect_ind.controller_num_valid = 1;
ret = qmi_send_ind(svc->uaudio_svc_hdl, svc->curr_conn,
&uaudio_stream_ind_desc, &disconnect_ind,
sizeof(disconnect_ind));
@@ -1222,7 +1229,11 @@ static int uaudio_qmi_plat_probe(struct platform_device *pdev)
goto free_domain;
}
- /* initialize xfer ring and xfer buf iova list */
+ /* initialize dcba, xfer ring and xfer buf iova list */
+ INIT_LIST_HEAD(&uaudio_qdev->dcba_list);
+ uaudio_qdev->curr_dcba_iova = IOVA_DCBA_BASE;
+ uaudio_qdev->dcba_iova_size = SNDRV_CARDS * PAGE_SIZE;
+
INIT_LIST_HEAD(&uaudio_qdev->xfer_ring_list);
uaudio_qdev->curr_xfer_ring_iova = IOVA_XFER_RING_BASE;
uaudio_qdev->xfer_ring_iova_size =
diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c
index 4fa8445badde..beff1aaf4981 100644
--- a/sound/usb/usb_audio_qmi_v01.c
+++ b/sound/usb/usb_audio_qmi_v01.c
@@ -1,4 +1,4 @@
- /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -633,46 +633,6 @@ struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
interrupter_num),
},
{
- .data_type = QMI_OPT_FLAG,
- .elem_len = 1,
- .elem_size = sizeof(uint8_t),
- .is_array = NO_ARRAY,
- .tlv_type = 0x1C,
- .offset = offsetof(
- struct qmi_uaudio_stream_resp_msg_v01,
- speed_info_valid),
- },
- {
- .data_type = QMI_SIGNED_4_BYTE_ENUM,
- .elem_len = 1,
- .elem_size = sizeof(enum usb_audio_device_speed_enum_v01),
- .is_array = NO_ARRAY,
- .tlv_type = 0x1C,
- .offset = offsetof(
- struct qmi_uaudio_stream_resp_msg_v01,
- speed_info),
- },
- {
- .data_type = QMI_OPT_FLAG,
- .elem_len = 1,
- .elem_size = sizeof(uint8_t),
- .is_array = NO_ARRAY,
- .tlv_type = 0x1D,
- .offset = offsetof(
- struct qmi_uaudio_stream_resp_msg_v01,
- controller_num_valid),
- },
- {
- .data_type = QMI_UNSIGNED_1_BYTE,
- .elem_len = 1,
- .elem_size = sizeof(uint8_t),
- .is_array = NO_ARRAY,
- .tlv_type = 0x1D,
- .offset = offsetof(
- struct qmi_uaudio_stream_resp_msg_v01,
- controller_num),
- },
- {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.is_array = QMI_COMMON_TLV_TYPE,
@@ -866,24 +826,6 @@ struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
interrupter_num),
},
{
- .data_type = QMI_OPT_FLAG,
- .elem_len = 1,
- .elem_size = sizeof(uint8_t),
- .is_array = NO_ARRAY,
- .tlv_type = 0x19,
- .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
- controller_num_valid),
- },
- {
- .data_type = QMI_UNSIGNED_1_BYTE,
- .elem_len = 1,
- .elem_size = sizeof(uint8_t),
- .is_array = NO_ARRAY,
- .tlv_type = 0x19,
- .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
- controller_num),
- },
- {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.is_array = QMI_COMMON_TLV_TYPE,
diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h
index addc0ed3de2a..f3b6eb05d5f0 100644
--- a/sound/usb/usb_audio_qmi_v01.h
+++ b/sound/usb/usb_audio_qmi_v01.h
@@ -1,4 +1,4 @@
- /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -77,17 +77,6 @@ enum usb_audio_device_indication_enum_v01 {
USB_AUDIO_DEVICE_INDICATION_ENUM_MAX_VAL_V01 = INT_MAX,
};
-enum usb_audio_device_speed_enum_v01 {
- USB_AUDIO_DEVICE_SPEED_ENUM_MIN_VAL_V01 = INT_MIN,
- USB_AUDIO_DEVICE_SPEED_INVALID_V01 = 0,
- USB_AUDIO_DEVICE_SPEED_LOW_V01 = 1,
- USB_AUDIO_DEVICE_SPEED_FULL_V01 = 2,
- USB_AUDIO_DEVICE_SPEED_HIGH_V01 = 3,
- USB_AUDIO_DEVICE_SPEED_SUPER_V01 = 4,
- USB_AUDIO_DEVICE_SPEED_SUPER_PLUS_V01 = 5,
- USB_AUDIO_DEVICE_SPEED_ENUM_MAX_VAL_V01 = INT_MAX,
-};
-
struct qmi_uaudio_stream_req_msg_v01 {
uint8_t enable;
uint32_t usb_token;
@@ -129,12 +118,8 @@ struct qmi_uaudio_stream_resp_msg_v01 {
struct apps_mem_info_v01 xhci_mem_info;
uint8_t interrupter_num_valid;
uint8_t interrupter_num;
- uint8_t speed_info_valid;
- enum usb_audio_device_speed_enum_v01 speed_info;
- uint8_t controller_num_valid;
- uint8_t controller_num;
};
-#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 202
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 191
extern struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
struct qmi_uaudio_stream_ind_msg_v01 {
@@ -158,10 +143,8 @@ struct qmi_uaudio_stream_ind_msg_v01 {
struct apps_mem_info_v01 xhci_mem_info;
uint8_t interrupter_num_valid;
uint8_t interrupter_num;
- uint8_t controller_num_valid;
- uint8_t controller_num;
};
-#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 181
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 177
extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
#endif