Skip to content

Commit c5a9ca3

Browse files
author
yupei_lin
committed
usb: dwc2: hcd: fix isoc out transfer with unaligned dma address
From rk3288_linux_release_v2.2.0_20200708 This DWC2 driver has handled the unaligned DMA address problem for urb->transfer_buffer and split in transfer. But it still has problem to handle the isoc out transfer with unaligned DMA address. I test an USB Audio device which supports 24bits 96KHz 3LE format: usb 1-1: new full-speed USB device number 2 using dwc2 usb 1-1: New USB device found, idVendor=21b4, idProduct=0083, bcdDevice= 1.06 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: AudioQuest DragonFly Black v1.5 usb 1-1: Manufacturer: AudioQuest usb 1-1: SerialNumber: AQDFBL0100023815 When play 24bits 96KHz WAV file, noise occurs. The rootcause is that the DWC2 controller use internal DMA to transfer USB audio data, and the DMA address of data buffer must be 4 bytes aligned, otherwise, the dwc2 will fail to transfer the data. In this test case, the USB audio may transfer 572 bytes or 582 bytes in one usb transaction. And one URB contains multiple usb transactions, if the DWC2 transfer the 582 Bytes in the middle of the URB, the DMA address will not be 4 bytes aligned. This patch allocates new aligned buf for isoc out transfer with unaligned DMA address. For isoc split out transfer, this patch sets the start schedule at the 2 * DWC2_SLICES_PER_UFRAME to transfer the SSPLIT-begin OUT transaction like EHCI controller. Without this patch, the SSPLIT-begin OUT transaction starts in the seventh microframe, and this makes the USB HUB unhappy. This patch sets the the SSPLIT-begin OUT transaction starts in the first microframe. Change-Id: I4bca16b1ebdb23d0270e3e51befe32643cd4f2a0
1 parent 127c70a commit c5a9ca3

File tree

2 files changed

+30
-9
lines changed

2 files changed

+30
-9
lines changed

drivers/usb/dwc2/hcd.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,10 +2549,13 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
25492549
}
25502550
}
25512551

2552-
static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
2553-
struct dwc2_qh *qh,
2554-
struct dwc2_host_chan *chan)
2552+
static int dwc2_alloc_qh_dma_aligned_buf(struct dwc2_hsotg *hsotg,
2553+
struct dwc2_qh *qh,
2554+
struct dwc2_qtd *qtd,
2555+
struct dwc2_host_chan *chan)
25552556
{
2557+
u32 offset;
2558+
25562559
if (!hsotg->unaligned_cache ||
25572560
chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE)
25582561
return -ENOMEM;
@@ -2564,6 +2567,18 @@ static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
25642567
return -ENOMEM;
25652568
}
25662569

2570+
if (!chan->ep_is_in) {
2571+
if (qh->do_split) {
2572+
offset = chan->xfer_dma - qtd->urb->dma;
2573+
memcpy(qh->dw_align_buf, (u8 *)qtd->urb->buf + offset,
2574+
(chan->xfer_len > 188 ? 188 : chan->xfer_len));
2575+
} else {
2576+
offset = chan->xfer_dma - qtd->urb->dma;
2577+
memcpy(qh->dw_align_buf, (u8 *)qtd->urb->buf + offset,
2578+
chan->xfer_len);
2579+
}
2580+
}
2581+
25672582
qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf,
25682583
DWC2_KMEM_UNALIGNED_BUF_SIZE,
25692584
DMA_FROM_DEVICE);
@@ -2758,10 +2773,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
27582773
dwc2_hc_init_xfer(hsotg, chan, qtd);
27592774

27602775
/* For non-dword aligned buffers */
2761-
if (hsotg->core_params->dma_enable > 0 && qh->do_split &&
2762-
chan->ep_is_in && (chan->xfer_dma & 0x3)) {
2776+
if (hsotg->core_params->dma_enable > 0 && (chan->xfer_dma & 0x3) &&
2777+
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
27632778
dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
2764-
if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) {
2779+
if (dwc2_alloc_qh_dma_aligned_buf(hsotg, qh, qtd, chan)) {
27652780
dev_err(hsotg->dev,
27662781
"Failed to allocate memory to handle non-aligned buffer\n");
27672782
/* Add channel back to free list */
@@ -2775,8 +2790,8 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
27752790
}
27762791
} else {
27772792
/*
2778-
* We assume that DMA is always aligned in non-split
2779-
* case or split out case. Warn if not.
2793+
* We assume that DMA is always aligned in other case,
2794+
* Warn if not.
27802795
*/
27812796
WARN_ON_ONCE(hsotg->core_params->dma_enable > 0 &&
27822797
(chan->xfer_dma & 0x3));

drivers/usb/dwc2/hcd_queue.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,8 +728,14 @@ static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg,
728728
* Note that this will tend to front-load the high speed schedule.
729729
* We may eventually want to try to avoid this by either considering
730730
* both schedules together or doing some sort of round robin.
731+
*
732+
* For isoc split out, start schedule at the 2 * DWC2_SLICES_PER_UFRAME
733+
* to transfer SSPLIT-begin OUT transaction like EHCI controller.
731734
*/
732-
ls_search_slice = 0;
735+
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in)
736+
ls_search_slice = 2 * DWC2_SLICES_PER_UFRAME;
737+
else
738+
ls_search_slice = 0;
733739

734740
while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) {
735741
int start_s_uframe;

0 commit comments

Comments
 (0)