Hello All,
I’m currently facing difficulties getting IMX93 working with WM8962 codec.
We are working in BareMetal ThreadX application, and I’m using the fsl_wm8962 driver directly. Currently our hardware has a SAI_MCLK line being generated externally by oscillator.
Given this last detail, I’m not sure what is the right SAI and wm8962 configuration, for this scenario.
Below I will share one of testing configuration not working.
Pinmux Configuration:
static void SAI1_InitPins(void)
{
IOMUXC_SetPinMux(IOMUXC_PAD_SAI1_TXD0__SAI1_TX_DATA00, 0U);
IOMUXC_SetPinConfig(IOMUXC_PAD_SAI1_TXD0__SAI1_TX_DATA00,
IOMUXC_PAD_DSE(15U));
IOMUXC_SetPinMux(IOMUXC_PAD_SAI1_RXD0__SAI1_RX_DATA00, 0U);
IOMUXC_SetPinConfig(IOMUXC_PAD_SAI1_RXD0__SAI1_RX_DATA00,
IOMUXC_PAD_DSE(15U));
IOMUXC_SetPinMux(IOMUXC_PAD_SAI1_TXC__SAI1_TX_BCLK, 0U);
IOMUXC_SetPinConfig(IOMUXC_PAD_SAI1_TXC__SAI1_TX_BCLK,
IOMUXC_PAD_DSE(15U));
IOMUXC_SetPinMux(IOMUXC_PAD_SAI1_TXFS__SAI1_TX_SYNC, 0U);
IOMUXC_SetPinConfig(IOMUXC_PAD_SAI1_TXFS__SAI1_TX_SYNC,
IOMUXC_PAD_DSE(15U));
/* MCLK is generated by external oscilator */
/* IOMUXC_SetPinMux(IOMUXC_PAD_UART2_RXD__SAI1_MCLK, 0U);
IOMUXC_SetPinConfig(IOMUXC_PAD_UART2_RXD__SAI1_MCLK,
IOMUXC_PAD_DSE(15U)); */
}
Board Configuration:
static void BOARD_AudioClockSetup(void)
{
/*
* AUDIOPLL1/AUDIOPLL1OUT
*
* VCO = (24MHz / rdiv) * (mfi + (mfn / mfd)) = 3,932,160,000 Hz
* Output = VCO / odiv = 393.216 MHz
*/
const fracn_pll_init_t g_audioPllCfg = {
.rdiv = 1,
.mfi = 163,
.mfn = 84,
.mfd = 100,
.odiv = 10
};
/* clang-format off */
const clock_root_config_t lpi2cClkCfg = {
.clockOff = false,
.mux = 0, // 24MHz oscillator source
.div = 1
};
const clock_root_config_t saiClkCfg = {
.clockOff = false,
.mux = 1, // select audiopll1out source(393216000 Hz)
.div = 32 // output 12288000 Hz
//.div = 16 // output 24576000 Hz
};
/* 250MHz DMA clock */
const clock_root_config_t dmaClkCfg = {
.clockOff = false,
.mux = kCLOCK_WAKEUPAXI_ClockRoot_MuxSysPll1Pfd0, // 1000MHz // For edma3 this is different ?
.div = 4
};
// Not Needed, external 24MHz
sai_master_clock_t saiMasterCfg = {
.mclkOutputEnable = false,
};
/* clang-format on */
/* ROM has already initialized PLL */
CLOCK_PllInit(AUDIOPLL, &g_audioPllCfg);
/*I2C */
CLOCK_SetRootClock(kCLOCK_Root_Lpi2c2, &lpi2cClkCfg);
CLOCK_EnableClock(kCLOCK_Lpi2c2);
/* SAI1 Clock Config */
CLOCK_SetRootClock(kCLOCK_Root_Sai1, &saiClkCfg);
CLOCK_EnableClock(kCLOCK_Sai1);
/* Edma Clock Config */
CLOCK_SetRootClock(kCLOCK_Root_WakeupAxi, &dmaClkCfg); // Check this
CLOCK_EnableClock(kCLOCK_Edma1);
//saiMasterCfg.mclkSourceClkHz = CLOCK_GetIpFreq(kCLOCK_Root_Sai1); /* setup source clock for MCLK */
//saiMasterCfg.mclkHz = 24000000U; /* setup target clock of MCLK */
SAI_SetMasterClockConfig(SAI1, &saiMasterCfg);
}
Init SAI:
void InitAudio_SAI(void)
{
/* SAI init */
SAI_Init(SAI1);
SAI_TransferTxCreateHandle(SAI1, &txHandle, SAI_callback, NULL);
/* I2S mode configurations */
SAI_GetClassicI2SConfig(&saiConfig, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask);
saiConfig.syncMode = kSAI_ModeAsync;
saiConfig.masterSlave = kSAI_Slave;
SAI_TransferTxSetConfig(SAI1, &txHandle, &saiConfig);
/* set bit clock divider */
SAI_TxSetBitClockRate(SAI1, 12288000U, kSAI_SampleRate16KHz, kSAI_WordWidth16bits, 2U);
intCtrlHandlerRegister(SAI1_IRQn,(void*)SAI1_DriverIRQHandler,OS_IRQ_PRIO_MAX);
EnableIRQ(SAI1_IRQn);
}
Init Codec and Config:
static wm8962_config_t wm8962Config_16 = {
.i2cConfig = {.codecI2CInstance = BOARD_CODEC_I2C_INSTANCE},
.route =
{
.enableLoopBack = false,
.leftInputPGASource = kWM8962_InputPGASourceInput1,
.rightInputPGASource = kWM8962_InputPGASourceInput1,
.leftInputMixerSource = kWM8962_InputMixerSourceInputPGA,
.rightInputMixerSource = kWM8962_InputMixerSourceInputPGA,
.leftSpeakerPGASource = kWM8962_OutputPGASourceDAC,
.rightSpeakerPGASource = kWM8962_OutputPGASourceDAC,
.leftSpeakerMixerSource = kWM8962_OutputMixerSourceLeftDAC,
.rightSpeakerMixerSource = kWM8962_OutputMixerSourceRightDAC,
.leftHeadphonePGASource = kWM8962_OutputPGASourceDAC,
.rightHeadphonePGASource = kWM8962_OutputPGASourceDAC,
.leftHeadphoneMixerSource = kWM8962_OutputMixerSourceLeftDAC,
.rightHeadphoneMixerSource = kWM8962_OutputMixerSourceRightDAC
},
.slaveAddress = WM8962_I2C_ADDR,
.bus = kWM8962_BusI2S,
.format = {.sampleRate = kWM8962_AudioSampleRate16KHz, .bitWidth = kWM8962_AudioBitWidth16bit},
.fllClock =
{
.fllClockSource = kWM8962_FLLClkSourceMCLK,
.fllReferenceClockFreq = 24000000U,
.fllOutputFreq = 12288000U,
},
//.sysclkSource = kWM8962_SysClkSourceMclk, /* use MCLK pin as sysclk's source */
.sysclkSource = kWM8962_SysClkSourceFLL,
/* sai running as master mode, so codec running as master just have PLL config by codec driver */
.masterSlave = true,
};
status_t Init_Codec(void)
{
wm8962Config_16.i2cConfig.codecI2CSourceClock = CLOCK_GetIpFreq(kCLOCK_Root_Lpi2c2);
wm8962Config_16.format.mclk_HZ = 24000000U;
status_t result = WM8962_Init(&codecHandle,&wm8962Config_16);
if (result != kStatus_Success) {
/* Handle initialization error */
arm64_lowputs("Error Initializing WM8962!\r\n");
return result;
}
else
{
arm64_lowputs("WM8962_Init Successfull!\r\n");
}
#if(1)
// Now configure as slave for actual operation
WM8962_ModifyReg(&codecHandle, WM8962_IFACE0, 1U << 6U, 0U << 6U); // Clear MS bit (slave mode)
arm64_lowputs("WM8962_Init Successful with FLL in slave mode!\r\n");
#endif
PlaySound:
/* xfer structure */
temp = (uint32_t)music;
xfer.data = (uint8_t *)temp;
xfer.dataSize = MUSIC_LEN;
SAI_TransferSendNonBlocking(SAI1, &txHandle, &xfer);
/* Wait until finished */
while (isFinished != true)
{
//tx_thread_sleep(1);
}
arm64_lowputs("\n\r SAI example finished!\n\r ");
Please if you could support with examples of such a configuration I would appreciate a lot.