usb: gadget: musb: fix short isoc packets with inventra dma
Handling short packets (length < max packet size) in the Inventra DMA engine in the MUSB driver causes the MUSB DMA controller to hang. An example of a problem that is caused by this problem is when streaming video out of a UVC gadget, only the first video frame is transferred. For short packets (mode-0 or mode-1 DMA), MUSB_TXCSR_TXPKTRDY must be set manually by the driver. This was previously done in musb_g_tx (musb_gadget.c), but incorrectly (all csr flags were cleared, and only MUSB_TXCSR_MODE and MUSB_TXCSR_TXPKTRDY were set). Fixing that problem allows some requests to be transferred correctly, but multiple requests were often put together in one USB packet, and caused problems if the packet size was not a multiple of 4. Instead, set MUSB_TXCSR_TXPKTRDY in dma_controller_irq (musbhsdma.c), just like host mode transfers. This topic was originally tackled by Nicolas Boichat [0] [1] and is discussed further at [2] as part of his GSoC project [3]. [0] https://groups.google.com/forum/?hl=en#!topic/beagleboard-gsoc/k8Azwfp75CU [1]b0be3b6cc1
?p=beagleboard-usbsniffer:beagleboard-usbsniffer-kernel.git;a=patch;h=b0be3b6cc195ba732189b04f1d43ec843c3e54c9 [2] http://beagleboard-usbsniffer.blogspot.com/2010/07/musb-isochronous-transfers-fixed.html [3] http://elinux.org/BeagleBoard/GSoC/USBSniffer Fixes:550a7375fe
("USB: Add MUSB and TUSB support") Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Bin Liu <b-liu@ti.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
f17b5f06cb
Коммит
c418fd6c01
|
@ -452,13 +452,10 @@ void musb_g_tx(struct musb *musb, u8 epnum)
|
|||
}
|
||||
|
||||
if (request) {
|
||||
u8 is_dma = 0;
|
||||
bool short_packet = false;
|
||||
|
||||
trace_musb_req_tx(req);
|
||||
|
||||
if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
|
||||
is_dma = 1;
|
||||
csr |= MUSB_TXCSR_P_WZC_BITS;
|
||||
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
|
||||
MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET);
|
||||
|
@ -476,16 +473,8 @@ void musb_g_tx(struct musb *musb, u8 epnum)
|
|||
*/
|
||||
if ((request->zero && request->length)
|
||||
&& (request->length % musb_ep->packet_sz == 0)
|
||||
&& (request->actual == request->length))
|
||||
short_packet = true;
|
||||
&& (request->actual == request->length)) {
|
||||
|
||||
if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) &&
|
||||
(is_dma && (!dma->desired_mode ||
|
||||
(request->actual &
|
||||
(musb_ep->packet_sz - 1)))))
|
||||
short_packet = true;
|
||||
|
||||
if (short_packet) {
|
||||
/*
|
||||
* On DMA completion, FIFO may not be
|
||||
* available yet...
|
||||
|
|
|
@ -346,12 +346,10 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
|||
channel->status = MUSB_DMA_STATUS_FREE;
|
||||
|
||||
/* completed */
|
||||
if ((devctl & MUSB_DEVCTL_HM)
|
||||
&& (musb_channel->transmit)
|
||||
&& ((channel->desired_mode == 0)
|
||||
|| (channel->actual_len &
|
||||
(musb_channel->max_packet_sz - 1)))
|
||||
) {
|
||||
if (musb_channel->transmit &&
|
||||
(!channel->desired_mode ||
|
||||
(channel->actual_len %
|
||||
musb_channel->max_packet_sz))) {
|
||||
u8 epnum = musb_channel->epnum;
|
||||
int offset = musb->io.ep_offset(epnum,
|
||||
MUSB_TXCSR);
|
||||
|
@ -363,11 +361,14 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
|||
*/
|
||||
musb_ep_select(mbase, epnum);
|
||||
txcsr = musb_readw(mbase, offset);
|
||||
txcsr &= ~(MUSB_TXCSR_DMAENAB
|
||||
if (channel->desired_mode == 1) {
|
||||
txcsr &= ~(MUSB_TXCSR_DMAENAB
|
||||
| MUSB_TXCSR_AUTOSET);
|
||||
musb_writew(mbase, offset, txcsr);
|
||||
/* Send out the packet */
|
||||
txcsr &= ~MUSB_TXCSR_DMAMODE;
|
||||
musb_writew(mbase, offset, txcsr);
|
||||
/* Send out the packet */
|
||||
txcsr &= ~MUSB_TXCSR_DMAMODE;
|
||||
txcsr |= MUSB_TXCSR_DMAENAB;
|
||||
}
|
||||
txcsr |= MUSB_TXCSR_TXPKTRDY;
|
||||
musb_writew(mbase, offset, txcsr);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче