1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4 * Copyright (c) 2024, Danila Tikhonov <danila@jiaxyga.com>
5 */
6
7 #include <linux/clk-provider.h>
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <linux/regmap.h>
11
12 #include <dt-bindings/clock/qcom,sm7150-videocc.h>
13
14 #include "common.h"
15 #include "clk-alpha-pll.h"
16 #include "clk-branch.h"
17 #include "clk-rcg.h"
18 #include "clk-regmap.h"
19 #include "clk-pll.h"
20 #include "gdsc.h"
21
22 enum {
23 DT_BI_TCXO,
24 DT_BI_TCXO_AO,
25 };
26
27 enum {
28 P_BI_TCXO,
29 P_VIDEOCC_PLL0_OUT_EVEN,
30 P_VIDEOCC_PLL0_OUT_MAIN,
31 P_VIDEOCC_PLL0_OUT_ODD,
32 };
33
34 static const struct pll_vco fabia_vco[] = {
35 { 249600000, 2000000000, 0 },
36 { 125000000, 1000000000, 1 },
37 };
38
39 static struct alpha_pll_config videocc_pll0_config = {
40 .l = 0x19,
41 .alpha = 0x0,
42 .config_ctl_val = 0x20485699,
43 .config_ctl_hi_val = 0x00002067,
44 .user_ctl_val = 0x00000001,
45 .user_ctl_hi_val = 0x00004805,
46 .test_ctl_hi_val = 0x40000000,
47 };
48
49 static struct clk_alpha_pll videocc_pll0 = {
50 .offset = 0x42c,
51 .vco_table = fabia_vco,
52 .num_vco = ARRAY_SIZE(fabia_vco),
53 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
54 .clkr = {
55 .hw.init = &(const struct clk_init_data) {
56 .name = "videocc_pll0",
57 .parent_data = &(const struct clk_parent_data) {
58 .index = DT_BI_TCXO,
59 },
60 .num_parents = 1,
61 .ops = &clk_alpha_pll_fabia_ops,
62 },
63 },
64 };
65
66 static const struct parent_map videocc_parent_map_0[] = {
67 { P_BI_TCXO, 0 },
68 { P_VIDEOCC_PLL0_OUT_MAIN, 1 },
69 { P_VIDEOCC_PLL0_OUT_EVEN, 2 },
70 { P_VIDEOCC_PLL0_OUT_ODD, 3 },
71 };
72
73 static const struct clk_parent_data videocc_parent_data_0[] = {
74 { .index = DT_BI_TCXO },
75 { .hw = &videocc_pll0.clkr.hw },
76 { .hw = &videocc_pll0.clkr.hw },
77 { .hw = &videocc_pll0.clkr.hw },
78 };
79
80 static const struct parent_map videocc_parent_map_1[] = {
81 { P_BI_TCXO, 0 },
82 };
83
84 static const struct clk_parent_data videocc_parent_data_1[] = {
85 { .index = DT_BI_TCXO_AO },
86 };
87
88 static const struct freq_tbl ftbl_videocc_iris_clk_src[] = {
89 F(240000000, P_VIDEOCC_PLL0_OUT_MAIN, 2, 0, 0),
90 F(338000000, P_VIDEOCC_PLL0_OUT_MAIN, 2, 0, 0),
91 F(365000000, P_VIDEOCC_PLL0_OUT_MAIN, 2, 0, 0),
92 F(444000000, P_VIDEOCC_PLL0_OUT_MAIN, 2, 0, 0),
93 F(533000000, P_VIDEOCC_PLL0_OUT_MAIN, 2, 0, 0),
94 { }
95 };
96
97 static struct clk_rcg2 videocc_iris_clk_src = {
98 .cmd_rcgr = 0x7f0,
99 .mnd_width = 0,
100 .hid_width = 5,
101 .parent_map = videocc_parent_map_0,
102 .freq_tbl = ftbl_videocc_iris_clk_src,
103 .clkr.hw.init = &(const struct clk_init_data) {
104 .name = "videocc_iris_clk_src",
105 .parent_data = videocc_parent_data_0,
106 .num_parents = ARRAY_SIZE(videocc_parent_data_0),
107 .flags = CLK_SET_RATE_PARENT,
108 .ops = &clk_rcg2_shared_ops,
109 },
110 };
111
112 static const struct freq_tbl ftbl_videocc_xo_clk_src[] = {
113 F(19200000, P_BI_TCXO, 1, 0, 0),
114 { }
115 };
116
117 static struct clk_rcg2 videocc_xo_clk_src = {
118 .cmd_rcgr = 0xa98,
119 .mnd_width = 0,
120 .hid_width = 5,
121 .parent_map = videocc_parent_map_1,
122 .freq_tbl = ftbl_videocc_xo_clk_src,
123 .clkr.hw.init = &(const struct clk_init_data) {
124 .name = "videocc_xo_clk_src",
125 .parent_data = videocc_parent_data_1,
126 .num_parents = ARRAY_SIZE(videocc_parent_data_1),
127 .ops = &clk_rcg2_ops,
128 },
129 };
130
131 static struct clk_branch videocc_iris_ahb_clk = {
132 .halt_reg = 0x8f4,
133 .halt_check = BRANCH_VOTED,
134 .clkr = {
135 .enable_reg = 0x8f4,
136 .enable_mask = BIT(0),
137 .hw.init = &(const struct clk_init_data) {
138 .name = "videocc_iris_ahb_clk",
139 .parent_data = &(const struct clk_parent_data) {
140 .hw = &videocc_iris_clk_src.clkr.hw,
141 },
142 .num_parents = 1,
143 .flags = CLK_SET_RATE_PARENT,
144 .ops = &clk_branch2_ops,
145 },
146 },
147 };
148
149 static struct clk_branch videocc_mvs0_axi_clk = {
150 .halt_reg = 0x9ec,
151 .halt_check = BRANCH_HALT,
152 .clkr = {
153 .enable_reg = 0x9ec,
154 .enable_mask = BIT(0),
155 .hw.init = &(const struct clk_init_data) {
156 .name = "videocc_mvs0_axi_clk",
157 .ops = &clk_branch2_ops,
158 },
159 },
160 };
161
162 static struct clk_branch videocc_mvs0_core_clk = {
163 .halt_reg = 0x890,
164 .halt_check = BRANCH_VOTED,
165 .clkr = {
166 .enable_reg = 0x890,
167 .enable_mask = BIT(0),
168 .hw.init = &(const struct clk_init_data) {
169 .name = "videocc_mvs0_core_clk",
170 .parent_data = &(const struct clk_parent_data) {
171 .hw = &videocc_iris_clk_src.clkr.hw,
172 },
173 .num_parents = 1,
174 .flags = CLK_SET_RATE_PARENT,
175 .ops = &clk_branch2_ops,
176 },
177 },
178 };
179
180 static struct clk_branch videocc_mvs1_axi_clk = {
181 .halt_reg = 0xa0c,
182 .halt_check = BRANCH_HALT,
183 .clkr = {
184 .enable_reg = 0xa0c,
185 .enable_mask = BIT(0),
186 .hw.init = &(const struct clk_init_data) {
187 .name = "videocc_mvs1_axi_clk",
188 .ops = &clk_branch2_ops,
189 },
190 },
191 };
192
193 static struct clk_branch videocc_mvs1_core_clk = {
194 .halt_reg = 0x8d0,
195 .halt_check = BRANCH_VOTED,
196 .clkr = {
197 .enable_reg = 0x8d0,
198 .enable_mask = BIT(0),
199 .hw.init = &(const struct clk_init_data) {
200 .name = "videocc_mvs1_core_clk",
201 .parent_data = &(const struct clk_parent_data) {
202 .hw = &videocc_iris_clk_src.clkr.hw,
203 },
204 .num_parents = 1,
205 .flags = CLK_SET_RATE_PARENT,
206 .ops = &clk_branch2_ops,
207 },
208 },
209 };
210
211 static struct clk_branch videocc_mvsc_core_clk = {
212 .halt_reg = 0x850,
213 .halt_check = BRANCH_HALT,
214 .clkr = {
215 .enable_reg = 0x850,
216 .enable_mask = BIT(0),
217 .hw.init = &(const struct clk_init_data) {
218 .name = "videocc_mvsc_core_clk",
219 .parent_data = &(const struct clk_parent_data) {
220 .hw = &videocc_iris_clk_src.clkr.hw,
221 },
222 .num_parents = 1,
223 .flags = CLK_SET_RATE_PARENT,
224 .ops = &clk_branch2_ops,
225 },
226 },
227 };
228
229 static struct clk_branch videocc_mvsc_ctl_axi_clk = {
230 .halt_reg = 0x9cc,
231 .halt_check = BRANCH_HALT,
232 .clkr = {
233 .enable_reg = 0x9cc,
234 .enable_mask = BIT(0),
235 .hw.init = &(const struct clk_init_data) {
236 .name = "videocc_mvsc_ctl_axi_clk",
237 .ops = &clk_branch2_ops,
238 },
239 },
240 };
241
242 static struct clk_branch videocc_venus_ahb_clk = {
243 .halt_reg = 0xa6c,
244 .halt_check = BRANCH_HALT,
245 .clkr = {
246 .enable_reg = 0xa6c,
247 .enable_mask = BIT(0),
248 .hw.init = &(const struct clk_init_data) {
249 .name = "videocc_venus_ahb_clk",
250 .ops = &clk_branch2_ops,
251 },
252 },
253 };
254
255 static struct gdsc venus_gdsc = {
256 .gdscr = 0x814,
257 .pd = {
258 .name = "venus_gdsc",
259 },
260 .cxcs = (unsigned int []){ 0x850, 0x9cc },
261 .cxc_count = 2,
262 .pwrsts = PWRSTS_OFF_ON,
263 .flags = POLL_CFG_GDSCR,
264 };
265
266 static struct gdsc vcodec0_gdsc = {
267 .gdscr = 0x874,
268 .pd = {
269 .name = "vcodec0_gdsc",
270 },
271 .cxcs = (unsigned int []){ 0x890, 0x9ec },
272 .cxc_count = 2,
273 .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR,
274 .pwrsts = PWRSTS_OFF_ON,
275 };
276
277 static struct gdsc vcodec1_gdsc = {
278 .gdscr = 0x8b4,
279 .pd = {
280 .name = "vcodec1_gdsc",
281 },
282 .cxcs = (unsigned int []){ 0x8d0, 0xa0c },
283 .cxc_count = 2,
284 .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR,
285 .pwrsts = PWRSTS_OFF_ON,
286 };
287
288 static struct clk_regmap *videocc_sm7150_clocks[] = {
289 [VIDEOCC_PLL0] = &videocc_pll0.clkr,
290 [VIDEOCC_IRIS_AHB_CLK] = &videocc_iris_ahb_clk.clkr,
291 [VIDEOCC_IRIS_CLK_SRC] = &videocc_iris_clk_src.clkr,
292 [VIDEOCC_MVS0_AXI_CLK] = &videocc_mvs0_axi_clk.clkr,
293 [VIDEOCC_MVS0_CORE_CLK] = &videocc_mvs0_core_clk.clkr,
294 [VIDEOCC_MVS1_AXI_CLK] = &videocc_mvs1_axi_clk.clkr,
295 [VIDEOCC_MVS1_CORE_CLK] = &videocc_mvs1_core_clk.clkr,
296 [VIDEOCC_MVSC_CORE_CLK] = &videocc_mvsc_core_clk.clkr,
297 [VIDEOCC_MVSC_CTL_AXI_CLK] = &videocc_mvsc_ctl_axi_clk.clkr,
298 [VIDEOCC_VENUS_AHB_CLK] = &videocc_venus_ahb_clk.clkr,
299 [VIDEOCC_XO_CLK_SRC] = &videocc_xo_clk_src.clkr,
300 };
301
302 static struct gdsc *videocc_sm7150_gdscs[] = {
303 [VENUS_GDSC] = &venus_gdsc,
304 [VCODEC0_GDSC] = &vcodec0_gdsc,
305 [VCODEC1_GDSC] = &vcodec1_gdsc,
306 };
307
308 static const struct regmap_config videocc_sm7150_regmap_config = {
309 .reg_bits = 32,
310 .reg_stride = 4,
311 .val_bits = 32,
312 .max_register = 0xb94,
313 .fast_io = true,
314 };
315
316 static const struct qcom_cc_desc videocc_sm7150_desc = {
317 .config = &videocc_sm7150_regmap_config,
318 .clks = videocc_sm7150_clocks,
319 .num_clks = ARRAY_SIZE(videocc_sm7150_clocks),
320 .gdscs = videocc_sm7150_gdscs,
321 .num_gdscs = ARRAY_SIZE(videocc_sm7150_gdscs),
322 };
323
324 static const struct of_device_id videocc_sm7150_match_table[] = {
325 { .compatible = "qcom,sm7150-videocc" },
326 { }
327 };
328 MODULE_DEVICE_TABLE(of, videocc_sm7150_match_table);
329
videocc_sm7150_probe(struct platform_device * pdev)330 static int videocc_sm7150_probe(struct platform_device *pdev)
331 {
332 struct regmap *regmap;
333
334 regmap = qcom_cc_map(pdev, &videocc_sm7150_desc);
335 if (IS_ERR(regmap))
336 return PTR_ERR(regmap);
337
338 clk_fabia_pll_configure(&videocc_pll0, regmap, &videocc_pll0_config);
339
340 /* Keep some clocks always-on */
341 qcom_branch_set_clk_en(regmap, 0x984); /* VIDEOCC_XO_CLK */
342
343 return qcom_cc_really_probe(&pdev->dev, &videocc_sm7150_desc, regmap);
344 }
345
346 static struct platform_driver videocc_sm7150_driver = {
347 .probe = videocc_sm7150_probe,
348 .driver = {
349 .name = "videocc-sm7150",
350 .of_match_table = videocc_sm7150_match_table,
351 },
352 };
353 module_platform_driver(videocc_sm7150_driver);
354
355 MODULE_DESCRIPTION("Qualcomm SM7150 Video Clock Controller");
356 MODULE_LICENSE("GPL");
357