1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/clk-provider.h> |
7 | #include <linux/mod_devicetable.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/pm_runtime.h> |
11 | #include <linux/regmap.h> |
12 | |
13 | #include <dt-bindings/clock/qcom,sm8450-videocc.h> |
14 | |
15 | #include "clk-alpha-pll.h" |
16 | #include "clk-branch.h" |
17 | #include "clk-rcg.h" |
18 | #include "clk-regmap.h" |
19 | #include "clk-regmap-divider.h" |
20 | #include "common.h" |
21 | #include "gdsc.h" |
22 | #include "reset.h" |
23 | |
24 | enum { |
25 | DT_BI_TCXO, |
26 | }; |
27 | |
28 | enum { |
29 | P_BI_TCXO, |
30 | P_VIDEO_CC_PLL0_OUT_MAIN, |
31 | P_VIDEO_CC_PLL1_OUT_MAIN, |
32 | }; |
33 | |
34 | static const struct pll_vco lucid_evo_vco[] = { |
35 | { 249600000, 2020000000, 0 }, |
36 | }; |
37 | |
38 | static const struct alpha_pll_config video_cc_pll0_config = { |
39 | /* .l includes CAL_L_VAL, L_VAL fields */ |
40 | .l = 0x0044001e, |
41 | .alpha = 0x0, |
42 | .config_ctl_val = 0x20485699, |
43 | .config_ctl_hi_val = 0x00182261, |
44 | .config_ctl_hi1_val = 0x32aa299c, |
45 | .user_ctl_val = 0x00000000, |
46 | .user_ctl_hi_val = 0x00000805, |
47 | }; |
48 | |
49 | static struct clk_alpha_pll video_cc_pll0 = { |
50 | .offset = 0x0, |
51 | .vco_table = lucid_evo_vco, |
52 | .num_vco = ARRAY_SIZE(lucid_evo_vco), |
53 | .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], |
54 | .clkr = { |
55 | .hw.init = &(const struct clk_init_data) { |
56 | .name = "video_cc_pll0" , |
57 | .parent_data = &(const struct clk_parent_data) { |
58 | .index = DT_BI_TCXO, |
59 | }, |
60 | .num_parents = 1, |
61 | .ops = &clk_alpha_pll_lucid_evo_ops, |
62 | }, |
63 | }, |
64 | }; |
65 | |
66 | static const struct alpha_pll_config video_cc_pll1_config = { |
67 | /* .l includes CAL_L_VAL, L_VAL fields */ |
68 | .l = 0x0044002b, |
69 | .alpha = 0xc000, |
70 | .config_ctl_val = 0x20485699, |
71 | .config_ctl_hi_val = 0x00182261, |
72 | .config_ctl_hi1_val = 0x32aa299c, |
73 | .user_ctl_val = 0x00000000, |
74 | .user_ctl_hi_val = 0x00000805, |
75 | }; |
76 | |
77 | static struct clk_alpha_pll video_cc_pll1 = { |
78 | .offset = 0x1000, |
79 | .vco_table = lucid_evo_vco, |
80 | .num_vco = ARRAY_SIZE(lucid_evo_vco), |
81 | .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], |
82 | .clkr = { |
83 | .hw.init = &(const struct clk_init_data) { |
84 | .name = "video_cc_pll1" , |
85 | .parent_data = &(const struct clk_parent_data) { |
86 | .index = DT_BI_TCXO, |
87 | }, |
88 | .num_parents = 1, |
89 | .ops = &clk_alpha_pll_lucid_evo_ops, |
90 | }, |
91 | }, |
92 | }; |
93 | |
94 | static const struct parent_map video_cc_parent_map_0[] = { |
95 | { P_BI_TCXO, 0 }, |
96 | { P_VIDEO_CC_PLL0_OUT_MAIN, 1 }, |
97 | }; |
98 | |
99 | static const struct clk_parent_data video_cc_parent_data_0[] = { |
100 | { .index = DT_BI_TCXO }, |
101 | { .hw = &video_cc_pll0.clkr.hw }, |
102 | }; |
103 | |
104 | static const struct parent_map video_cc_parent_map_1[] = { |
105 | { P_BI_TCXO, 0 }, |
106 | { P_VIDEO_CC_PLL1_OUT_MAIN, 1 }, |
107 | }; |
108 | |
109 | static const struct clk_parent_data video_cc_parent_data_1[] = { |
110 | { .index = DT_BI_TCXO }, |
111 | { .hw = &video_cc_pll1.clkr.hw }, |
112 | }; |
113 | |
114 | static const struct freq_tbl ftbl_video_cc_mvs0_clk_src[] = { |
115 | F(576000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), |
116 | F(720000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), |
117 | F(1014000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), |
118 | F(1098000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), |
119 | F(1332000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), |
120 | { } |
121 | }; |
122 | |
123 | static struct clk_rcg2 video_cc_mvs0_clk_src = { |
124 | .cmd_rcgr = 0x8000, |
125 | .mnd_width = 0, |
126 | .hid_width = 5, |
127 | .parent_map = video_cc_parent_map_0, |
128 | .freq_tbl = ftbl_video_cc_mvs0_clk_src, |
129 | .clkr.hw.init = &(const struct clk_init_data) { |
130 | .name = "video_cc_mvs0_clk_src" , |
131 | .parent_data = video_cc_parent_data_0, |
132 | .num_parents = ARRAY_SIZE(video_cc_parent_data_0), |
133 | .flags = CLK_SET_RATE_PARENT, |
134 | .ops = &clk_rcg2_shared_ops, |
135 | }, |
136 | }; |
137 | |
138 | static const struct freq_tbl ftbl_video_cc_mvs1_clk_src[] = { |
139 | F(840000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), |
140 | F(1050000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), |
141 | F(1350000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), |
142 | F(1500000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), |
143 | F(1650000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), |
144 | { } |
145 | }; |
146 | |
147 | static struct clk_rcg2 video_cc_mvs1_clk_src = { |
148 | .cmd_rcgr = 0x8018, |
149 | .mnd_width = 0, |
150 | .hid_width = 5, |
151 | .parent_map = video_cc_parent_map_1, |
152 | .freq_tbl = ftbl_video_cc_mvs1_clk_src, |
153 | .clkr.hw.init = &(const struct clk_init_data) { |
154 | .name = "video_cc_mvs1_clk_src" , |
155 | .parent_data = video_cc_parent_data_1, |
156 | .num_parents = ARRAY_SIZE(video_cc_parent_data_1), |
157 | .flags = CLK_SET_RATE_PARENT, |
158 | .ops = &clk_rcg2_shared_ops, |
159 | }, |
160 | }; |
161 | |
162 | static struct clk_regmap_div video_cc_mvs0_div_clk_src = { |
163 | .reg = 0x80b8, |
164 | .shift = 0, |
165 | .width = 4, |
166 | .clkr.hw.init = &(const struct clk_init_data) { |
167 | .name = "video_cc_mvs0_div_clk_src" , |
168 | .parent_hws = (const struct clk_hw*[]) { |
169 | &video_cc_mvs0_clk_src.clkr.hw, |
170 | }, |
171 | .num_parents = 1, |
172 | .flags = CLK_SET_RATE_PARENT, |
173 | .ops = &clk_regmap_div_ro_ops, |
174 | }, |
175 | }; |
176 | |
177 | static struct clk_regmap_div video_cc_mvs0c_div2_div_clk_src = { |
178 | .reg = 0x806c, |
179 | .shift = 0, |
180 | .width = 4, |
181 | .clkr.hw.init = &(const struct clk_init_data) { |
182 | .name = "video_cc_mvs0c_div2_div_clk_src" , |
183 | .parent_hws = (const struct clk_hw*[]) { |
184 | &video_cc_mvs0_clk_src.clkr.hw, |
185 | }, |
186 | .num_parents = 1, |
187 | .flags = CLK_SET_RATE_PARENT, |
188 | .ops = &clk_regmap_div_ro_ops, |
189 | }, |
190 | }; |
191 | |
192 | static struct clk_regmap_div video_cc_mvs1_div_clk_src = { |
193 | .reg = 0x80dc, |
194 | .shift = 0, |
195 | .width = 4, |
196 | .clkr.hw.init = &(const struct clk_init_data) { |
197 | .name = "video_cc_mvs1_div_clk_src" , |
198 | .parent_hws = (const struct clk_hw*[]) { |
199 | &video_cc_mvs1_clk_src.clkr.hw, |
200 | }, |
201 | .num_parents = 1, |
202 | .flags = CLK_SET_RATE_PARENT, |
203 | .ops = &clk_regmap_div_ro_ops, |
204 | }, |
205 | }; |
206 | |
207 | static struct clk_regmap_div video_cc_mvs1c_div2_div_clk_src = { |
208 | .reg = 0x8094, |
209 | .shift = 0, |
210 | .width = 4, |
211 | .clkr.hw.init = &(const struct clk_init_data) { |
212 | .name = "video_cc_mvs1c_div2_div_clk_src" , |
213 | .parent_hws = (const struct clk_hw*[]) { |
214 | &video_cc_mvs1_clk_src.clkr.hw, |
215 | }, |
216 | .num_parents = 1, |
217 | .flags = CLK_SET_RATE_PARENT, |
218 | .ops = &clk_regmap_div_ro_ops, |
219 | }, |
220 | }; |
221 | |
222 | static struct clk_branch video_cc_mvs0_clk = { |
223 | .halt_reg = 0x80b0, |
224 | .halt_check = BRANCH_HALT_SKIP, |
225 | .hwcg_reg = 0x80b0, |
226 | .hwcg_bit = 1, |
227 | .clkr = { |
228 | .enable_reg = 0x80b0, |
229 | .enable_mask = BIT(0), |
230 | .hw.init = &(const struct clk_init_data) { |
231 | .name = "video_cc_mvs0_clk" , |
232 | .parent_hws = (const struct clk_hw*[]) { |
233 | &video_cc_mvs0_div_clk_src.clkr.hw, |
234 | }, |
235 | .num_parents = 1, |
236 | .flags = CLK_SET_RATE_PARENT, |
237 | .ops = &clk_branch2_ops, |
238 | }, |
239 | }, |
240 | }; |
241 | |
242 | static struct clk_branch video_cc_mvs0c_clk = { |
243 | .halt_reg = 0x8064, |
244 | .halt_check = BRANCH_HALT, |
245 | .clkr = { |
246 | .enable_reg = 0x8064, |
247 | .enable_mask = BIT(0), |
248 | .hw.init = &(const struct clk_init_data) { |
249 | .name = "video_cc_mvs0c_clk" , |
250 | .parent_hws = (const struct clk_hw*[]) { |
251 | &video_cc_mvs0c_div2_div_clk_src.clkr.hw, |
252 | }, |
253 | .num_parents = 1, |
254 | .flags = CLK_SET_RATE_PARENT, |
255 | .ops = &clk_branch2_ops, |
256 | }, |
257 | }, |
258 | }; |
259 | |
260 | static struct clk_branch video_cc_mvs1_clk = { |
261 | .halt_reg = 0x80d4, |
262 | .halt_check = BRANCH_HALT_SKIP, |
263 | .hwcg_reg = 0x80d4, |
264 | .hwcg_bit = 1, |
265 | .clkr = { |
266 | .enable_reg = 0x80d4, |
267 | .enable_mask = BIT(0), |
268 | .hw.init = &(const struct clk_init_data) { |
269 | .name = "video_cc_mvs1_clk" , |
270 | .parent_hws = (const struct clk_hw*[]) { |
271 | &video_cc_mvs1_div_clk_src.clkr.hw, |
272 | }, |
273 | .num_parents = 1, |
274 | .flags = CLK_SET_RATE_PARENT, |
275 | .ops = &clk_branch2_ops, |
276 | }, |
277 | }, |
278 | }; |
279 | |
280 | static struct clk_branch video_cc_mvs1c_clk = { |
281 | .halt_reg = 0x808c, |
282 | .halt_check = BRANCH_HALT, |
283 | .clkr = { |
284 | .enable_reg = 0x808c, |
285 | .enable_mask = BIT(0), |
286 | .hw.init = &(const struct clk_init_data) { |
287 | .name = "video_cc_mvs1c_clk" , |
288 | .parent_hws = (const struct clk_hw*[]) { |
289 | &video_cc_mvs1c_div2_div_clk_src.clkr.hw, |
290 | }, |
291 | .num_parents = 1, |
292 | .flags = CLK_SET_RATE_PARENT, |
293 | .ops = &clk_branch2_ops, |
294 | }, |
295 | }, |
296 | }; |
297 | |
298 | static struct gdsc video_cc_mvs0c_gdsc = { |
299 | .gdscr = 0x804c, |
300 | .en_rest_wait_val = 0x2, |
301 | .en_few_wait_val = 0x2, |
302 | .clk_dis_wait_val = 0x6, |
303 | .pd = { |
304 | .name = "video_cc_mvs0c_gdsc" , |
305 | }, |
306 | .pwrsts = PWRSTS_OFF_ON, |
307 | .flags = RETAIN_FF_ENABLE, |
308 | }; |
309 | |
310 | static struct gdsc video_cc_mvs0_gdsc = { |
311 | .gdscr = 0x809c, |
312 | .en_rest_wait_val = 0x2, |
313 | .en_few_wait_val = 0x2, |
314 | .clk_dis_wait_val = 0x6, |
315 | .pd = { |
316 | .name = "video_cc_mvs0_gdsc" , |
317 | }, |
318 | .pwrsts = PWRSTS_OFF_ON, |
319 | .parent = &video_cc_mvs0c_gdsc.pd, |
320 | .flags = RETAIN_FF_ENABLE | HW_CTRL, |
321 | }; |
322 | |
323 | static struct gdsc video_cc_mvs1c_gdsc = { |
324 | .gdscr = 0x8074, |
325 | .en_rest_wait_val = 0x2, |
326 | .en_few_wait_val = 0x2, |
327 | .clk_dis_wait_val = 0x6, |
328 | .pd = { |
329 | .name = "video_cc_mvs1c_gdsc" , |
330 | }, |
331 | .pwrsts = PWRSTS_OFF_ON, |
332 | .flags = RETAIN_FF_ENABLE, |
333 | }; |
334 | |
335 | static struct gdsc video_cc_mvs1_gdsc = { |
336 | .gdscr = 0x80c0, |
337 | .en_rest_wait_val = 0x2, |
338 | .en_few_wait_val = 0x2, |
339 | .clk_dis_wait_val = 0x6, |
340 | .pd = { |
341 | .name = "video_cc_mvs1_gdsc" , |
342 | }, |
343 | .pwrsts = PWRSTS_OFF_ON, |
344 | .parent = &video_cc_mvs1c_gdsc.pd, |
345 | .flags = RETAIN_FF_ENABLE | HW_CTRL, |
346 | }; |
347 | |
348 | static struct clk_regmap *video_cc_sm8450_clocks[] = { |
349 | [VIDEO_CC_MVS0_CLK] = &video_cc_mvs0_clk.clkr, |
350 | [VIDEO_CC_MVS0_CLK_SRC] = &video_cc_mvs0_clk_src.clkr, |
351 | [VIDEO_CC_MVS0_DIV_CLK_SRC] = &video_cc_mvs0_div_clk_src.clkr, |
352 | [VIDEO_CC_MVS0C_CLK] = &video_cc_mvs0c_clk.clkr, |
353 | [VIDEO_CC_MVS0C_DIV2_DIV_CLK_SRC] = &video_cc_mvs0c_div2_div_clk_src.clkr, |
354 | [VIDEO_CC_MVS1_CLK] = &video_cc_mvs1_clk.clkr, |
355 | [VIDEO_CC_MVS1_CLK_SRC] = &video_cc_mvs1_clk_src.clkr, |
356 | [VIDEO_CC_MVS1_DIV_CLK_SRC] = &video_cc_mvs1_div_clk_src.clkr, |
357 | [VIDEO_CC_MVS1C_CLK] = &video_cc_mvs1c_clk.clkr, |
358 | [VIDEO_CC_MVS1C_DIV2_DIV_CLK_SRC] = &video_cc_mvs1c_div2_div_clk_src.clkr, |
359 | [VIDEO_CC_PLL0] = &video_cc_pll0.clkr, |
360 | [VIDEO_CC_PLL1] = &video_cc_pll1.clkr, |
361 | }; |
362 | |
363 | static struct gdsc *video_cc_sm8450_gdscs[] = { |
364 | [VIDEO_CC_MVS0C_GDSC] = &video_cc_mvs0c_gdsc, |
365 | [VIDEO_CC_MVS0_GDSC] = &video_cc_mvs0_gdsc, |
366 | [VIDEO_CC_MVS1C_GDSC] = &video_cc_mvs1c_gdsc, |
367 | [VIDEO_CC_MVS1_GDSC] = &video_cc_mvs1_gdsc, |
368 | }; |
369 | |
370 | static const struct qcom_reset_map video_cc_sm8450_resets[] = { |
371 | [CVP_VIDEO_CC_INTERFACE_BCR] = { 0x80e0 }, |
372 | [CVP_VIDEO_CC_MVS0_BCR] = { 0x8098 }, |
373 | [CVP_VIDEO_CC_MVS0C_BCR] = { 0x8048 }, |
374 | [CVP_VIDEO_CC_MVS1_BCR] = { 0x80bc }, |
375 | [CVP_VIDEO_CC_MVS1C_BCR] = { 0x8070 }, |
376 | [VIDEO_CC_MVS0C_CLK_ARES] = { .reg = 0x8064, .bit = 2, .udelay = 1000 }, |
377 | [VIDEO_CC_MVS1C_CLK_ARES] = { .reg = 0x808c, .bit = 2, .udelay = 1000 }, |
378 | }; |
379 | |
380 | static const struct regmap_config video_cc_sm8450_regmap_config = { |
381 | .reg_bits = 32, |
382 | .reg_stride = 4, |
383 | .val_bits = 32, |
384 | .max_register = 0x9f4c, |
385 | .fast_io = true, |
386 | }; |
387 | |
388 | static struct qcom_cc_desc video_cc_sm8450_desc = { |
389 | .config = &video_cc_sm8450_regmap_config, |
390 | .clks = video_cc_sm8450_clocks, |
391 | .num_clks = ARRAY_SIZE(video_cc_sm8450_clocks), |
392 | .resets = video_cc_sm8450_resets, |
393 | .num_resets = ARRAY_SIZE(video_cc_sm8450_resets), |
394 | .gdscs = video_cc_sm8450_gdscs, |
395 | .num_gdscs = ARRAY_SIZE(video_cc_sm8450_gdscs), |
396 | }; |
397 | |
398 | static const struct of_device_id video_cc_sm8450_match_table[] = { |
399 | { .compatible = "qcom,sm8450-videocc" }, |
400 | { } |
401 | }; |
402 | MODULE_DEVICE_TABLE(of, video_cc_sm8450_match_table); |
403 | |
404 | static int video_cc_sm8450_probe(struct platform_device *pdev) |
405 | { |
406 | struct regmap *regmap; |
407 | int ret; |
408 | |
409 | ret = devm_pm_runtime_enable(dev: &pdev->dev); |
410 | if (ret) |
411 | return ret; |
412 | |
413 | ret = pm_runtime_resume_and_get(dev: &pdev->dev); |
414 | if (ret) |
415 | return ret; |
416 | |
417 | regmap = qcom_cc_map(pdev, desc: &video_cc_sm8450_desc); |
418 | if (IS_ERR(ptr: regmap)) { |
419 | pm_runtime_put(dev: &pdev->dev); |
420 | return PTR_ERR(ptr: regmap); |
421 | } |
422 | |
423 | clk_lucid_evo_pll_configure(pll: &video_cc_pll0, regmap, config: &video_cc_pll0_config); |
424 | clk_lucid_evo_pll_configure(pll: &video_cc_pll1, regmap, config: &video_cc_pll1_config); |
425 | |
426 | /* Keep some clocks always-on */ |
427 | qcom_branch_set_clk_en(regmap, cbcr: 0x80e4); /* VIDEO_CC_AHB_CLK */ |
428 | qcom_branch_set_clk_en(regmap, cbcr: 0x8130); /* VIDEO_CC_SLEEP_CLK */ |
429 | qcom_branch_set_clk_en(regmap, cbcr: 0x8114); /* VIDEO_CC_XO_CLK */ |
430 | |
431 | ret = qcom_cc_really_probe(pdev, desc: &video_cc_sm8450_desc, regmap); |
432 | |
433 | pm_runtime_put(dev: &pdev->dev); |
434 | |
435 | return ret; |
436 | } |
437 | |
438 | static struct platform_driver video_cc_sm8450_driver = { |
439 | .probe = video_cc_sm8450_probe, |
440 | .driver = { |
441 | .name = "video_cc-sm8450" , |
442 | .of_match_table = video_cc_sm8450_match_table, |
443 | }, |
444 | }; |
445 | |
446 | module_platform_driver(video_cc_sm8450_driver); |
447 | |
448 | MODULE_DESCRIPTION("QTI VIDEOCC SM8450 Driver" ); |
449 | MODULE_LICENSE("GPL" ); |
450 | |