diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index eb1357cce876..e8163cb3be0c 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2456,10 +2456,13 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, } } -static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg, - struct dwc2_qh *qh, - struct dwc2_host_chan *chan) +static int dwc2_alloc_qh_dma_aligned_buf(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, + struct dwc2_qtd *qtd, + struct dwc2_host_chan *chan) { + u32 offset; + if (!hsotg->unaligned_cache || chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE) return -ENOMEM; @@ -2471,6 +2474,18 @@ static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg, return -ENOMEM; } + if (!chan->ep_is_in) { + if (qh->do_split) { + offset = chan->xfer_dma - qtd->urb->dma; + memcpy(qh->dw_align_buf, (u8 *)qtd->urb->buf + offset, + (chan->xfer_len > 188 ? 188 : chan->xfer_len)); + } else { + offset = chan->xfer_dma - qtd->urb->dma; + memcpy(qh->dw_align_buf, (u8 *)qtd->urb->buf + offset, + chan->xfer_len); + } + } + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf, DWC2_KMEM_UNALIGNED_BUF_SIZE, DMA_FROM_DEVICE); @@ -2675,10 +2690,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) dwc2_hc_init_xfer(hsotg, chan, qtd); /* For non-dword aligned buffers */ - if (hsotg->params.host_dma && qh->do_split && - chan->ep_is_in && (chan->xfer_dma & 0x3)) { + if (hsotg->params.host_dma && (chan->xfer_dma & 0x3) && + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); - if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) { + if (dwc2_alloc_qh_dma_aligned_buf(hsotg, qh, qtd, chan)) { dev_err(hsotg->dev, "Failed to allocate memory to handle non-aligned buffer\n"); /* Add channel back to free list */ @@ -2692,8 +2707,8 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) } } else { /* - * We assume that DMA is always aligned in non-split - * case or split out case. Warn if not. + * We assume that DMA is always aligned in other case, + * Warn if not. */ WARN_ON_ONCE(hsotg->params.host_dma && (chan->xfer_dma & 0x3)); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 68bbac64b753..9d2b92a3721b 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -730,8 +730,14 @@ static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg, * Note that this will tend to front-load the high speed schedule. * We may eventually want to try to avoid this by either considering * both schedules together or doing some sort of round robin. + * + * For isoc split out, start schedule at the 2 * DWC2_SLICES_PER_UFRAME + * to transfer SSPLIT-begin OUT transaction like EHCI controller. */ - ls_search_slice = 0; + if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in) + ls_search_slice = 2 * DWC2_SLICES_PER_UFRAME; + else + ls_search_slice = 0; while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) { int start_s_uframe;