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