clk-tegra-audio.c (6381B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. 4 */ 5 6#include <linux/io.h> 7#include <linux/clk-provider.h> 8#include <linux/of.h> 9#include <linux/of_address.h> 10#include <linux/delay.h> 11#include <linux/export.h> 12#include <linux/clk/tegra.h> 13 14#include "clk.h" 15#include "clk-id.h" 16 17#define AUDIO_SYNC_CLK_I2S0 0x4a0 18#define AUDIO_SYNC_CLK_I2S1 0x4a4 19#define AUDIO_SYNC_CLK_I2S2 0x4a8 20#define AUDIO_SYNC_CLK_I2S3 0x4ac 21#define AUDIO_SYNC_CLK_I2S4 0x4b0 22#define AUDIO_SYNC_CLK_SPDIF 0x4b4 23#define AUDIO_SYNC_CLK_DMIC1 0x560 24#define AUDIO_SYNC_CLK_DMIC2 0x564 25#define AUDIO_SYNC_CLK_DMIC3 0x6b8 26 27#define AUDIO_SYNC_DOUBLER 0x49c 28 29#define PLLA_OUT 0xb4 30 31struct tegra_sync_source_initdata { 32 char *name; 33 unsigned long rate; 34 unsigned long max_rate; 35 int clk_id; 36}; 37 38#define SYNC(_name) \ 39 {\ 40 .name = #_name,\ 41 .clk_id = tegra_clk_ ## _name,\ 42 } 43 44struct tegra_audio_clk_initdata { 45 char *gate_name; 46 char *mux_name; 47 u32 offset; 48 int gate_clk_id; 49 int mux_clk_id; 50}; 51 52#define AUDIO(_name, _offset) \ 53 {\ 54 .gate_name = #_name,\ 55 .mux_name = #_name"_mux",\ 56 .offset = _offset,\ 57 .gate_clk_id = tegra_clk_ ## _name,\ 58 .mux_clk_id = tegra_clk_ ## _name ## _mux,\ 59 } 60 61struct tegra_audio2x_clk_initdata { 62 char *parent; 63 char *gate_name; 64 char *name_2x; 65 char *div_name; 66 int clk_id; 67 int clk_num; 68 u8 div_offset; 69}; 70 71#define AUDIO2X(_name, _num, _offset) \ 72 {\ 73 .parent = #_name,\ 74 .gate_name = #_name"_2x",\ 75 .name_2x = #_name"_doubler",\ 76 .div_name = #_name"_div",\ 77 .clk_id = tegra_clk_ ## _name ## _2x,\ 78 .clk_num = _num,\ 79 .div_offset = _offset,\ 80 } 81 82static DEFINE_SPINLOCK(clk_doubler_lock); 83 84static const char * const mux_audio_sync_clk[] = { "spdif_in_sync", 85 "i2s0_sync", "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", 86 "pll_a_out0", "vimclk_sync", 87}; 88 89static const char * const mux_dmic_sync_clk[] = { "unused", "i2s0_sync", 90 "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "pll_a_out0", 91 "vimclk_sync", 92}; 93 94static struct tegra_sync_source_initdata sync_source_clks[] __initdata = { 95 SYNC(spdif_in_sync), 96 SYNC(i2s0_sync), 97 SYNC(i2s1_sync), 98 SYNC(i2s2_sync), 99 SYNC(i2s3_sync), 100 SYNC(i2s4_sync), 101 SYNC(vimclk_sync), 102}; 103 104static struct tegra_audio_clk_initdata audio_clks[] = { 105 AUDIO(audio0, AUDIO_SYNC_CLK_I2S0), 106 AUDIO(audio1, AUDIO_SYNC_CLK_I2S1), 107 AUDIO(audio2, AUDIO_SYNC_CLK_I2S2), 108 AUDIO(audio3, AUDIO_SYNC_CLK_I2S3), 109 AUDIO(audio4, AUDIO_SYNC_CLK_I2S4), 110 AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF), 111}; 112 113static struct tegra_audio_clk_initdata dmic_clks[] = { 114 AUDIO(dmic1_sync_clk, AUDIO_SYNC_CLK_DMIC1), 115 AUDIO(dmic2_sync_clk, AUDIO_SYNC_CLK_DMIC2), 116 AUDIO(dmic3_sync_clk, AUDIO_SYNC_CLK_DMIC3), 117}; 118 119static struct tegra_audio2x_clk_initdata audio2x_clks[] = { 120 AUDIO2X(audio0, 113, 24), 121 AUDIO2X(audio1, 114, 25), 122 AUDIO2X(audio2, 115, 26), 123 AUDIO2X(audio3, 116, 27), 124 AUDIO2X(audio4, 117, 28), 125 AUDIO2X(spdif, 118, 29), 126}; 127 128static void __init tegra_audio_sync_clk_init(void __iomem *clk_base, 129 struct tegra_clk *tegra_clks, 130 struct tegra_audio_clk_initdata *sync, 131 int num_sync_clks, 132 const char * const *mux_names, 133 int num_mux_inputs) 134{ 135 struct clk *clk; 136 struct clk **dt_clk; 137 struct tegra_audio_clk_initdata *data; 138 int i; 139 140 for (i = 0, data = sync; i < num_sync_clks; i++, data++) { 141 dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks); 142 if (!dt_clk) 143 continue; 144 145 clk = clk_register_mux(NULL, data->mux_name, mux_names, 146 num_mux_inputs, 147 CLK_SET_RATE_NO_REPARENT, 148 clk_base + data->offset, 0, 3, 0, 149 NULL); 150 *dt_clk = clk; 151 152 dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks); 153 if (!dt_clk) 154 continue; 155 156 clk = clk_register_gate(NULL, data->gate_name, data->mux_name, 157 0, clk_base + data->offset, 4, 158 CLK_GATE_SET_TO_DISABLE, NULL); 159 *dt_clk = clk; 160 } 161} 162 163void __init tegra_audio_clk_init(void __iomem *clk_base, 164 void __iomem *pmc_base, struct tegra_clk *tegra_clks, 165 struct tegra_audio_clk_info *audio_info, 166 unsigned int num_plls, unsigned long sync_max_rate) 167{ 168 struct clk *clk; 169 struct clk **dt_clk; 170 int i; 171 172 if (!audio_info || num_plls < 1) { 173 pr_err("No audio data passed to tegra_audio_clk_init\n"); 174 WARN_ON(1); 175 return; 176 } 177 178 for (i = 0; i < num_plls; i++) { 179 struct tegra_audio_clk_info *info = &audio_info[i]; 180 181 dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks); 182 if (dt_clk) { 183 clk = tegra_clk_register_pll(info->name, info->parent, 184 clk_base, pmc_base, 0, info->pll_params, 185 NULL); 186 *dt_clk = clk; 187 } 188 } 189 190 /* PLLA_OUT0 */ 191 dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks); 192 if (dt_clk) { 193 clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", 194 clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, 195 8, 8, 1, NULL); 196 clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", 197 clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | 198 CLK_SET_RATE_PARENT, 0, NULL); 199 *dt_clk = clk; 200 } 201 202 for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) { 203 struct tegra_sync_source_initdata *data; 204 205 data = &sync_source_clks[i]; 206 207 dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks); 208 if (!dt_clk) 209 continue; 210 211 clk = tegra_clk_register_sync_source(data->name, sync_max_rate); 212 *dt_clk = clk; 213 } 214 215 tegra_audio_sync_clk_init(clk_base, tegra_clks, audio_clks, 216 ARRAY_SIZE(audio_clks), mux_audio_sync_clk, 217 ARRAY_SIZE(mux_audio_sync_clk)); 218 219 /* make sure the DMIC sync clocks have a valid parent */ 220 for (i = 0; i < ARRAY_SIZE(dmic_clks); i++) 221 writel_relaxed(1, clk_base + dmic_clks[i].offset); 222 223 tegra_audio_sync_clk_init(clk_base, tegra_clks, dmic_clks, 224 ARRAY_SIZE(dmic_clks), mux_dmic_sync_clk, 225 ARRAY_SIZE(mux_dmic_sync_clk)); 226 227 for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) { 228 struct tegra_audio2x_clk_initdata *data; 229 230 data = &audio2x_clks[i]; 231 dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks); 232 if (!dt_clk) 233 continue; 234 235 clk = clk_register_fixed_factor(NULL, data->name_2x, 236 data->parent, CLK_SET_RATE_PARENT, 2, 1); 237 clk = tegra_clk_register_divider(data->div_name, 238 data->name_2x, clk_base + AUDIO_SYNC_DOUBLER, 239 0, 0, data->div_offset, 1, 0, 240 &clk_doubler_lock); 241 clk = tegra_clk_register_periph_gate(data->gate_name, 242 data->div_name, TEGRA_PERIPH_NO_RESET, 243 clk_base, CLK_SET_RATE_PARENT, data->clk_num, 244 periph_clk_enb_refcnt); 245 *dt_clk = clk; 246 } 247} 248