|
19 | 19 | * ADDA nRST = GPIO14 (active low — low holds the card in reset; |
20 | 20 | * shared with the display controller) |
21 | 21 | * |
22 | | - * Console/shell: built-in USB Serial/JTAG (usb_serial), not uart0. |
| 22 | + * Console/shell: uart0 (GPIO43 TX / GPIO44 RX, the DevKitC's "UART" |
| 23 | + * USB connector). The built-in USB Serial/JTAG controller shares the |
| 24 | + * chip's single internal USB PHY with the USB-OTG core; enabling the USB |
| 25 | + * audio device (usb_otg) hands the PHY to OTG, so the "USB" connector is |
| 26 | + * the audio interface and can no longer carry the console. |
| 27 | + * |
| 28 | + * USB audio (UAC2) <-> FPGA I2S, ESP32 is I2S *target* (slave) of the |
| 29 | + * FPGA's media clock (BCLK = 64 fs, WS = fs, 24-bit in 32-bit slots): |
| 30 | + * BCLK = GPIO40 (in, FPGA bclk_dac / bclk_adc) |
| 31 | + * WS = GPIO41 (in, FPGA fs_dac / fs_adc) |
| 32 | + * DIN = GPIO42 (in, FPGA tdm_out[0] -> USB record) |
| 33 | + * DOUT = GPIO39 (out, FPGA tdm_in[0] <- USB playback) |
| 34 | + * GPIO39..42 are the chip's own JTAG pads (unused: FPGA JTAG is bit-banged |
| 35 | + * on GPIO4..7, the ESP itself is debugged over USB-JTAG/UART). |
23 | 36 | * |
24 | 37 | * Display controller (LEDs/buttons/7-segment, 115200 8N1): |
25 | 38 | * TX = GPIO17, RX = GPIO18 (board default uart1 pinmux) |
|
33 | 46 | */ |
34 | 47 |
|
35 | 48 | #include <zephyr/dt-bindings/gpio/gpio.h> |
| 49 | +#include <zephyr/dt-bindings/pinctrl/esp32s3-pinctrl.h> |
| 50 | +#include <zephyr/dt-bindings/usb/audio.h> |
36 | 51 |
|
37 | 52 | &psram0 { |
38 | 53 | size = <DT_SIZE_M(8)>; |
|
55 | 70 | }; |
56 | 71 | }; |
57 | 72 |
|
| 73 | + /* I2S0 as full-duplex target: BCLK and WS are inputs only, so they |
| 74 | + * are muxed with explicit ESP32_PINMUX(pin, <in-signal>, ESP_NOSIG) |
| 75 | + * instead of the stock I2S0_O_BCK_GPIOnn macros, which also connect |
| 76 | + * the peripheral's output signal and enable the pad driver — that |
| 77 | + * would fight the FPGA's clock outputs. The TX (O_) and RX (I_) |
| 78 | + * halves both get the same pads (the GPIO matrix fans one pad out |
| 79 | + * to several input signals); the driver additionally sets |
| 80 | + * sig_loopback so RX follows TX's clock inputs. */ |
| 81 | + i2s0_fpga: i2s0_fpga { |
| 82 | + group1 { |
| 83 | + pinmux = <ESP32_PINMUX(40, ESP_I2S0O_BCK_IN, ESP_NOSIG)>, |
| 84 | + <ESP32_PINMUX(40, ESP_I2S0I_BCK_IN, ESP_NOSIG)>, |
| 85 | + <ESP32_PINMUX(41, ESP_I2S0O_WS_IN, ESP_NOSIG)>, |
| 86 | + <ESP32_PINMUX(41, ESP_I2S0I_WS_IN, ESP_NOSIG)>, |
| 87 | + <I2S0_I_SD_GPIO42>; |
| 88 | + }; |
| 89 | + group2 { |
| 90 | + pinmux = <I2S0_O_SD_GPIO39>; |
| 91 | + output-low; |
| 92 | + }; |
| 93 | + }; |
| 94 | +}; |
| 95 | + |
| 96 | +/* I2S0 <-> FPGA audio port. usb_audio_i2s.c programs the controller and |
| 97 | + * GDMA channel 0 through the ESP HAL (Zephyr's I2S driver restarts the DMA |
| 98 | + * per block and drops samples in the gap); it only takes reg, clocks and |
| 99 | + * the pinctrl from this node. The node stays disabled so the Zephyr driver |
| 100 | + * is never instantiated, and `dma` stays disabled as in the board DTS. */ |
| 101 | +&i2s0 { |
| 102 | + pinctrl-0 = <&i2s0_fpga>; |
| 103 | + pinctrl-names = "default"; |
| 104 | +}; |
| 105 | + |
| 106 | +/* USB-OTG device controller (already labelled zephyr_udc0 / okay in the |
| 107 | + * board DTS) and the USB Audio Class 2 function on top of it. */ |
| 108 | +&usb_otg { |
| 109 | + status = "okay"; |
| 110 | +}; |
| 111 | + |
| 112 | +&usb_serial { |
| 113 | + status = "disabled"; |
58 | 114 | }; |
59 | 115 |
|
60 | 116 | &spi2 { |
@@ -101,19 +157,9 @@ i2c2: &i2c0 { |
101 | 157 | pinctrl-names = "default"; |
102 | 158 | }; |
103 | 159 |
|
104 | | -/* Console + shell on the ESP32-S3's built-in USB Serial/JTAG controller |
105 | | - * (the "USB" port of the DevKitC, CDC-ACM towards the host) instead of |
106 | | - * uart0 / the UART-bridge port. Zephyr derives ESP_CONSOLE_USB_SERIAL_JTAG |
107 | | - * and the ROM console device number from this chosen node, so no extra |
108 | | - * Kconfig is needed. uart0 stays enabled as a plain peripheral. */ |
109 | | -&usb_serial { |
110 | | - status = "okay"; |
111 | | -}; |
112 | | - |
113 | 160 | / { |
| 161 | + /* Console + shell stay on the board default, uart0 (see header). */ |
114 | 162 | chosen { |
115 | | - zephyr,console = &usb_serial; |
116 | | - zephyr,shell-uart = &usb_serial; |
117 | 163 | zephyr,fpga-spi = &fpga_spi; |
118 | 164 | }; |
119 | 165 |
|
@@ -145,3 +191,79 @@ i2c2: &i2c0 { |
145 | 191 | gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; |
146 | 192 | }; |
147 | 193 | }; |
| 194 | + |
| 195 | +/* USB Audio Class 2 function: 2 in / 2 out, 48 kHz, 24 bit in 3-byte |
| 196 | + * subslots (294 B per Full-Speed packet; 4-byte subslots would need a |
| 197 | + * 392 B RxFIFO, more than the ESP32-S3's DWC2 can spare next to the IN |
| 198 | + * FIFO, see CONFIG_UDC_DWC2_RXFIFO_MAX_PERCENTAGE). The clock is the FPGA's |
| 199 | + * PTP-disciplined media clock: external, free-running, not SOF-locked -> |
| 200 | + * asynchronous endpoints. Rate matching uses implicit feedback: the IN |
| 201 | + * stream carries exactly as many frames per SOF as I2S delivered and the |
| 202 | + * host derives the OUT packet size from that; both directions share the |
| 203 | + * clock, so the playback queue stays level. Entity IDs are the child |
| 204 | + * index + 1 (UAC2_ENTITY_ID()), the order below is therefore significant. */ |
| 205 | +/ { |
| 206 | + uac2_fpga: usb_audio2 { |
| 207 | + compatible = "zephyr,uac2"; |
| 208 | + status = "okay"; |
| 209 | + full-speed; |
| 210 | + audio-function = <AUDIO_FUNCTION_IO_BOX>; |
| 211 | + |
| 212 | + uac_aclk: aclk { |
| 213 | + compatible = "zephyr,uac2-clock-source"; |
| 214 | + clock-type = "external"; |
| 215 | + frequency-control = "read-only"; |
| 216 | + sampling-frequencies = <48000>; |
| 217 | + }; |
| 218 | + |
| 219 | + /* Host -> device (USB playback) ... */ |
| 220 | + usb_out_terminal: usb_out_terminal { |
| 221 | + compatible = "zephyr,uac2-input-terminal"; |
| 222 | + clock-source = <&uac_aclk>; |
| 223 | + terminal-type = <USB_TERMINAL_STREAMING>; |
| 224 | + front-left; |
| 225 | + front-right; |
| 226 | + }; |
| 227 | + |
| 228 | + /* ... leaves through the I2S port into the FPGA (AES67 TX). */ |
| 229 | + fpga_tx_terminal: fpga_tx { |
| 230 | + compatible = "zephyr,uac2-output-terminal"; |
| 231 | + data-source = <&usb_out_terminal>; |
| 232 | + clock-source = <&uac_aclk>; |
| 233 | + terminal-type = <EXTERNAL_TERMINAL_DIGITAL_AUDIO_INTERFACE>; |
| 234 | + }; |
| 235 | + |
| 236 | + /* FPGA (AES67 RX) enters through the I2S port ... */ |
| 237 | + fpga_rx_terminal: fpga_rx { |
| 238 | + compatible = "zephyr,uac2-input-terminal"; |
| 239 | + clock-source = <&uac_aclk>; |
| 240 | + terminal-type = <EXTERNAL_TERMINAL_DIGITAL_AUDIO_INTERFACE>; |
| 241 | + front-left; |
| 242 | + front-right; |
| 243 | + }; |
| 244 | + |
| 245 | + /* ... and goes to the host (USB record). */ |
| 246 | + usb_in_terminal: usb_in_terminal { |
| 247 | + compatible = "zephyr,uac2-output-terminal"; |
| 248 | + data-source = <&fpga_rx_terminal>; |
| 249 | + clock-source = <&uac_aclk>; |
| 250 | + terminal-type = <USB_TERMINAL_STREAMING>; |
| 251 | + }; |
| 252 | + |
| 253 | + as_iso_out: out_interface { |
| 254 | + compatible = "zephyr,uac2-audio-streaming"; |
| 255 | + linked-terminal = <&usb_out_terminal>; |
| 256 | + implicit-feedback; |
| 257 | + subslot-size = <3>; |
| 258 | + bit-resolution = <24>; |
| 259 | + }; |
| 260 | + |
| 261 | + as_iso_in: in_interface { |
| 262 | + compatible = "zephyr,uac2-audio-streaming"; |
| 263 | + linked-terminal = <&usb_in_terminal>; |
| 264 | + implicit-feedback; |
| 265 | + subslot-size = <3>; |
| 266 | + bit-resolution = <24>; |
| 267 | + }; |
| 268 | + }; |
| 269 | +}; |
0 commit comments