@@ -115,6 +115,9 @@ SaiHandle::Result SaiHandle::Impl::Init(const SaiHandle::Config& config)
115115 break ;
116116 default : break ;
117117 }
118+ const bool both_blocks_slave = config.a_sync == Config::Sync::SLAVE
119+ && config.b_sync == Config::Sync::SLAVE ;
120+
118121 // Audio Mode A
119122 if (config.a_sync == Config::Sync::MASTER )
120123 {
@@ -128,6 +131,8 @@ SaiHandle::Result SaiHandle::Impl::Init(const SaiHandle::Config& config)
128131 sai_a_handle_.Init .AudioMode
129132 = config.a_dir == Config::Direction::TRANSMIT ? SAI_MODESLAVE_TX
130133 : SAI_MODESLAVE_RX ;
134+ // In external-clock dual-slave mode, Block A follows Block B since pins
135+ // exposed on SOMs are mostly from the Block B.
131136 sai_a_handle_.Init .Synchro = SAI_SYNCHRONOUS ;
132137 }
133138 // Audio Mode B
@@ -143,7 +148,9 @@ SaiHandle::Result SaiHandle::Impl::Init(const SaiHandle::Config& config)
143148 sai_b_handle_.Init .AudioMode
144149 = config.b_dir == Config::Direction::TRANSMIT ? SAI_MODESLAVE_TX
145150 : SAI_MODESLAVE_RX ;
146- sai_b_handle_.Init .Synchro = SAI_SYNCHRONOUS ;
151+ // When no channel is the master, B will remain async.
152+ sai_b_handle_.Init .Synchro
153+ = both_blocks_slave ? SAI_ASYNCHRONOUS : SAI_SYNCHRONOUS ;
147154 }
148155 // Bitdepth / protocol (currently based on bitdepth..)
149156 // TODO probably split these up for better flexibility..
@@ -303,33 +310,47 @@ SaiHandle::Impl::StartDmaTransfer(int32_t* buffer_rx,
303310 buff_size_ = size;
304311 callback_ = callback;
305312
306- // This assumes there will be one master and one slave
307- if (config_.a_sync == Config::Sync::SLAVE )
313+ const bool a_slave = config_.a_sync == Config::Sync::SLAVE ;
314+ const bool b_slave = config_.b_sync == Config::Sync::SLAVE ;
315+
316+ auto start_block
317+ = [&](SAI_HandleTypeDef* hsai, Config::Direction dir) -> bool {
318+ HAL_StatusTypeDef status
319+ = dir == Config::Direction::RECEIVE
320+ ? HAL_SAI_Receive_DMA (hsai, (uint8_t *)buffer_rx, size)
321+ : HAL_SAI_Transmit_DMA (hsai, (uint8_t *)buffer_tx, size);
322+ return status == HAL_OK ;
323+ };
324+
325+ // Start synchronous slave before its source when there is one master/async block.
326+ // In dual-slave external-clock mode, start B first because A is configured synchronous to B.
327+ if (a_slave && b_slave)
308328 {
309- config_.a_dir == Config::Direction::RECEIVE
310- ? HAL_SAI_Receive_DMA (&sai_a_handle_, (uint8_t *)buffer_rx, size)
311- : HAL_SAI_Transmit_DMA (&sai_a_handle_, (uint8_t *)buffer_tx, size);
312- config_.b_dir == Config::Direction::RECEIVE
313- ? HAL_SAI_Receive_DMA (&sai_b_handle_, (uint8_t *)buffer_rx, size)
314- : HAL_SAI_Transmit_DMA (&sai_b_handle_, (uint8_t *)buffer_tx, size);
329+ if (!start_block (&sai_b_handle_, config_.b_dir )
330+ || !start_block (&sai_a_handle_, config_.a_dir ))
331+ return Result::ERR ;
332+ }
333+ else if (a_slave)
334+ {
335+ if (!start_block (&sai_a_handle_, config_.a_dir )
336+ || !start_block (&sai_b_handle_, config_.b_dir ))
337+ return Result::ERR ;
315338 }
316339 else
317340 {
318- config_.b_dir == Config::Direction::RECEIVE
319- ? HAL_SAI_Receive_DMA (&sai_b_handle_, (uint8_t *)buffer_rx, size)
320- : HAL_SAI_Transmit_DMA (&sai_b_handle_, (uint8_t *)buffer_tx, size);
321- config_.a_dir == Config::Direction::RECEIVE
322- ? HAL_SAI_Receive_DMA (&sai_a_handle_, (uint8_t *)buffer_rx, size)
323- : HAL_SAI_Transmit_DMA (&sai_a_handle_, (uint8_t *)buffer_tx, size);
341+ if (!start_block (&sai_b_handle_, config_.b_dir )
342+ || !start_block (&sai_a_handle_, config_.a_dir ))
343+ return Result::ERR ;
324344 }
325345
326346 return Result::OK ;
327347}
328348SaiHandle::Result SaiHandle::Impl::StopDmaTransfer ()
329349{
330- HAL_SAI_DMAStop (&sai_a_handle_);
331- HAL_SAI_DMAStop (&sai_b_handle_);
332- return Result::OK ;
350+ HAL_StatusTypeDef status_a = HAL_SAI_DMAStop (&sai_a_handle_);
351+ HAL_StatusTypeDef status_b = HAL_SAI_DMAStop (&sai_b_handle_);
352+ return (status_a == HAL_OK && status_b == HAL_OK ) ? Result::OK
353+ : Result::ERR ;
333354}
334355
335356float SaiHandle::Impl::GetSampleRate ()
0 commit comments