#include <zephyr/init.h>
#include <fsl_clock.h>
#include <fsl_iomuxc.h>
/* Retrieve the base memory address of the linked SAI instance dynamically from DTS */
#define CODEC_SAI_NODE DT_PHANDLE(DT_DRV_INST(0), bus-sai)
#define CODEC_SAI_BASE_ADDR DT_REG_ADDR(CODEC_SAI_NODE)
#define AUDIO_SAI_PERIPHERAL ((I2S_Type *)CODEC_SAI_BASE_ADDR)
/**
* @brief Early system hook to force MCLK generation for the SGTL5000 codec.
*
* This function dynamically determines which SAI instance (SAI1, SAI2, or SAI3)
* is selected in the device tree, enables its clock gates via the NXP SDK HAL,
* and sets up the required bit clock registers to un-block the codec's I2C engine.
*/
static int sgtl5000_force_mclk_init(void)
{
/*
* 1. Dynamic Clock and Pinmux configuration based on the selected SAI instance.
* This covers SAI1, SAI2, and SAI3 peripheral blocks for i.MX RT processors.
*/
if (CODEC_SAI_BASE_ADDR == DT_REG_ADDR(DT_NODELABEL(sai1))) {
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_09_SAI1_MCLK, 0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_09_SAI1_MCLK, 0x10B0U);
CLOCK_EnableClock(kCLOCK_Sai1);
CLOCK_SetMux(kCLOCK_Sai1Mux, 0); /* Select PLL4 (Audio PLL) */
CLOCK_SetDiv(kCLOCK_Sai1PreDiv, 3);
CLOCK_SetDiv(kCLOCK_Sai1Div, 7);
printk("SGTL5000 Patch: Target interface identified as SAI1\n");
} else if (CODEC_SAI_BASE_ADDR == DT_REG_ADDR(DT_NODELABEL(sai2))) {
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B0_00_SAI2_MCLK, 0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B0_00_SAI2_MCLK, 0x10B0U);
CLOCK_EnableClock(kCLOCK_Sai2);
CLOCK_SetMux(kCLOCK_Sai2Mux, 0); /* Select PLL4 (Audio PLL) */
CLOCK_SetDiv(kCLOCK_Sai2PreDiv, 3);
CLOCK_SetDiv(kCLOCK_Sai2Div, 7);
printk("SGTL5000 Patch: Target interface identified as SAI2\n");
} else if (CODEC_SAI_BASE_ADDR == DT_REG_ADDR(DT_NODELABEL(sai3))) {
/* SAI3 MCLK mapping on i.MX RT1024 / RT1020 standard pinmux allocation */
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_00_SAI3_MCLK, 0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_00_SAI3_MCLK, 0x10B0U);
CLOCK_EnableClock(kCLOCK_Sai3);
CLOCK_SetMux(kCLOCK_Sai3Mux, 0); /* Select PLL4 (Audio PLL) */
CLOCK_SetDiv(kCLOCK_Sai3PreDiv, 3);
CLOCK_SetDiv(kCLOCK_Sai3Div, 7);
printk("SGTL5000 Patch: Target interface identified as SAI3\n");
} else {
LOG_ERR("SGTL5000 Patch: Linked SAI base address (0x%08X) is invalid or unsupported",
CODEC_SAI_BASE_ADDR);
return -EINVAL;
}
/*
* 2. Force the Transmitter Bit Clock Enable (BCE) bit.
* This forces the selected SAI peripheral engine to continuously drive the MCLK pin
* before Zephyr reaches the POST_KERNEL device discovery loops.
*/
AUDIO_SAI_PERIPHERAL->TCSR |= I2S_TCSR_BCE_MASK;
printk("SGTL5000 Patch: Continuous MCLK generation un-gated for hardware block at 0x%08X\n",
CODEC_SAI_BASE_ADDR);
return 0;
}
/* Execute during PRE_KERNEL_2 to un-freeze the codec hardware before I2C initialization */
SYS_INIT(sgtl5000_force_mclk_init, PRE_KERNEL_2, 50);