Skip to content

Commit 6daf07b

Browse files
authored
Merge pull request #3275 from HiFiPhile/dwc2_iso_incomp
dcd/dwc2: support ISO IN transfer when bInterval > 1
2 parents 35447b7 + 39c7808 commit 6daf07b

File tree

1 file changed

+55
-3
lines changed

1 file changed

+55
-3
lines changed

src/portable/synopsys/dwc2/dcd_dwc2.c

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ typedef struct {
5151
uint16_t total_len;
5252
uint16_t max_size;
5353
uint8_t interval;
54+
uint8_t iso_retry; // ISO retry counter
5455
} xfer_ctl_t;
5556

5657
// This variable is modified from ISR context, so it must be protected by critical section
@@ -261,7 +262,13 @@ static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint
261262

262263
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
263264
xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
264-
xfer->interval = p_endpoint_desc->bInterval;
265+
266+
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
267+
if (dsts.enum_speed == DCFG_SPEED_HIGH) {
268+
xfer->interval = 1 << (p_endpoint_desc->bInterval - 1);
269+
} else {
270+
xfer->interval = p_endpoint_desc->bInterval;
271+
}
265272

266273
// Endpoint control
267274
dwc2_depctl_t depctl = {.value = 0};
@@ -400,7 +407,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
400407
dwc2_depctl_t depctl = {.value = dep->ctl};
401408
depctl.clear_nak = 1;
402409
depctl.enable = 1;
403-
if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
410+
if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS) {
404411
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
405412
const uint32_t odd_now = dsts.frame_number & 1u;
406413
if (odd_now != 0) {
@@ -643,6 +650,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
643650
xfer->buffer = buffer;
644651
xfer->ff = NULL;
645652
xfer->total_len = total_bytes;
653+
xfer->iso_retry = xfer->interval; // Reset ISO retry counter to interval value
646654

647655
// EP0 can only handle one packet
648656
if (epnum == 0) {
@@ -680,6 +688,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
680688
xfer->buffer = NULL;
681689
xfer->ff = ff;
682690
xfer->total_len = total_bytes;
691+
xfer->iso_retry = xfer->interval; // Reset ISO retry counter to interval value
683692

684693
// Schedule packets to be sent within interrupt
685694
// TODO xfer fifo may only available for slave mode
@@ -782,7 +791,7 @@ static void handle_bus_reset(uint8_t rhport) {
782791
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
783792
}
784793

785-
dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
794+
dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
786795
}
787796

788797
static void handle_enum_done(uint8_t rhport) {
@@ -1057,6 +1066,43 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
10571066
}
10581067
}
10591068

1069+
static void handle_incomplete_iso_in(uint8_t rhport) {
1070+
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
1071+
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
1072+
const uint32_t odd_now = dsts.frame_number & 1u;
1073+
1074+
// Loop over all IN endpoints
1075+
const uint8_t ep_count = dwc2_ep_count(dwc2);
1076+
for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
1077+
dwc2_dep_t *epin = &dwc2->epin[epnum];
1078+
dwc2_depctl_t depctl = {.value = epin->diepctl};
1079+
// Read DSTS and DIEPCTLn for all isochronous endpoints. If the current EP is enabled and the read value of
1080+
// DSTS.SOFFN is the targeted uframe number for this EP, then this EP has an incomplete transfer.
1081+
if (depctl.enable && depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && depctl.dpid_iso_odd == odd_now) {
1082+
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
1083+
if (xfer->iso_retry > 0) {
1084+
xfer->iso_retry--;
1085+
// Restart ISO transfe: re-write TSIZ and CTL
1086+
dwc2_ep_tsize_t deptsiz = {.value = 0};
1087+
deptsiz.xfer_size = xfer->total_len;
1088+
deptsiz.packet_count = tu_div_ceil(xfer->total_len, xfer->max_size);
1089+
epin->tsiz = deptsiz.value;
1090+
1091+
if (odd_now) {
1092+
depctl.set_data0_iso_even = 1;
1093+
} else {
1094+
depctl.set_data1_iso_odd = 1;
1095+
}
1096+
epin->diepctl = depctl.value;
1097+
} else {
1098+
// too many retries, give up
1099+
edpt_disable(rhport, epnum | TUSB_DIR_IN_MASK, false);
1100+
dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, 0, XFER_RESULT_FAILED, true);
1101+
}
1102+
}
1103+
}
1104+
}
1105+
10601106
/* Interrupt Hierarchy
10611107
DIEPINT DIEPINT
10621108
\ /
@@ -1156,6 +1202,12 @@ void dcd_int_handler(uint8_t rhport) {
11561202
// IEPINT bit read-only, clear using DIEPINTn
11571203
handle_ep_irq(rhport, TUSB_DIR_IN);
11581204
}
1205+
1206+
// Incomplete isochronous IN transfer interrupt handling.
1207+
if (gintsts & GINTSTS_IISOIXFR) {
1208+
dwc2->gintsts = GINTSTS_IISOIXFR;
1209+
handle_incomplete_iso_in(rhport);
1210+
}
11591211
}
11601212

11611213
#if CFG_TUD_TEST_MODE

0 commit comments

Comments
 (0)