Paul Cercueil <paul@xxxxxxxxxxxxxxx> writes: > Hi Aidan, > > Le dim. 23 oct. 2022 à 15:56:53 +0100, Aidan MacDonald > <aidanmacdonald.0x0@xxxxxxxxx> a écrit : >> 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@xxxxxxxxx> >> --- >> drivers/clk/ingenic/x1000-cgu.c | 69 +++++++++++++++++++++++++++++++++ >> 1 file changed, 69 insertions(+) >> diff --git a/drivers/clk/ingenic/x1000-cgu.c >> b/drivers/clk/ingenic/x1000-cgu.c >> index b2ce3fb83f54..341276e5e1ef 100644 >> --- a/drivers/clk/ingenic/x1000-cgu.c >> +++ b/drivers/clk/ingenic/x1000-cgu.c >> @@ -8,6 +8,7 @@ >> #include <linux/delay.h> >> #include <linux/io.h> >> #include <linux/of.h> >> +#include <linux/rational.h> >> #include <dt-bindings/clock/ingenic,x1000-cgu.h> >> @@ -168,6 +169,37 @@ static const struct clk_ops x1000_otg_phy_ops = { >> .is_enabled = x1000_usb_phy_is_enabled, >> }; >> +static void >> +x1000_i2spll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, >> + unsigned long rate, unsigned long parent_rate, >> + unsigned int *pm, unsigned int *pn, unsigned int *pod) >> +{ >> + const unsigned long m_max = GENMASK(pll_info->m_bits - 1, 0); >> + const unsigned long n_max = GENMASK(pll_info->n_bits - 1, 0); >> + unsigned long m, n; >> + >> + rational_best_approximation(rate, parent_rate, m_max, n_max, &m, &n); >> + >> + /* n should not be less than 2*m */ >> + if (n < 2 * m) >> + n = 2 * m; >> + >> + *pm = m; >> + *pn = n; >> + *pod = 1; >> +} >> + >> +static void >> +x1000_i2spll_set_rate_hook(const struct ingenic_cgu_pll_info *pll_info, >> + unsigned long rate, unsigned long parent_rate) >> +{ >> + /* >> + * For some reason, the I2S divider doesn't work properly after >> + * updating I2SCDR unless I2SCDR1 is read & written back. >> + */ >> + writel(readl(cgu->base + CGU_REG_I2SCDR1), cgu->base + CGU_REG_I2SCDR1); > > Not fond of the nesting here, just use a variable. > > Besides... According to the documentation, bits 31 and 30 of this register are > misconnected: writing to bit 31 will be reflected in bit 30, and vice-versa. So > this would work only if the bits 30 and 31 have the same value. >From my tests it seems that reads are swapped w.r.t. the documented bit positions, but not writes. I'm assuming this is the case because when I write to the register with bit 30 (I2S_DEN) set, I can change I2SDIV_D freely, but if bit 30 is 0, then I2SDIV_D is replaced by I2SDIV_N/2 like the manual suggests. > And worse than that, where do you actually set the register's value? Because > bits 30/31, if cleared, will automatically compute the M/N values to the I2SCDR > fields, overriding what the driver's .set_rate() callback is doing. I don't initialize the register, but I2S_NEN (bit 31 of I2SCDR1) doesn't cause I2SCDR to be automatically updated like I see with I2SCDR1 when I2S_DEN = 0. It seems setting I2S_NEN to 1 has some other effect on the clock output, but I don't have an oscilloscope so I'm not sure exactly what it is. I'm limited to testing by ear. For example, with f_in = 1008 MHz, M = 7, N = 2500, the output should nominally be 2.8224 MHz (= 64 * 44.1 KHz). Now that works just fine for playing 44.1 KHz audio if I set I2S_NEN to 0, but setting it to 1, I need to increase M to 16 to get things to sound right. I've tested a couple other frequencies but there isn't an obvious pattern to what increase in M is needed (it always seems to need an increase). I don't really care to play around with it because everything works fine if I keep I2S_NEN = I2S_DEN = 0. I've tested that configuration from 8 KHz to 192 KHz at typical audio frequencies and randomly chosen non-standard frequencies, and haven't experienced any issues. > Either we want that, and in that case the I2S clock should be a custom clock > (since it wouldn't need to compute or write M/N), or we don't, and in this case > bits 30/31 of this register should be set. My experience suggests we should set I2S_NEN and I2S_DEN to 0 and calculate output frequency as F_out = F_in * I2SDIV_M / I2SDIV_N. Reading I2SCDR1 and writing the same value back is something I took from the Ingenic BSP kernels. (The manual also suggests doing this.) It worked fine for me so I didn't bother to invesigate it further. The important part actually seems to be refreshing I2SDIV_D to a "reasonable" value, which happens automatically when I2S_DEN = 0. But the automatic calculation only happens when writing the register. Writing 0 to I2SCDR1 solves both problems: the register is properly initialized and I2SDIV_D gets updated automatically. That seems to be the best solution. >> +} >> + >> static const s8 pll_od_encoding[8] = {itt >> 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3, >> }; >> @@ -319,6 +351,37 @@ static const struct ingenic_cgu_clk_info >> x1000_cgu_clocks[] = { >> .gate = { CGU_REG_CLKGR, 25 }, >> }, >> + [X1000_CLK_I2SPLLMUX] = { >> + "i2s_pll_mux", CGU_CLK_MUX, >> + .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL, -1, -1 }, > > If you have only 1 bit you can only have two parents, so you can remove the > -1s. > Thanks, will do. Regards, Aidan >> + .mux = { CGU_REG_I2SCDR, 31, 1 }, >> + }, >> + >> + [X1000_CLK_I2SPLL] = { >> + "i2s_pll", CGU_CLK_PLL, >> + .parents = { X1000_CLK_I2SPLLMUX, -1, -1, -1 }, > > .parents = { X1000_CLK_I2SPLLMUX, }, > >> + .pll = { >> + .reg = CGU_REG_I2SCDR, >> + .rate_multiplier = 1, >> + .m_shift = 13, >> + .m_bits = 9, >> + .n_shift = 0, >> + .n_bits = 13, >> + .calc_m_n_od = x1000_i2spll_calc_m_n_od, >> + .set_rate_hook = x1000_i2spll_set_rate_hook, >> + }, >> + }, >> + >> + [X1000_CLK_I2S] = { >> + "i2s", CGU_CLK_MUX, >> + .parents = { X1000_CLK_EXCLK, -1, -1, X1000_CLK_I2SPLL }, >> + /* >> + * NOTE: the mux is at bit 30; bit 29 enables the M/N divider. >> + * Therefore, the divider is disabled when EXCLK is selected. >> + */ >> + .mux = { CGU_REG_I2SCDR, 29, 2 }, >> + }, >> + >> [X1000_CLK_LCD] = { >> "lcd", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, >> .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL }, >> @@ -426,6 +489,12 @@ static const struct ingenic_cgu_clk_info >> x1000_cgu_clocks[] = { >> .gate = { CGU_REG_CLKGR, 9 }, >> }, >> + [X1000_CLK_AIC] = { >> + "aic", CGU_CLK_GATE, >> + .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, > > .parents = { X1000_CLK_EXCLK, }, > > Cheers, > -Paul > >> + .gate = { CGU_REG_CLKGR, 11 }, >> + }, >> + >> [X1000_CLK_UART0] = { >> "uart0", CGU_CLK_GATE, >> .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, >> -- >> 2.38.1 >>