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

clk: imx: imx8mp: Add audiomix block control

Unlike the other block control IPs in i.MX8M, the audiomix is mostly a
series of clock gates and muxes. Model it as a large static table of
gates and muxes with one exception, which is the PLL14xx . The PLL14xx
SAI PLL has to be registered separately.

Reviewed-by: Marco Felsch <m.felsch@pengutronix.de>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Fabio Estevam <festevam@gmail.com>
Tested-by: Adam Ford <aford173@gmail.com> #imx8mp-beacon-kit
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com>
Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Marek Vasut <marex@denx.de>
Tested-by: Richard Leitner <richard.leitner@skidata.com>
Signed-off-by: Abel Vesa <abel.vesa@linaro.org>
Link: https://lore.kernel.org/r/20230301163257.49005-2-marex@denx.de

authored by

Marek Vasut and committed by
Abel Vesa
6cd95f7b 27fc5ec6

+278 -1
+1 -1
drivers/clk/imx/Makefile
··· 27 27 28 28 obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o 29 29 obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o 30 - obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o 30 + obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-imx8mp-audiomix.o 31 31 obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o 32 32 33 33 obj-$(CONFIG_CLK_IMX93) += clk-imx93.o
+277
drivers/clk/imx/clk-imx8mp-audiomix.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Driver for i.MX8M Plus Audio BLK_CTRL 4 + * 5 + * Copyright (C) 2022 Marek Vasut <marex@denx.de> 6 + */ 7 + 8 + #include <linux/clk-provider.h> 9 + #include <linux/device.h> 10 + #include <linux/mod_devicetable.h> 11 + #include <linux/module.h> 12 + #include <linux/of.h> 13 + #include <linux/platform_device.h> 14 + 15 + #include <dt-bindings/clock/imx8mp-clock.h> 16 + 17 + #include "clk.h" 18 + 19 + #define CLKEN0 0x000 20 + #define CLKEN1 0x004 21 + #define SAI_MCLK_SEL(n) (0x300 + 4 * (n)) /* n in 0..5 */ 22 + #define PDM_SEL 0x318 23 + #define SAI_PLL_GNRL_CTL 0x400 24 + 25 + #define SAIn_MCLK1_PARENT(n) \ 26 + static const struct clk_parent_data \ 27 + clk_imx8mp_audiomix_sai##n##_mclk1_parents[] = { \ 28 + { \ 29 + .fw_name = "sai"__stringify(n), \ 30 + .name = "sai"__stringify(n) \ 31 + }, { \ 32 + .fw_name = "sai"__stringify(n)"_mclk", \ 33 + .name = "sai"__stringify(n)"_mclk" \ 34 + }, \ 35 + } 36 + 37 + SAIn_MCLK1_PARENT(1); 38 + SAIn_MCLK1_PARENT(2); 39 + SAIn_MCLK1_PARENT(3); 40 + SAIn_MCLK1_PARENT(5); 41 + SAIn_MCLK1_PARENT(6); 42 + SAIn_MCLK1_PARENT(7); 43 + 44 + static const struct clk_parent_data clk_imx8mp_audiomix_sai_mclk2_parents[] = { 45 + { .fw_name = "sai1", .name = "sai1" }, 46 + { .fw_name = "sai2", .name = "sai2" }, 47 + { .fw_name = "sai3", .name = "sai3" }, 48 + { .name = "dummy" }, 49 + { .fw_name = "sai5", .name = "sai5" }, 50 + { .fw_name = "sai6", .name = "sai6" }, 51 + { .fw_name = "sai7", .name = "sai7" }, 52 + { .fw_name = "sai1_mclk", .name = "sai1_mclk" }, 53 + { .fw_name = "sai2_mclk", .name = "sai2_mclk" }, 54 + { .fw_name = "sai3_mclk", .name = "sai3_mclk" }, 55 + { .name = "dummy" }, 56 + { .fw_name = "sai5_mclk", .name = "sai5_mclk" }, 57 + { .fw_name = "sai6_mclk", .name = "sai6_mclk" }, 58 + { .fw_name = "sai7_mclk", .name = "sai7_mclk" }, 59 + { .fw_name = "spdif_extclk", .name = "spdif_extclk" }, 60 + { .name = "dummy" }, 61 + }; 62 + 63 + static const struct clk_parent_data clk_imx8mp_audiomix_pdm_parents[] = { 64 + { .fw_name = "pdm", .name = "pdm" }, 65 + { .name = "sai_pll_out_div2" }, 66 + { .fw_name = "sai1_mclk", .name = "sai1_mclk" }, 67 + { .name = "dummy" }, 68 + }; 69 + 70 + 71 + static const struct clk_parent_data clk_imx8mp_audiomix_pll_parents[] = { 72 + { .fw_name = "osc_24m", .name = "osc_24m" }, 73 + { .name = "dummy" }, 74 + { .name = "dummy" }, 75 + { .name = "dummy" }, 76 + }; 77 + 78 + static const struct clk_parent_data clk_imx8mp_audiomix_pll_bypass_sels[] = { 79 + { .fw_name = "sai_pll", .name = "sai_pll" }, 80 + { .fw_name = "sai_pll_ref_sel", .name = "sai_pll_ref_sel" }, 81 + }; 82 + 83 + #define CLK_GATE(gname, cname) \ 84 + { \ 85 + gname"_cg", \ 86 + IMX8MP_CLK_AUDIOMIX_##cname, \ 87 + { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \ 88 + CLKEN0 + 4 * !!(IMX8MP_CLK_AUDIOMIX_##cname / 32), \ 89 + 1, IMX8MP_CLK_AUDIOMIX_##cname % 32 \ 90 + } 91 + 92 + #define CLK_SAIn(n) \ 93 + { \ 94 + "sai"__stringify(n)"_mclk1_sel", \ 95 + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1_SEL, {}, \ 96 + clk_imx8mp_audiomix_sai##n##_mclk1_parents, \ 97 + ARRAY_SIZE(clk_imx8mp_audiomix_sai##n##_mclk1_parents), \ 98 + SAI_MCLK_SEL(n), 1, 0 \ 99 + }, { \ 100 + "sai"__stringify(n)"_mclk2_sel", \ 101 + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2_SEL, {}, \ 102 + clk_imx8mp_audiomix_sai_mclk2_parents, \ 103 + ARRAY_SIZE(clk_imx8mp_audiomix_sai_mclk2_parents), \ 104 + SAI_MCLK_SEL(n), 4, 1 \ 105 + }, { \ 106 + "sai"__stringify(n)"_ipg_cg", \ 107 + IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG, \ 108 + { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \ 109 + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG \ 110 + }, { \ 111 + "sai"__stringify(n)"_mclk1_cg", \ 112 + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1, \ 113 + { \ 114 + .fw_name = "sai"__stringify(n)"_mclk1_sel", \ 115 + .name = "sai"__stringify(n)"_mclk1_sel" \ 116 + }, NULL, 1, \ 117 + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1 \ 118 + }, { \ 119 + "sai"__stringify(n)"_mclk2_cg", \ 120 + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2, \ 121 + { \ 122 + .fw_name = "sai"__stringify(n)"_mclk2_sel", \ 123 + .name = "sai"__stringify(n)"_mclk2_sel" \ 124 + }, NULL, 1, \ 125 + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2 \ 126 + }, { \ 127 + "sai"__stringify(n)"_mclk3_cg", \ 128 + IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3, \ 129 + { \ 130 + .fw_name = "sai_pll_out_div2", \ 131 + .name = "sai_pll_out_div2" \ 132 + }, NULL, 1, \ 133 + CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3 \ 134 + } 135 + 136 + #define CLK_PDM \ 137 + { \ 138 + "pdm_sel", IMX8MP_CLK_AUDIOMIX_PDM_SEL, {}, \ 139 + clk_imx8mp_audiomix_pdm_parents, \ 140 + ARRAY_SIZE(clk_imx8mp_audiomix_pdm_parents), \ 141 + PDM_SEL, 2, 0 \ 142 + } 143 + 144 + struct clk_imx8mp_audiomix_sel { 145 + const char *name; 146 + int clkid; 147 + const struct clk_parent_data parent; /* For gate */ 148 + const struct clk_parent_data *parents; /* For mux */ 149 + int num_parents; 150 + u16 reg; 151 + u8 width; 152 + u8 shift; 153 + }; 154 + 155 + static struct clk_imx8mp_audiomix_sel sels[] = { 156 + CLK_GATE("asrc", ASRC_IPG), 157 + CLK_GATE("pdm", PDM_IPG), 158 + CLK_GATE("earc", EARC_IPG), 159 + CLK_GATE("ocrama", OCRAMA_IPG), 160 + CLK_GATE("aud2htx", AUD2HTX_IPG), 161 + CLK_GATE("earc_phy", EARC_PHY), 162 + CLK_GATE("sdma2", SDMA2_ROOT), 163 + CLK_GATE("sdma3", SDMA3_ROOT), 164 + CLK_GATE("spba2", SPBA2_ROOT), 165 + CLK_GATE("dsp", DSP_ROOT), 166 + CLK_GATE("dspdbg", DSPDBG_ROOT), 167 + CLK_GATE("edma", EDMA_ROOT), 168 + CLK_GATE("audpll", AUDPLL_ROOT), 169 + CLK_GATE("mu2", MU2_ROOT), 170 + CLK_GATE("mu3", MU3_ROOT), 171 + CLK_PDM, 172 + CLK_SAIn(1), 173 + CLK_SAIn(2), 174 + CLK_SAIn(3), 175 + CLK_SAIn(5), 176 + CLK_SAIn(6), 177 + CLK_SAIn(7) 178 + }; 179 + 180 + static int clk_imx8mp_audiomix_probe(struct platform_device *pdev) 181 + { 182 + struct clk_hw_onecell_data *priv; 183 + struct device *dev = &pdev->dev; 184 + void __iomem *base; 185 + struct clk_hw *hw; 186 + int i; 187 + 188 + priv = devm_kzalloc(dev, 189 + struct_size(priv, hws, IMX8MP_CLK_AUDIOMIX_END), 190 + GFP_KERNEL); 191 + if (!priv) 192 + return -ENOMEM; 193 + 194 + priv->num = IMX8MP_CLK_AUDIOMIX_END; 195 + 196 + base = devm_platform_ioremap_resource(pdev, 0); 197 + if (IS_ERR(base)) 198 + return PTR_ERR(base); 199 + 200 + for (i = 0; i < ARRAY_SIZE(sels); i++) { 201 + if (sels[i].num_parents == 1) { 202 + hw = devm_clk_hw_register_gate_parent_data(dev, 203 + sels[i].name, &sels[i].parent, 0, 204 + base + sels[i].reg, sels[i].shift, 0, NULL); 205 + } else { 206 + hw = devm_clk_hw_register_mux_parent_data_table(dev, 207 + sels[i].name, sels[i].parents, 208 + sels[i].num_parents, 0, 209 + base + sels[i].reg, 210 + sels[i].shift, sels[i].width, 211 + 0, NULL, NULL); 212 + } 213 + 214 + if (IS_ERR(hw)) 215 + return PTR_ERR(hw); 216 + 217 + priv->hws[sels[i].clkid] = hw; 218 + } 219 + 220 + /* SAI PLL */ 221 + hw = devm_clk_hw_register_mux_parent_data_table(dev, 222 + "sai_pll_ref_sel", clk_imx8mp_audiomix_pll_parents, 223 + ARRAY_SIZE(clk_imx8mp_audiomix_pll_parents), 224 + CLK_SET_RATE_NO_REPARENT, base + SAI_PLL_GNRL_CTL, 225 + 0, 2, 0, NULL, NULL); 226 + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw; 227 + 228 + hw = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel", 229 + base + 0x400, &imx_1443x_pll); 230 + if (IS_ERR(hw)) 231 + return PTR_ERR(hw); 232 + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw; 233 + 234 + hw = devm_clk_hw_register_mux_parent_data_table(dev, 235 + "sai_pll_bypass", clk_imx8mp_audiomix_pll_bypass_sels, 236 + ARRAY_SIZE(clk_imx8mp_audiomix_pll_bypass_sels), 237 + CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 238 + base + SAI_PLL_GNRL_CTL, 16, 1, 0, NULL, NULL); 239 + if (IS_ERR(hw)) 240 + return PTR_ERR(hw); 241 + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw; 242 + 243 + hw = devm_clk_hw_register_gate(dev, "sai_pll_out", "sai_pll_bypass", 244 + 0, base + SAI_PLL_GNRL_CTL, 13, 245 + 0, NULL); 246 + if (IS_ERR(hw)) 247 + return PTR_ERR(hw); 248 + priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw; 249 + 250 + hw = devm_clk_hw_register_fixed_factor(dev, "sai_pll_out_div2", 251 + "sai_pll_out", 0, 1, 2); 252 + if (IS_ERR(hw)) 253 + return PTR_ERR(hw); 254 + 255 + return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, 256 + priv); 257 + } 258 + 259 + static const struct of_device_id clk_imx8mp_audiomix_of_match[] = { 260 + { .compatible = "fsl,imx8mp-audio-blk-ctrl" }, 261 + { /* sentinel */ } 262 + }; 263 + MODULE_DEVICE_TABLE(of, clk_imx8mp_audiomix_of_match); 264 + 265 + static struct platform_driver clk_imx8mp_audiomix_driver = { 266 + .probe = clk_imx8mp_audiomix_probe, 267 + .driver = { 268 + .name = "imx8mp-audio-blk-ctrl", 269 + .of_match_table = clk_imx8mp_audiomix_of_match, 270 + }, 271 + }; 272 + 273 + module_platform_driver(clk_imx8mp_audiomix_driver); 274 + 275 + MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 276 + MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller driver"); 277 + MODULE_LICENSE("GPL");