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