Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: ingenic: Add X1000 audio clocks

The X1000's CGU supplies the I2S system clock to the AIC module
and ultimately the audio codec, represented by the "i2s" clock.
It is a simple mux which can either pass through EXCLK or a PLL
multiplied by a fractional divider (the "i2s_pll" clock).

The AIC contains a separate 1/N divider controlled by the I2S
driver, which generates the bit clock from the system clock.
The frame clock is always fixed to 1/64th of the bit clock.

Signed-off-by: Aidan MacDonald <aidanmacdonald.0x0@gmail.com>
Link: https://lore.kernel.org/r/20221026194345.243007-6-aidanmacdonald.0x0@gmail.com
Reviewed-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

Aidan MacDonald and committed by
Stephen Boyd
662e8ed7 5e5b1005

+70
+70
drivers/clk/ingenic/x1000-cgu.c
··· 8 8 #include <linux/delay.h> 9 9 #include <linux/io.h> 10 10 #include <linux/of.h> 11 + #include <linux/rational.h> 11 12 12 13 #include <dt-bindings/clock/ingenic,x1000-cgu.h> 13 14 ··· 169 168 .is_enabled = x1000_usb_phy_is_enabled, 170 169 }; 171 170 171 + static void 172 + x1000_i2spll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, 173 + unsigned long rate, unsigned long parent_rate, 174 + unsigned int *pm, unsigned int *pn, unsigned int *pod) 175 + { 176 + const unsigned long m_max = GENMASK(pll_info->m_bits - 1, 0); 177 + const unsigned long n_max = GENMASK(pll_info->n_bits - 1, 0); 178 + unsigned long m, n; 179 + 180 + rational_best_approximation(rate, parent_rate, m_max, n_max, &m, &n); 181 + 182 + /* n should not be less than 2*m */ 183 + if (n < 2 * m) 184 + n = 2 * m; 185 + 186 + *pm = m; 187 + *pn = n; 188 + *pod = 1; 189 + } 190 + 191 + static void 192 + x1000_i2spll_set_rate_hook(const struct ingenic_cgu_pll_info *pll_info, 193 + unsigned long rate, unsigned long parent_rate) 194 + { 195 + /* 196 + * Writing 0 causes I2SCDR1.I2SDIV_D to be automatically recalculated 197 + * based on the current value of I2SCDR.I2SDIV_N, which is needed for 198 + * the divider to function correctly. 199 + */ 200 + writel(0, cgu->base + CGU_REG_I2SCDR1); 201 + } 202 + 172 203 static const s8 pll_od_encoding[8] = { 173 204 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3, 174 205 }; ··· 352 319 .gate = { CGU_REG_CLKGR, 25 }, 353 320 }, 354 321 322 + [X1000_CLK_I2SPLLMUX] = { 323 + "i2s_pll_mux", CGU_CLK_MUX, 324 + .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL }, 325 + .mux = { CGU_REG_I2SCDR, 31, 1 }, 326 + }, 327 + 328 + [X1000_CLK_I2SPLL] = { 329 + "i2s_pll", CGU_CLK_PLL, 330 + .parents = { X1000_CLK_I2SPLLMUX }, 331 + .pll = { 332 + .reg = CGU_REG_I2SCDR, 333 + .rate_multiplier = 1, 334 + .m_shift = 13, 335 + .m_bits = 9, 336 + .n_shift = 0, 337 + .n_bits = 13, 338 + .calc_m_n_od = x1000_i2spll_calc_m_n_od, 339 + .set_rate_hook = x1000_i2spll_set_rate_hook, 340 + }, 341 + }, 342 + 343 + [X1000_CLK_I2S] = { 344 + "i2s", CGU_CLK_MUX, 345 + .parents = { X1000_CLK_EXCLK, -1, -1, X1000_CLK_I2SPLL }, 346 + /* 347 + * NOTE: the mux is at bit 30; bit 29 enables the M/N divider. 348 + * Therefore, the divider is disabled when EXCLK is selected. 349 + */ 350 + .mux = { CGU_REG_I2SCDR, 29, 2 }, 351 + }, 352 + 355 353 [X1000_CLK_LCD] = { 356 354 "lcd", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 357 355 .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL }, ··· 488 424 "i2c2", CGU_CLK_GATE, 489 425 .parents = { X1000_CLK_PCLK, -1, -1, -1 }, 490 426 .gate = { CGU_REG_CLKGR, 9 }, 427 + }, 428 + 429 + [X1000_CLK_AIC] = { 430 + "aic", CGU_CLK_GATE, 431 + .parents = { X1000_CLK_EXCLK }, 432 + .gate = { CGU_REG_CLKGR, 11 }, 491 433 }, 492 434 493 435 [X1000_CLK_UART0] = {