Skip to content

Commit 335b05f

Browse files
P33MP33M
P33M
authored and
P33M
committed
dwc_otg: fiq_fsm: Make high-speed isochronous strided transfers work properly
Certain low-bandwidth high-speed USB devices (specialist audio devices, compressed-frame webcams) have packet intervals > 1 microframe. Stride these transfers in the FIQ by using the start-of-frame interrupt to restart the channel at the right time.
1 parent 2a2dc4e commit 335b05f

File tree

4 files changed

+27
-8
lines changed

4 files changed

+27
-8
lines changed

drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,16 +615,19 @@ static int notrace noinline fiq_fsm_do_sof(struct fiq_state *state, int num_chan
615615
break;
616616

617617
case FIQ_HS_ISOC_SLEEPING:
618-
state->channel[n].fsm = FIQ_HS_ISOC_TURBO;
619-
fiq_fsm_restart_channel(state, n, 0);
618+
/* Is it time to wake this channel yet? */
619+
if (--state->channel[n].uframe_sleeps == 0) {
620+
state->channel[n].fsm = FIQ_HS_ISOC_TURBO;
621+
fiq_fsm_restart_channel(state, n, 0);
622+
}
620623
break;
621624

622625
case FIQ_PER_SSPLIT_QUEUED:
623626
if ((hfnum.b.frnum & 0x7) == 5)
624627
break;
625628
if(!fiq_fsm_tt_in_use(state, num_channels, n)) {
626629
if (!fiq_fsm_too_late(state, n)) {
627-
fiq_print(FIQDBG_INT, st, "SOF GO %01d", n);
630+
fiq_print(FIQDBG_INT, state, "SOF GO %01d", n);
628631
fiq_fsm_restart_channel(state, n, 0);
629632
state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED;
630633
} else {
@@ -1069,8 +1072,14 @@ static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_c
10691072
if (fiq_fsm_update_hs_isoc(state, n, hcint)) {
10701073
/* more transactions to come */
10711074
handled = 1;
1072-
restart = 1;
10731075
fiq_print(FIQDBG_INT, state, "HSISO M ");
1076+
/* For strided transfers, put ourselves to sleep */
1077+
if (st->hs_isoc_info.stride > 1) {
1078+
st->uframe_sleeps = st->hs_isoc_info.stride - 1;
1079+
st->fsm = FIQ_HS_ISOC_SLEEPING;
1080+
} else {
1081+
restart = 1;
1082+
}
10741083
} else {
10751084
st->fsm = FIQ_HS_ISOC_DONE;
10761085
fiq_print(FIQDBG_INT, state, "HSISO F ");

drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,13 @@ struct fiq_dma_blob {
260260
* @iso_frame: Pointer to the array of OTG URB iso_frame_descs.
261261
* @nrframes: Total length of iso_frame_desc array
262262
* @index: Current index (FIQ-maintained)
263-
*
263+
* @stride: Interval in uframes between HS isoc transactions
264264
*/
265265
struct fiq_hs_isoc_info {
266266
struct dwc_otg_hcd_iso_packet_desc *iso_desc;
267267
unsigned int nrframes;
268268
unsigned int index;
269+
unsigned int stride;
269270
};
270271

271272
/**
@@ -296,6 +297,8 @@ struct fiq_channel_state {
296297
/* Hardware bug workaround: sometimes channel halt interrupts are
297298
* delayed until the next SOF. Keep track of when we expected to get interrupted. */
298299
unsigned int expected_uframe;
300+
/* number of uframes remaining (for interval > 1 HS isoc transfers) before next transfer */
301+
unsigned int uframe_sleeps;
299302
/* in/out for communicating number of dma buffers used, or number of ISOC to do */
300303
unsigned int nrpackets;
301304
struct fiq_dma_info dma_info;

drivers/usb/host/dwc_otg/dwc_otg_hcd.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,9 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
16781678
}
16791679
}
16801680

1681+
st->hs_isoc_info.stride = qh->interval;
1682+
st->uframe_sleeps = 0;
1683+
16811684
fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d ", hc->hc_num);
16821685
fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32);
16831686
fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32);
@@ -1692,9 +1695,11 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
16921695
DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
16931696
if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) {
16941697
/* Prevent queueing near EOF1. Bad things happen if a periodic
1695-
* split transaction is queued very close to EOF.
1698+
* split transaction is queued very close to EOF. SOF interrupt handler
1699+
* will wake this channel at the next interrupt. *
16961700
*/
16971701
st->fsm = FIQ_HS_ISOC_SLEEPING;
1702+
st->uframe_sleeps = 1;
16981703
} else {
16991704
st->fsm = FIQ_HS_ISOC_TURBO;
17001705
st->hcchar_copy.b.chen = 1;

drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,10 +2297,10 @@ void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd
22972297
dwc_urb->error_count++;
22982298
}
22992299
}
2300+
qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, qh->interval * (nr_frames - 1));
2301+
23002302
//printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n",
23012303
// __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count);
2302-
hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0);
2303-
release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
23042304
}
23052305

23062306
/**
@@ -2543,6 +2543,8 @@ void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num)
25432543
* fail.
25442544
*/
25452545
dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num);
2546+
hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
2547+
release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
25462548
break;
25472549

25482550
case FIQ_PER_SPLIT_LS_ABORTED:

0 commit comments

Comments
 (0)