summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2025-11-19 16:48:15 +0700
committerGitHub <[email protected]>2025-11-19 16:48:15 +0700
commit6daf07bb92d7c7f8204852d80942362d7cea3ce0 (patch)
tree2cff76a882604779e8fa5f2f22d24f87ba06f9c0
parent35447b7e36150ad730854a766feef027c52baf2b (diff)
parent39c78085cc6ab04b4097031b3260b738cc5dcec2 (diff)
Merge pull request #3275 from HiFiPhile/dwc2_iso_incomp
dcd/dwc2: support ISO IN transfer when bInterval > 1
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c58
1 files changed, 55 insertions, 3 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index dc38d7561..53b6821fa 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -51,6 +51,7 @@ typedef struct {
uint16_t total_len;
uint16_t max_size;
uint8_t interval;
+ uint8_t iso_retry; // ISO retry counter
} xfer_ctl_t;
// 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
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
- xfer->interval = p_endpoint_desc->bInterval;
+
+ const dwc2_dsts_t dsts = {.value = dwc2->dsts};
+ if (dsts.enum_speed == DCFG_SPEED_HIGH) {
+ xfer->interval = 1 << (p_endpoint_desc->bInterval - 1);
+ } else {
+ xfer->interval = p_endpoint_desc->bInterval;
+ }
// Endpoint control
dwc2_depctl_t depctl = {.value = 0};
@@ -400,7 +407,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
dwc2_depctl_t depctl = {.value = dep->ctl};
depctl.clear_nak = 1;
depctl.enable = 1;
- if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
+ if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS) {
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
const uint32_t odd_now = dsts.frame_number & 1u;
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
xfer->buffer = buffer;
xfer->ff = NULL;
xfer->total_len = total_bytes;
+ xfer->iso_retry = xfer->interval; // Reset ISO retry counter to interval value
// EP0 can only handle one packet
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
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
+ xfer->iso_retry = xfer->interval; // Reset ISO retry counter to interval value
// Schedule packets to be sent within interrupt
// TODO xfer fifo may only available for slave mode
@@ -782,7 +791,7 @@ static void handle_bus_reset(uint8_t rhport) {
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
}
- dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
+ dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
}
static void handle_enum_done(uint8_t rhport) {
@@ -1057,6 +1066,43 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
}
}
+static void handle_incomplete_iso_in(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ const dwc2_dsts_t dsts = {.value = dwc2->dsts};
+ const uint32_t odd_now = dsts.frame_number & 1u;
+
+ // Loop over all IN endpoints
+ const uint8_t ep_count = dwc2_ep_count(dwc2);
+ for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
+ dwc2_dep_t *epin = &dwc2->epin[epnum];
+ dwc2_depctl_t depctl = {.value = epin->diepctl};
+ // Read DSTS and DIEPCTLn for all isochronous endpoints. If the current EP is enabled and the read value of
+ // DSTS.SOFFN is the targeted uframe number for this EP, then this EP has an incomplete transfer.
+ if (depctl.enable && depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && depctl.dpid_iso_odd == odd_now) {
+ xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
+ if (xfer->iso_retry > 0) {
+ xfer->iso_retry--;
+ // Restart ISO transfe: re-write TSIZ and CTL
+ dwc2_ep_tsize_t deptsiz = {.value = 0};
+ deptsiz.xfer_size = xfer->total_len;
+ deptsiz.packet_count = tu_div_ceil(xfer->total_len, xfer->max_size);
+ epin->tsiz = deptsiz.value;
+
+ if (odd_now) {
+ depctl.set_data0_iso_even = 1;
+ } else {
+ depctl.set_data1_iso_odd = 1;
+ }
+ epin->diepctl = depctl.value;
+ } else {
+ // too many retries, give up
+ edpt_disable(rhport, epnum | TUSB_DIR_IN_MASK, false);
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, 0, XFER_RESULT_FAILED, true);
+ }
+ }
+ }
+}
+
/* Interrupt Hierarchy
DIEPINT DIEPINT
\ /
@@ -1156,6 +1202,12 @@ void dcd_int_handler(uint8_t rhport) {
// IEPINT bit read-only, clear using DIEPINTn
handle_ep_irq(rhport, TUSB_DIR_IN);
}
+
+ // Incomplete isochronous IN transfer interrupt handling.
+ if (gintsts & GINTSTS_IISOIXFR) {
+ dwc2->gintsts = GINTSTS_IISOIXFR;
+ handle_incomplete_iso_in(rhport);
+ }
}
#if CFG_TUD_TEST_MODE