usb-designware: fix ISO frame scheduling
rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]>
| Newsgroups | gmane.comp.systems.archos.rockbox.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit b616047311294d30f14fd3afc0fe39e809e27e70 Author: Aidan MacDonald <[email protected]> Date: Sat Aug 15 20:23:49 2026 +0100 usb-designware: fix ISO frame scheduling The frame-scheduling logic cherry picked from the 'rockpod' fork [1] was not correct for high-speed hosts and broke USB Audio output. The DWC seems to schedule based on the DWC_DSTS.SOFFN field, which is a 14-bit microframe number for high-speed hosts. For full speed hosts SOFFN is the LSB-aligned 11-bit frame number. For high-speed hosts, USB Audio and iAP use a bInterval of 4 for the ISO endpoint which means the host sends a packet every 8 microframes. Thus, we only receive data on even microframes, which is why adding frame scheduling broke things (and why we got away with not doing it before). For full speed the bInterval is 1, we get a packet each frame and we do need to tell the core to receive on odd frames. This code is still not completely correct for arbitrary ISO endpoints -- for that we would need the function drivers to provide the (micro)frame number on which they want to send or receive. Tested by forcing full speed at the device side by setting USB_DW_DCFG_SPEED=3. USB Audio works fine in both high and full speed modes now. [1] https://github.com/nuxcodes/rockpod/commit/c390dfdbdf1e63396700af222aa33747769cea15 Change-Id: I17d283821cd0861e414c48208ca67f6e98464d7c diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c index d6030caceb..b6f1691176 100644 --- a/firmware/drivers/usb-designware.c +++ b/firmware/drivers/usb-designware.c @@ -730,8 +730,17 @@ static void usb_dw_epstart(int epnum, enum usb_dw_epdir epdir, DWC_EPTSIZ(epnum, epdir) = eptsiz; if (((DWC_EPCTL(epnum, epdir) >> 18) & 0x3) == EPTYP_ISOCHRONOUS) { - /* Schedule the transfer for the next frame. */ - if ((DWC_DSTS >> 8) & 1) + /* + * Handle frame scheduling for isochronous endpoints. + * + * TODO: this needs to take into account the endpoint's bInterval, + * and currently will not work for USB 2.0 endpoints that require + * one packet per microframe. For slower USB 2.0 endpoints data is + * only transferred on *even* frames; SETD1PIDOF is not used. + * This also breaks for USB 1.0 endpoints with bInterval > 1 for + * the same reason. + */ + if (usb_drv_port_speed() || (usb_drv_get_frame_number() & 1)) DWC_EPCTL(epnum, epdir) |= EPENA | nak | SETD0PIDEF; else DWC_EPCTL(epnum, epdir) |= EPENA | nak | SETD1PIDOF; @@ -1524,10 +1533,14 @@ int usb_drv_send(int endpoint, void *ptr, int length) return dw_ep->status; } -int usb_drv_get_frame_number() +int usb_drv_get_frame_number(void) { - // SOFFN is 14 bits, the least significant 3 appear to be some sort of microframe count. - // The USB spec says a frame number is 11 bits. This way we get 1 frame per millisecond, - // just like we're supposed to! - return (DWC_DSTS >> 11) & 0x7FF; + /* + * SOFFN is a 14-bit microframe number for high-speed hosts and + * a plain frame number for full-speed hosts. + */ + if (usb_drv_port_speed()) + return (DWC_DSTS >> 11) & 0x7FF; + else + return (DWC_DSTS >> 8) & 0x3FFF; } -- rockbox-cvs mailing list [email protected] https://lists.haxx.se/mailman/listinfo/rockbox-cvs