diff --git a/gateware/src/rs/hal/src/polysynth.rs b/gateware/src/rs/hal/src/polysynth.rs index 8a167dc8..7bfa8d26 100644 --- a/gateware/src/rs/hal/src/polysynth.rs +++ b/gateware/src/rs/hal/src/polysynth.rs @@ -96,8 +96,11 @@ macro_rules! impl_polysynth { self.registers.midi_read().read().bits() } - pub fn usb_midi_host(&mut self, enable: bool, cfg_id: u8, endpt_id: u8) { + pub fn usb_host_vbus(&mut self, enable: bool) { self.registers.usb_midi_host().write(|w| unsafe { w.host().bit(enable) } ); + } + + pub fn usb_host_midi(&mut self, cfg_id: u8, endpt_id: u8) { self.registers.usb_midi_cfg().write(|w| unsafe { w.value().bits(cfg_id) } ); self.registers.usb_midi_endp().write(|w| unsafe { w.value().bits(endpt_id) } ); } diff --git a/gateware/src/rs/hal/src/tusb322.rs b/gateware/src/rs/hal/src/tusb322.rs index 9de7d392..85acd407 100644 --- a/gateware/src/rs/hal/src/tusb322.rs +++ b/gateware/src/rs/hal/src/tusb322.rs @@ -182,9 +182,12 @@ impl TUSB322Driver { }) } + pub fn disable_term(&mut self) -> Result<(), I2C::Error> { + self.write_register(0x0A, 0x01) + } + pub fn set_mode(&mut self, mode: TUSB322Mode) -> Result<(), I2C::Error> { - // First disable termination - self.write_register(0x0A, 0x01)?; + self.disable_term()?; // Set mode (device/host) let mode_bits = match mode { diff --git a/gateware/src/top/polysyn/fw/src/main.rs b/gateware/src/top/polysyn/fw/src/main.rs index 07a9c153..3268b790 100644 --- a/gateware/src/top/polysyn/fw/src/main.rs +++ b/gateware/src/top/polysyn/fw/src/main.rs @@ -250,12 +250,14 @@ impl App { let pmod = EurorackPmod0::new(peripherals.PMOD0_PERIPH); let i2cdev = I2c0::new(peripherals.I2C0); let pca9635 = Pca9635Driver::new(i2cdev); - let synth = Polysynth0::new(peripherals.SYNTH_PERIPH); let drive_smoother = OnePoleSmoother::new(0.05f32); let reso_smoother = OnePoleSmoother::new(0.05f32); let diffusion_smoother = OnePoleSmoother::new(0.05f32); let touch_controller = MidiTouchController::new(); let cc_mapper = build_cc_mapper(&opts); + let mut synth = Polysynth0::new(peripherals.SYNTH_PERIPH); + synth.usb_host_midi(1, 1); + synth.usb_host_vbus(false); // default, but set anyway... Self { ui: ui::UI::new(opts, TIMER0_ISR_PERIOD_MS, encoder, pca9635, pmod), @@ -338,7 +340,7 @@ fn main() -> ! { let i2cdev = I2c0::new(peripherals.I2C0); let mut tusb322 = TUSB322Driver::new(i2cdev); tusb322.soft_reset().ok(); - tusb322.set_mode(TUSB322Mode::Dfp).ok(); + tusb322.disable_term().ok(); // // Create App instance @@ -364,8 +366,8 @@ fn main() -> ! { let mut last_jack = pmod.jack(); - let mut usb_cc_attached_as_src = false; let mut last_attached_state = AttachedState::NotAttached; + let mut last_opt_host_enabled = false; loop { @@ -379,34 +381,51 @@ fn main() -> ! { let save_opts = app.ui.opts.misc.save_opts.poll(); let wipe_opts = app.ui.opts.misc.wipe_opts.poll(); + // USB Host: Disable DFP terminations if `MISC->usb-host` option is off + // + // This stops us supplying VCONN on the CC pins for e-tagged cables, fixing an + // edge case where we may backpower VBUS due to the following bug on + // early Tiliqua R5.1s: https://github.com/apfaudio/tiliqua-hardware/issues/1 + // + + let opt_host_enabled = app.ui.opts.misc.usb_host.value == UsbHost::On; + if last_opt_host_enabled != opt_host_enabled { + if opt_host_enabled { + tusb322.soft_reset().ok(); + tusb322.set_mode(TUSB322Mode::Dfp).ok(); + } else { + tusb322.disable_term().ok(); + app.synth.usb_host_vbus(false); + } + } + last_opt_host_enabled = opt_host_enabled; + // Type-C hotplugging detection // // Only enable VBUS if the TUSB322 Type-C controller says we are attached as: // - Attached.SRC // - Attached.ACCESSORY && AccessoryType.DebugDfp // - // It seems some USB-C adapters will enumerate as a DebugDfp accessory. Enabling VBUS - // is still safe in this situation, as we can only land in DebugDfp if the CC - // controller is sure we are a DFP. It's not sufficient to just check - // Attached.ACCESSORY as we could also enumerate as an UFP accessory against a host! - // - // This is essentially a safeguard against applying VBUS if we're accidentally connecting host<->host. + // Due to the same hardware bug as above, Tiliqua's host port in DFP mode may + // have the TUSB322I see some rare adapter combinations as `DebugDfp` accessories, + // rather than `Attached.SRC`. To work around this, we still power VBUS on `DebugDfp`. // if let Ok(ctrl) = tusb322.read_connection_status_control() { if let Ok(conn) = tusb322.read_connection_status() { // Only update on valid reads to reduce risk of unintended toggling mid-stream if ctrl.attached_state != last_attached_state { - info!("USB hotplug: {:?} | {:?}", ctrl, conn); + info!("USB CC hotplug: {:?} | {:?}", ctrl, conn); last_attached_state = ctrl.attached_state; } - let debug_dfp = (ctrl.attached_state == AttachedState::AttachedAccessory) && - (conn.accessory == AccessoryType::DebugDfp); - usb_cc_attached_as_src = ctrl.attached_state == AttachedState::AttachedSrc || debug_dfp; + // Check how we are attached + let attached_src = ctrl.attached_state == AttachedState::AttachedSrc; + let attached_debug_dfp = + (ctrl.attached_state == AttachedState::AttachedAccessory) && + (conn.accessory == AccessoryType::DebugDfp); + app.synth.usb_host_vbus(opt_host_enabled && (attached_src || attached_debug_dfp)); } } - let usb_vbus_enabled = usb_cc_attached_as_src && (app.ui.opts.misc.usb_host.value == UsbHost::On); - app.synth.usb_midi_host(usb_vbus_enabled, 1, 1); // // Copy out all the bits of state we need for drawing