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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.