1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2020, The Linux Foundation. All rights reserved.
4  * Copyright (c) 2021, Linaro Ltd.
5  */
6 
7 #include <linux/err.h>
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/of.h>
11 #include <linux/platform_device.h>
12 #include <linux/regmap.h>
13 
14 #include <dt-bindings/clock/qcom,dispcc-qcm2290.h>
15 
16 #include "clk-alpha-pll.h"
17 #include "clk-branch.h"
18 #include "clk-rcg.h"
19 #include "clk-regmap.h"
20 #include "clk-regmap-divider.h"
21 #include "common.h"
22 #include "gdsc.h"
23 #include "reset.h"
24 
25 enum {
26 	P_BI_TCXO,
27 	P_BI_TCXO_AO,
28 	P_DISP_CC_PLL0_OUT_MAIN,
29 	P_DSI0_PHY_PLL_OUT_BYTECLK,
30 	P_DSI0_PHY_PLL_OUT_DSICLK,
31 	P_GPLL0_OUT_DIV,
32 	P_GPLL0_OUT_MAIN,
33 	P_SLEEP_CLK,
34 };
35 
36 static const struct pll_vco spark_vco[] = {
37 	{ 500000000, 1000000000, 2 },
38 };
39 
40 /* 768MHz configuration */
41 static const struct alpha_pll_config disp_cc_pll0_config = {
42 	.l = 0x28,
43 	.alpha = 0x0,
44 	.alpha_en_mask = BIT(24),
45 	.vco_val = 0x2 << 20,
46 	.vco_mask = GENMASK(21, 20),
47 	.main_output_mask = BIT(0),
48 	.config_ctl_val = 0x4001055B,
49 };
50 
51 static struct clk_alpha_pll disp_cc_pll0 = {
52 	.offset = 0x0,
53 	.vco_table = spark_vco,
54 	.num_vco = ARRAY_SIZE(spark_vco),
55 	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT],
56 	.clkr = {
57 		.hw.init = &(struct clk_init_data){
58 			.name = "disp_cc_pll0",
59 			.parent_data = &(const struct clk_parent_data){
60 				.fw_name = "bi_tcxo",
61 			},
62 			.num_parents = 1,
63 			.ops = &clk_alpha_pll_ops,
64 		},
65 	},
66 };
67 
68 static const struct parent_map disp_cc_parent_map_0[] = {
69 	{ P_BI_TCXO, 0 },
70 	{ P_DSI0_PHY_PLL_OUT_BYTECLK, 1 },
71 };
72 
73 static const struct clk_parent_data disp_cc_parent_data_0[] = {
74 	{ .fw_name = "bi_tcxo" },
75 	{ .fw_name = "dsi0_phy_pll_out_byteclk" },
76 };
77 
78 static const struct parent_map disp_cc_parent_map_1[] = {
79 	{ P_BI_TCXO, 0 },
80 };
81 
82 static const struct clk_parent_data disp_cc_parent_data_1[] = {
83 	{ .fw_name = "bi_tcxo" },
84 };
85 
86 static const struct parent_map disp_cc_parent_map_2[] = {
87 	{ P_BI_TCXO_AO, 0 },
88 	{ P_GPLL0_OUT_DIV, 4 },
89 };
90 
91 static const struct clk_parent_data disp_cc_parent_data_2[] = {
92 	{ .fw_name = "bi_tcxo_ao" },
93 	{ .fw_name = "gcc_disp_gpll0_div_clk_src" },
94 };
95 
96 static const struct parent_map disp_cc_parent_map_3[] = {
97 	{ P_BI_TCXO, 0 },
98 	{ P_DISP_CC_PLL0_OUT_MAIN, 1 },
99 	{ P_GPLL0_OUT_MAIN, 4 },
100 };
101 
102 static const struct clk_parent_data disp_cc_parent_data_3[] = {
103 	{ .fw_name = "bi_tcxo" },
104 	{ .hw = &disp_cc_pll0.clkr.hw },
105 	{ .fw_name = "gcc_disp_gpll0_clk_src" },
106 };
107 
108 static const struct parent_map disp_cc_parent_map_4[] = {
109 	{ P_BI_TCXO, 0 },
110 	{ P_DSI0_PHY_PLL_OUT_DSICLK, 1 },
111 };
112 
113 static const struct clk_parent_data disp_cc_parent_data_4[] = {
114 	{ .fw_name = "bi_tcxo" },
115 	{ .fw_name = "dsi0_phy_pll_out_dsiclk" },
116 };
117 
118 static const struct parent_map disp_cc_parent_map_5[] = {
119 	{ P_SLEEP_CLK, 0 },
120 };
121 
122 static const struct clk_parent_data disp_cc_parent_data_5[] = {
123 	{ .fw_name = "sleep_clk" },
124 };
125 
126 static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = {
127 	.cmd_rcgr = 0x20a4,
128 	.mnd_width = 0,
129 	.hid_width = 5,
130 	.parent_map = disp_cc_parent_map_0,
131 	.clkr.hw.init = &(struct clk_init_data){
132 		.name = "disp_cc_mdss_byte0_clk_src",
133 		.parent_data = disp_cc_parent_data_0,
134 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_0),
135 		/* For set_rate and set_parent to succeed, parent(s) must be enabled */
136 		.flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
137 		.ops = &clk_byte2_ops,
138 	},
139 };
140 
141 static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
142 	.reg = 0x20bc,
143 	.shift = 0,
144 	.width = 2,
145 	.clkr.hw.init = &(struct clk_init_data) {
146 		.name = "disp_cc_mdss_byte0_div_clk_src",
147 		.parent_hws = (const struct clk_hw*[]){
148 			&disp_cc_mdss_byte0_clk_src.clkr.hw,
149 		},
150 		.num_parents = 1,
151 		.ops = &clk_regmap_div_ops,
152 	},
153 };
154 
155 static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = {
156 	F(19200000, P_BI_TCXO_AO, 1, 0, 0),
157 	F(37500000, P_GPLL0_OUT_DIV, 8, 0, 0),
158 	F(75000000, P_GPLL0_OUT_DIV, 4, 0, 0),
159 	{ }
160 };
161 
162 static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = {
163 	.cmd_rcgr = 0x2154,
164 	.mnd_width = 0,
165 	.hid_width = 5,
166 	.parent_map = disp_cc_parent_map_2,
167 	.freq_tbl = ftbl_disp_cc_mdss_ahb_clk_src,
168 	.clkr.hw.init = &(struct clk_init_data){
169 		.name = "disp_cc_mdss_ahb_clk_src",
170 		.parent_data = disp_cc_parent_data_2,
171 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_2),
172 		.ops = &clk_rcg2_shared_ops,
173 	},
174 };
175 
176 static const struct freq_tbl ftbl_disp_cc_mdss_esc0_clk_src[] = {
177 	F(19200000, P_BI_TCXO, 1, 0, 0),
178 	{ }
179 };
180 
181 static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = {
182 	.cmd_rcgr = 0x20c0,
183 	.mnd_width = 0,
184 	.hid_width = 5,
185 	.parent_map = disp_cc_parent_map_0,
186 	.freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src,
187 	.clkr.hw.init = &(struct clk_init_data){
188 		.name = "disp_cc_mdss_esc0_clk_src",
189 		.parent_data = disp_cc_parent_data_0,
190 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_0),
191 		.ops = &clk_rcg2_ops,
192 	},
193 };
194 
195 static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = {
196 	F(19200000, P_BI_TCXO, 1, 0, 0),
197 	F(192000000, P_DISP_CC_PLL0_OUT_MAIN, 4, 0, 0),
198 	F(256000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0),
199 	F(307200000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0),
200 	F(384000000, P_DISP_CC_PLL0_OUT_MAIN, 2, 0, 0),
201 	{ }
202 };
203 
204 static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = {
205 	.cmd_rcgr = 0x2074,
206 	.mnd_width = 0,
207 	.hid_width = 5,
208 	.parent_map = disp_cc_parent_map_3,
209 	.freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src,
210 	.clkr.hw.init = &(struct clk_init_data){
211 		.name = "disp_cc_mdss_mdp_clk_src",
212 		.parent_data = disp_cc_parent_data_3,
213 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_3),
214 		.flags = CLK_SET_RATE_PARENT,
215 		.ops = &clk_rcg2_shared_ops,
216 	},
217 };
218 
219 static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = {
220 	.cmd_rcgr = 0x205c,
221 	.mnd_width = 8,
222 	.hid_width = 5,
223 	.parent_map = disp_cc_parent_map_4,
224 	.clkr.hw.init = &(struct clk_init_data){
225 		.name = "disp_cc_mdss_pclk0_clk_src",
226 		.parent_data = disp_cc_parent_data_4,
227 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_4),
228 		/* For set_rate and set_parent to succeed, parent(s) must be enabled */
229 		.flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
230 		.ops = &clk_pixel_ops,
231 	},
232 };
233 
234 static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = {
235 	.cmd_rcgr = 0x208c,
236 	.mnd_width = 0,
237 	.hid_width = 5,
238 	.parent_map = disp_cc_parent_map_1,
239 	.freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src,
240 	.clkr.hw.init = &(struct clk_init_data){
241 		.name = "disp_cc_mdss_vsync_clk_src",
242 		.parent_data = disp_cc_parent_data_1,
243 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_1),
244 		.flags = CLK_SET_RATE_PARENT,
245 		.ops = &clk_rcg2_shared_ops,
246 	},
247 };
248 
249 static const struct freq_tbl ftbl_disp_cc_sleep_clk_src[] = {
250 	F(32764, P_SLEEP_CLK, 1, 0, 0),
251 	{ }
252 };
253 
254 static struct clk_rcg2 disp_cc_sleep_clk_src = {
255 	.cmd_rcgr = 0x6050,
256 	.mnd_width = 0,
257 	.hid_width = 5,
258 	.parent_map = disp_cc_parent_map_5,
259 	.freq_tbl = ftbl_disp_cc_sleep_clk_src,
260 	.clkr.hw.init = &(struct clk_init_data){
261 		.name = "disp_cc_sleep_clk_src",
262 		.parent_data = disp_cc_parent_data_5,
263 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_5),
264 		.ops = &clk_rcg2_ops,
265 	},
266 };
267 
268 static struct clk_branch disp_cc_mdss_ahb_clk = {
269 	.halt_reg = 0x2044,
270 	.halt_check = BRANCH_HALT,
271 	.clkr = {
272 		.enable_reg = 0x2044,
273 		.enable_mask = BIT(0),
274 		.hw.init = &(struct clk_init_data){
275 			.name = "disp_cc_mdss_ahb_clk",
276 			.parent_hws = (const struct clk_hw*[]){
277 				&disp_cc_mdss_ahb_clk_src.clkr.hw,
278 			},
279 			.num_parents = 1,
280 			.flags = CLK_SET_RATE_PARENT,
281 			.ops = &clk_branch2_ops,
282 		},
283 	},
284 };
285 
286 static struct clk_branch disp_cc_mdss_byte0_clk = {
287 	.halt_reg = 0x201c,
288 	.halt_check = BRANCH_HALT,
289 	.clkr = {
290 		.enable_reg = 0x201c,
291 		.enable_mask = BIT(0),
292 		.hw.init = &(struct clk_init_data){
293 			.name = "disp_cc_mdss_byte0_clk",
294 			.parent_hws = (const struct clk_hw*[]){
295 				&disp_cc_mdss_byte0_clk_src.clkr.hw,
296 			},
297 			.num_parents = 1,
298 			.flags = CLK_SET_RATE_PARENT,
299 			.ops = &clk_branch2_ops,
300 		},
301 	},
302 };
303 
304 static struct clk_branch disp_cc_mdss_byte0_intf_clk = {
305 	.halt_reg = 0x2020,
306 	.halt_check = BRANCH_HALT,
307 	.clkr = {
308 		.enable_reg = 0x2020,
309 		.enable_mask = BIT(0),
310 		.hw.init = &(struct clk_init_data){
311 			.name = "disp_cc_mdss_byte0_intf_clk",
312 			.parent_hws = (const struct clk_hw*[]){
313 				&disp_cc_mdss_byte0_div_clk_src.clkr.hw,
314 			},
315 			.num_parents = 1,
316 			.flags = CLK_SET_RATE_PARENT,
317 			.ops = &clk_branch2_ops,
318 		},
319 	},
320 };
321 
322 static struct clk_branch disp_cc_mdss_esc0_clk = {
323 	.halt_reg = 0x2024,
324 	.halt_check = BRANCH_HALT,
325 	.clkr = {
326 		.enable_reg = 0x2024,
327 		.enable_mask = BIT(0),
328 		.hw.init = &(struct clk_init_data){
329 			.name = "disp_cc_mdss_esc0_clk",
330 			.parent_hws = (const struct clk_hw*[]){
331 				&disp_cc_mdss_esc0_clk_src.clkr.hw,
332 			},
333 			.num_parents = 1,
334 			.flags = CLK_SET_RATE_PARENT,
335 			.ops = &clk_branch2_ops,
336 		},
337 	},
338 };
339 
340 static struct clk_branch disp_cc_mdss_mdp_clk = {
341 	.halt_reg = 0x2008,
342 	.halt_check = BRANCH_HALT,
343 	.clkr = {
344 		.enable_reg = 0x2008,
345 		.enable_mask = BIT(0),
346 		.hw.init = &(struct clk_init_data){
347 			.name = "disp_cc_mdss_mdp_clk",
348 			.parent_hws = (const struct clk_hw*[]){
349 				&disp_cc_mdss_mdp_clk_src.clkr.hw,
350 			},
351 			.num_parents = 1,
352 			.flags = CLK_SET_RATE_PARENT,
353 			.ops = &clk_branch2_ops,
354 		},
355 	},
356 };
357 
358 static struct clk_branch disp_cc_mdss_mdp_lut_clk = {
359 	.halt_reg = 0x2010,
360 	.halt_check = BRANCH_HALT_VOTED,
361 	.clkr = {
362 		.enable_reg = 0x2010,
363 		.enable_mask = BIT(0),
364 		.hw.init = &(struct clk_init_data){
365 			.name = "disp_cc_mdss_mdp_lut_clk",
366 			.parent_hws = (const struct clk_hw*[]){
367 				&disp_cc_mdss_mdp_clk_src.clkr.hw,
368 			},
369 			.num_parents = 1,
370 			.flags = CLK_SET_RATE_PARENT,
371 			.ops = &clk_branch2_ops,
372 		},
373 	},
374 };
375 
376 static struct clk_branch disp_cc_mdss_non_gdsc_ahb_clk = {
377 	.halt_reg = 0x4004,
378 	.halt_check = BRANCH_HALT_VOTED,
379 	.clkr = {
380 		.enable_reg = 0x4004,
381 		.enable_mask = BIT(0),
382 		.hw.init = &(struct clk_init_data){
383 			.name = "disp_cc_mdss_non_gdsc_ahb_clk",
384 			.parent_hws = (const struct clk_hw*[]){
385 				&disp_cc_mdss_ahb_clk_src.clkr.hw,
386 			},
387 			.num_parents = 1,
388 			.flags = CLK_SET_RATE_PARENT,
389 			.ops = &clk_branch2_ops,
390 		},
391 	},
392 };
393 
394 static struct clk_branch disp_cc_mdss_pclk0_clk = {
395 	.halt_reg = 0x2004,
396 	.halt_check = BRANCH_HALT,
397 	.clkr = {
398 		.enable_reg = 0x2004,
399 		.enable_mask = BIT(0),
400 		.hw.init = &(struct clk_init_data){
401 			.name = "disp_cc_mdss_pclk0_clk",
402 			.parent_hws = (const struct clk_hw*[]){
403 				&disp_cc_mdss_pclk0_clk_src.clkr.hw,
404 			},
405 			.num_parents = 1,
406 			.flags = CLK_SET_RATE_PARENT,
407 			.ops = &clk_branch2_ops,
408 		},
409 	},
410 };
411 
412 static struct clk_branch disp_cc_mdss_vsync_clk = {
413 	.halt_reg = 0x2018,
414 	.halt_check = BRANCH_HALT,
415 	.clkr = {
416 		.enable_reg = 0x2018,
417 		.enable_mask = BIT(0),
418 		.hw.init = &(struct clk_init_data){
419 			.name = "disp_cc_mdss_vsync_clk",
420 			.parent_hws = (const struct clk_hw*[]){
421 				&disp_cc_mdss_vsync_clk_src.clkr.hw,
422 			},
423 			.num_parents = 1,
424 			.flags = CLK_SET_RATE_PARENT,
425 			.ops = &clk_branch2_ops,
426 		},
427 	},
428 };
429 
430 static struct clk_branch disp_cc_sleep_clk = {
431 	.halt_reg = 0x6068,
432 	.halt_check = BRANCH_HALT,
433 	.clkr = {
434 		.enable_reg = 0x6068,
435 		.enable_mask = BIT(0),
436 		.hw.init = &(struct clk_init_data){
437 			.name = "disp_cc_sleep_clk",
438 			.parent_hws = (const struct clk_hw*[]){
439 				&disp_cc_sleep_clk_src.clkr.hw,
440 			},
441 			.num_parents = 1,
442 			.flags = CLK_SET_RATE_PARENT,
443 			.ops = &clk_branch2_ops,
444 		},
445 	},
446 };
447 
448 static const struct qcom_reset_map disp_cc_qcm2290_resets[] = {
449 	[DISP_CC_MDSS_CORE_BCR] = { 0x2000 },
450 };
451 
452 static struct gdsc mdss_gdsc = {
453 	.gdscr = 0x3000,
454 	.pd = {
455 		.name = "mdss_gdsc",
456 	},
457 	.pwrsts = PWRSTS_OFF_ON,
458 	.flags = HW_CTRL,
459 };
460 
461 static struct gdsc *disp_cc_qcm2290_gdscs[] = {
462 	[MDSS_GDSC] = &mdss_gdsc,
463 };
464 
465 static struct clk_regmap *disp_cc_qcm2290_clocks[] = {
466 	[DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr,
467 	[DISP_CC_MDSS_AHB_CLK_SRC] = &disp_cc_mdss_ahb_clk_src.clkr,
468 	[DISP_CC_MDSS_BYTE0_CLK] = &disp_cc_mdss_byte0_clk.clkr,
469 	[DISP_CC_MDSS_BYTE0_CLK_SRC] = &disp_cc_mdss_byte0_clk_src.clkr,
470 	[DISP_CC_MDSS_BYTE0_DIV_CLK_SRC] = &disp_cc_mdss_byte0_div_clk_src.clkr,
471 	[DISP_CC_MDSS_BYTE0_INTF_CLK] = &disp_cc_mdss_byte0_intf_clk.clkr,
472 	[DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr,
473 	[DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr,
474 	[DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr,
475 	[DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr,
476 	[DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr,
477 	[DISP_CC_MDSS_NON_GDSC_AHB_CLK] = &disp_cc_mdss_non_gdsc_ahb_clk.clkr,
478 	[DISP_CC_MDSS_PCLK0_CLK] = &disp_cc_mdss_pclk0_clk.clkr,
479 	[DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr,
480 	[DISP_CC_MDSS_VSYNC_CLK] = &disp_cc_mdss_vsync_clk.clkr,
481 	[DISP_CC_MDSS_VSYNC_CLK_SRC] = &disp_cc_mdss_vsync_clk_src.clkr,
482 	[DISP_CC_PLL0] = &disp_cc_pll0.clkr,
483 	[DISP_CC_SLEEP_CLK] = &disp_cc_sleep_clk.clkr,
484 	[DISP_CC_SLEEP_CLK_SRC] = &disp_cc_sleep_clk_src.clkr,
485 };
486 
487 static const struct regmap_config disp_cc_qcm2290_regmap_config = {
488 	.reg_bits = 32,
489 	.reg_stride = 4,
490 	.val_bits = 32,
491 	.max_register = 0x10000,
492 	.fast_io = true,
493 };
494 
495 static const struct qcom_cc_desc disp_cc_qcm2290_desc = {
496 	.config = &disp_cc_qcm2290_regmap_config,
497 	.clks = disp_cc_qcm2290_clocks,
498 	.num_clks = ARRAY_SIZE(disp_cc_qcm2290_clocks),
499 	.gdscs = disp_cc_qcm2290_gdscs,
500 	.num_gdscs = ARRAY_SIZE(disp_cc_qcm2290_gdscs),
501 	.resets = disp_cc_qcm2290_resets,
502 	.num_resets = ARRAY_SIZE(disp_cc_qcm2290_resets),
503 };
504 
505 static const struct of_device_id disp_cc_qcm2290_match_table[] = {
506 	{ .compatible = "qcom,qcm2290-dispcc" },
507 	{ }
508 };
509 MODULE_DEVICE_TABLE(of, disp_cc_qcm2290_match_table);
510 
disp_cc_qcm2290_probe(struct platform_device * pdev)511 static int disp_cc_qcm2290_probe(struct platform_device *pdev)
512 {
513 	struct regmap *regmap;
514 	int ret;
515 
516 	regmap = qcom_cc_map(pdev, &disp_cc_qcm2290_desc);
517 	if (IS_ERR(regmap))
518 		return PTR_ERR(regmap);
519 
520 	clk_alpha_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config);
521 
522 	/* Keep DISP_CC_XO_CLK always-ON */
523 	regmap_update_bits(regmap, 0x604c, BIT(0), BIT(0));
524 
525 	ret = qcom_cc_really_probe(pdev, &disp_cc_qcm2290_desc, regmap);
526 	if (ret) {
527 		dev_err(&pdev->dev, "Failed to register DISP CC clocks\n");
528 		return ret;
529 	}
530 
531 	return ret;
532 }
533 
534 static struct platform_driver disp_cc_qcm2290_driver = {
535 	.probe = disp_cc_qcm2290_probe,
536 	.driver = {
537 		.name = "dispcc-qcm2290",
538 		.of_match_table = disp_cc_qcm2290_match_table,
539 	},
540 };
541 
disp_cc_qcm2290_init(void)542 static int __init disp_cc_qcm2290_init(void)
543 {
544 	return platform_driver_register(&disp_cc_qcm2290_driver);
545 }
546 subsys_initcall(disp_cc_qcm2290_init);
547 
disp_cc_qcm2290_exit(void)548 static void __exit disp_cc_qcm2290_exit(void)
549 {
550 	platform_driver_unregister(&disp_cc_qcm2290_driver);
551 }
552 module_exit(disp_cc_qcm2290_exit);
553 
554 MODULE_DESCRIPTION("QTI DISP_CC qcm2290 Driver");
555 MODULE_LICENSE("GPL v2");
556