1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
From cab5abd144fc154c140f9f522c3c2f81a94aaa74 Mon Sep 17 00:00:00 2001
From: P33M <P33M@github.com>
Date: Tue, 4 Aug 2015 01:15:20 +0100
Subject: [PATCH 149/171] 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.
---
drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 17 +++++++++++++----
drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 5 ++++-
drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 7 ++++++-
drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 6 ++++--
4 files changed, 27 insertions(+), 8 deletions(-)
--- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
@@ -615,8 +615,11 @@ static int notrace noinline fiq_fsm_do_s
break;
case FIQ_HS_ISOC_SLEEPING:
- state->channel[n].fsm = FIQ_HS_ISOC_TURBO;
- fiq_fsm_restart_channel(state, n, 0);
+ /* Is it time to wake this channel yet? */
+ if (--state->channel[n].uframe_sleeps == 0) {
+ state->channel[n].fsm = FIQ_HS_ISOC_TURBO;
+ fiq_fsm_restart_channel(state, n, 0);
+ }
break;
case FIQ_PER_SSPLIT_QUEUED:
@@ -624,7 +627,7 @@ static int notrace noinline fiq_fsm_do_s
break;
if(!fiq_fsm_tt_in_use(state, num_channels, n)) {
if (!fiq_fsm_too_late(state, n)) {
- fiq_print(FIQDBG_INT, st, "SOF GO %01d", n);
+ fiq_print(FIQDBG_INT, state, "SOF GO %01d", n);
fiq_fsm_restart_channel(state, n, 0);
state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED;
} else {
@@ -1069,8 +1072,14 @@ static int notrace noinline fiq_fsm_do_h
if (fiq_fsm_update_hs_isoc(state, n, hcint)) {
/* more transactions to come */
handled = 1;
- restart = 1;
fiq_print(FIQDBG_INT, state, "HSISO M ");
+ /* For strided transfers, put ourselves to sleep */
+ if (st->hs_isoc_info.stride > 1) {
+ st->uframe_sleeps = st->hs_isoc_info.stride - 1;
+ st->fsm = FIQ_HS_ISOC_SLEEPING;
+ } else {
+ restart = 1;
+ }
} else {
st->fsm = FIQ_HS_ISOC_DONE;
fiq_print(FIQDBG_INT, state, "HSISO F ");
--- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
@@ -260,12 +260,13 @@ struct fiq_dma_blob {
* @iso_frame: Pointer to the array of OTG URB iso_frame_descs.
* @nrframes: Total length of iso_frame_desc array
* @index: Current index (FIQ-maintained)
- *
+ * @stride: Interval in uframes between HS isoc transactions
*/
struct fiq_hs_isoc_info {
struct dwc_otg_hcd_iso_packet_desc *iso_desc;
unsigned int nrframes;
unsigned int index;
+ unsigned int stride;
};
/**
@@ -296,6 +297,8 @@ struct fiq_channel_state {
/* Hardware bug workaround: sometimes channel halt interrupts are
* delayed until the next SOF. Keep track of when we expected to get interrupted. */
unsigned int expected_uframe;
+ /* number of uframes remaining (for interval > 1 HS isoc transfers) before next transfer */
+ unsigned int uframe_sleeps;
/* in/out for communicating number of dma buffers used, or number of ISOC to do */
unsigned int nrpackets;
struct fiq_dma_info dma_info;
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
@@ -1678,6 +1678,9 @@ int fiq_fsm_queue_isoc_transaction(dwc_o
}
}
+ st->hs_isoc_info.stride = qh->interval;
+ st->uframe_sleeps = 0;
+
fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d ", hc->hc_num);
fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32);
fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32);
@@ -1692,9 +1695,11 @@ int fiq_fsm_queue_isoc_transaction(dwc_o
DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) {
/* Prevent queueing near EOF1. Bad things happen if a periodic
- * split transaction is queued very close to EOF.
+ * split transaction is queued very close to EOF. SOF interrupt handler
+ * will wake this channel at the next interrupt.
*/
st->fsm = FIQ_HS_ISOC_SLEEPING;
+ st->uframe_sleeps = 1;
} else {
st->fsm = FIQ_HS_ISOC_TURBO;
st->hcchar_copy.b.chen = 1;
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
@@ -2297,10 +2297,10 @@ void dwc_otg_fiq_unmangle_isoc(dwc_otg_h
dwc_urb->error_count++;
}
}
+ qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, qh->interval * (nr_frames - 1));
+
//printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n",
// __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count);
- hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0);
- release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
}
/**
@@ -2543,6 +2543,8 @@ void dwc_otg_hcd_handle_hc_fsm(dwc_otg_h
* fail.
*/
dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num);
+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
break;
case FIQ_PER_SPLIT_LS_ABORTED:
|