diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
index 274da38f333d..1bb6e5034b58 100644
--- a/drivers/usb/host/xhci-sideband.c
+++ b/drivers/usb/host/xhci-sideband.c
@@ -9,6 +9,7 @@
*/
#include <linux/usb/xhci-sideband.h>
+#include <linux/dmapool.h>
#include "xhci.h"
@@ -67,6 +68,7 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e
xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
ep->sideband = NULL;
+ ep->priv_seg_pool = NULL;
sb->eps[ep->ep_index] = NULL;
}
@@ -113,6 +115,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
* xhci_sideband_add_endpoint - add endpoint to sideband access list
* @sb: sideband instance for this usb device
* @host_ep: usb host endpoint
+ * @pool: dma pool to allocate this endpoint's ring segments from, or NULL
+ * to leave the endpoint's current pool selection untouched
*
* Adds an endpoint to the list of sideband accessed endpoints for this usb
* device.
@@ -123,7 +127,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
*/
int
xhci_sideband_add_endpoint(struct xhci_sideband *sb,
- struct usb_host_endpoint *host_ep)
+ struct usb_host_endpoint *host_ep,
+ struct dma_pool *pool)
{
struct xhci_virt_ep *ep;
unsigned int ep_index;@@ -153,6 +158,9 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb,
ep->sideband = sb;
sb->eps[ep_index] = ep;
+ if (pool)
+ ep->priv_seg_pool = pool;
+
return 0;
}
EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
@@ -288,6 +296,7 @@ EXPORT_SYMBOL_GPL(xhci_sideband_check);
* xhci_sideband_create_interrupter - creates a new interrupter for this sideband
* @sb: sideband instance for this usb device
* @num_seg: number of event ring segments to allocate
+ * @pool: dma pool to allocate the interrupter's event ring segments from
* @ip_autoclear: IP autoclearing support such as MSI implemented
*
* Sets up a xhci interrupter that can be used for this sideband accessed usb
@@ -301,7 +310,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_check);
*/
int
xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
- bool ip_autoclear, u32 imod_interval, int intr_num)
+ struct dma_pool *pool, bool ip_autoclear,
+ u32 imod_interval, int intr_num)
{
if (!sb || !sb->xhci)
return -ENODEV;@@ -315,7 +325,7 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
return -EBUSY;
sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
- num_seg, NULL,
+ num_seg, pool,
imod_interval, intr_num);
if (!sb->ir)
return -ENOMEM;
@@ -370,6 +380,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
/**
* xhci_sideband_register - register a sideband for a usb device
* @intf: usb interface associated with the sideband device
+ * @type: xHCI sideband type
+ * @notify_client: callback for xHCI sideband sequences
*
* Allows for clients to utilize XHCI interrupters and fetch transfer and event
* ring parameters for executing data transfers.
@@ -436,6 +448,15 @@ EXPORT_SYMBOL_GPL(xhci_sideband_register);
* After this the endpoint and interrupter event buffers should no longer
* be accessed via sideband. The xhci driver can now take over handling
* the buffers.
+ * Any transfer ring allocated from a client supplied dma pool is freed here
+ * as well, as the client is not expected to keep that pool alive any longer
+ * than this call. This includes rings of endpoints already removed with
+ * xhci_sideband_remove_endpoint(), which xhci would otherwise only free once
+ * the device is reconfigured or torn down, i.e. after the client is gone.
+ *
+ * The caller must ensure the usb device is no longer streaming through the
+ * normal, non-sideband path when calling this, as the freed rings are still
+ * referenced by the endpoint contexts until xhci reconfigures the device.
*/
void
xhci_sideband_unregister(struct xhci_sideband *sb)
@@ -458,6 +479,17 @@ xhci_sideband_unregister(struct xhci_sideband *sb)
if (sb->eps[i])
__xhci_sideband_remove_endpoint(sb, sb->eps[i]);
+ spin_lock_irq(&xhci->lock);
+ for (i = 0; i < EP_CTX_PER_DEV; i++) {
+ struct xhci_ring *ring = vdev->eps[i].ring;
+
+ if (ring && ring->segment_pool != xhci->segment_pool) {
+ xhci_ring_free(xhci, ring);
+ vdev->eps[i].ring = NULL;
+ }
+ }
+ spin_unlock_irq(&xhci->lock);
+
__xhci_sideband_remove_interrupter(sb);
sb->vdev = NULL;diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 15ce0bb7aa3f..1353d6fa2776 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -19,6 +19,7 @@
#include <linux/usb/hcd.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/io-64-nonatomic-hi-lo.h>
+#include <linux/usb/xhci-sideband.h>
/* Code sharing between pci-quirks and xhci hcd */
#include "xhci-ext-caps.h"
@@ -740,8 +741,6 @@ struct xhci_interval_bw_table {
unsigned int ss_bw_out;
};
-#define EP_CTX_PER_DEV 31
-
struct xhci_virt_device {
int slot_id;
struct usb_device *udev;@@ -1255,14 +1254,11 @@ static inline const char *xhci_trb_type_string(u8 type)
#define NEC_FW_MAJOR(p) (((p) >> 8) & 0xff)
/*
- * TRBS_PER_SEGMENT must be a multiple of 4,
- * since the command ring is 64-byte aligned.
- * It must also be greater than 16.
+ * TRBS_PER_SEGMENT and TRB_SEGMENT_SIZE are defined in
+ * <linux/usb/xhci-sideband.h>, shared with sideband client drivers.
*/
-#define TRBS_PER_SEGMENT 256
/* Allow two commands + a link TRB, along with any reserved command TRBs */
#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3)
-#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT*16)
#define TRB_SEGMENT_SHIFT (ilog2(TRB_SEGMENT_SIZE))
/* TRB buffer pointers can't cross 64KB boundaries */
#define TRB_MAX_BUFF_SHIFT 16
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
index 005257085dcb..6d318e6a3bf6 100644
--- a/include/linux/usb/xhci-sideband.h
+++ b/include/linux/usb/xhci-sideband.h
@@ -13,7 +13,19 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
-#define EP_CTX_PER_DEV 31 /* FIXME defined twice, from xhci.h */
+/*
+ * Constants shared with the xHCI host driver (drivers/usb/host/xhci.h),
+ * which includes this header for its canonical definitions.
+ */
+#define EP_CTX_PER_DEV 31
+
+/*
+ * TRBS_PER_SEGMENT must be a multiple of 4,
+ * since the command ring is 64-byte aligned.
+ * It must also be greater than 16.
+ */
+#define TRBS_PER_SEGMENT 256
+#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT * 16)
struct xhci_sideband;
@@ -72,7 +84,8 @@ void
xhci_sideband_unregister(struct xhci_sideband *sb);
int
xhci_sideband_add_endpoint(struct xhci_sideband *sb,
- struct usb_host_endpoint *host_ep);
+ struct usb_host_endpoint *host_ep,
+ struct dma_pool *pool);
int
xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
struct usb_host_endpoint *host_ep);
@@ -94,7 +107,8 @@ static inline bool xhci_sideband_check(struct usb_hcd *hcd)
int
xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
- bool ip_autoclear, u32 imod_interval, int intr_num);
+ struct dma_pool *pool, bool ip_autoclear,
+ u32 imod_interval, int intr_num);
void
xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
int
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
index e4bfd43a2488..c94b15423a9a 100644
--- a/sound/usb/qcom/qc_audio_offload.c
+++ b/sound/usb/qcom/qc_audio_offload.c
@@ -7,6 +7,7 @@
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/dma-map-ops.h>
+#include <linux/dmapool.h>
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/module.h>
@@ -131,6 +132,7 @@ struct uaudio_dev {
/* xhci sideband */
struct xhci_sideband *sb;
+ struct dma_pool *segment_pool;
/* SoC USB device */
struct snd_soc_usb_device *sdev;@@ -1140,7 +1142,8 @@ uaudio_endpoint_setup(struct snd_usb_substream *subs,
memcpy(ep_desc, &ep->desc, sizeof(ep->desc));
- ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep);
+ ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep,
+ uadev[card_num].segment_pool);
if (ret < 0) {
dev_err(&subs->dev->dev,
"failed to add data ep to sec intr: %d\n", ret);@@ -1211,8 +1214,9 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
goto exit;
/* event ring */
- ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false,
- 0, uaudio_qdev->data->intr_num);
+ ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1,
+ uadev[card_num].segment_pool,
+ false, 0, uaudio_qdev->data->intr_num);
if (ret < 0) {
dev_err(&subs->dev->dev, "failed to fetch interrupter\n");
goto put_offload;@@ -1786,6 +1790,7 @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
struct usb_interface_descriptor *altsd;
struct usb_host_interface *alts;
struct snd_soc_usb_device *sdev;
+ struct dma_pool *segment_pool;
struct xhci_sideband *sb;
/*
@@ -1804,10 +1809,21 @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
if (!sdev)
return;
+ segment_pool = dma_pool_create("xHCI sideband ring segments",
+ interface_to_usbdev(intf)->bus->sysdev,
+ TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE,
+ TRB_SEGMENT_SIZE);
+ if (!segment_pool)
+ goto free_sdev;
+
sb = xhci_sideband_register(intf, XHCI_SIDEBAND_VENDOR,
uaudio_sideband_notifier);
- if (!sb)
+ if (!sb) {
+ dma_pool_destroy(segment_pool);
goto free_sdev;
+ }
+
+ uadev[chip->card->number].segment_pool = segment_pool;
} else {
sb = uadev[chip->card->number].sb;
sdev = uadev[chip->card->number].sdev;@@ -1844,8 +1860,11 @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
return;
unreg_xhci:
+ segment_pool = uadev[chip->card->number].segment_pool;
xhci_sideband_unregister(sb);
+ dma_pool_destroy(segment_pool);
uadev[chip->card->number].sb = NULL;
+ uadev[chip->card->number].segment_pool = NULL;
free_sdev:
kfree(sdev);
uadev[chip->card->number].sdev = NULL;
@@ -1905,8 +1924,13 @@ static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
* This is to accommodate for devices w/ multiple UAC functions.
*/
if (chip->num_interfaces == 1) {
+ struct dma_pool *segment_pool = dev->segment_pool;
+
snd_soc_usb_disconnect(uaudio_qdev->auxdev->dev.parent, dev->sdev);
xhci_sideband_unregister(dev->sb);
+ dma_pool_destroy(segment_pool);
+ dev->sb = NULL;
+ dev->segment_pool = NULL;
dev->chip = NULL;
kfree(dev->sdev->ppcm_idx);
kfree(dev->sdev);
--
2.34.1