/* PLL Configuration if required */
if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
uint64_t out;
uint32_t t;
uint32_t div2 = 0;
uint32_t in = mclk_freq;
uint32_t int_div, frac_div;
if (mclk_freq > 17000000) {
div2 = 1;
in = mclk_freq / 2;
} else {
div2 = 0;
in = mclk_freq;
}
/* Select target upstream VCO clock layout based on sampling grid framework */
if (sys_fs == 44100) {
out = 180633600ULL;
} else {
out = 196608000ULL;
}
/*
* Execution steps identical to Linux source code:
* 1. do_div calculates remainder into 't', and overwrites 'out' with (out / in)
*/
t = do_div(out, in);
int_div = (uint32_t)out;
/* 2. Scale the extracted remainder factor and extract fractional dividers scaling */
t *= 2048U;
frac_div = do_div(t, in);
uint32_t pll_ctl = (int_div << SGTL5000_PLL_INT_DIV_SHIFT) |
(frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT);
sgtl5000_write_reg(dev, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
/* Apply dynamic state modifications to integrated dividers gates */
sgtl5000_update_reg(dev, SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INPUT_FREQ_DIV2,
div2 ? SGTL5000_INPUT_FREQ_DIV2 : 0);
/* Secure hardware activation power for core VCO structures */
sgtl5000_update_reg(dev, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
/* Required by hardware manual sequence: write clock controls after loop activation */
sgtl5000_write_reg(dev, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
} else {
/* Otherwise, write clock controls before dynamic loop power-down triggers */
sgtl5000_write_reg(dev, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
sgtl5000_update_reg(dev, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, 0);
}