1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/module.h> 8 #include <linux/mod_devicetable.h> 9 #include <linux/of.h> 10 #include <linux/platform_device.h> 11 #include <linux/pm_runtime.h> 12 #include <linux/regmap.h> 13 14 #include <dt-bindings/clock/qcom,sa8775p-videocc.h> 15 16 #include "clk-alpha-pll.h" 17 #include "clk-branch.h" 18 #include "clk-pll.h" 19 #include "clk-rcg.h" 20 #include "clk-regmap.h" 21 #include "clk-regmap-divider.h" 22 #include "clk-regmap-mux.h" 23 #include "common.h" 24 #include "gdsc.h" 25 #include "reset.h" 26 27 enum { 28 DT_IFACE, 29 DT_BI_TCXO, 30 DT_BI_TCXO_AO, 31 DT_SLEEP_CLK, 32 }; 33 34 enum { 35 P_BI_TCXO, 36 P_BI_TCXO_AO, 37 P_SLEEP_CLK, 38 P_VIDEO_PLL0_OUT_MAIN, 39 P_VIDEO_PLL1_OUT_MAIN, 40 }; 41 42 static const struct pll_vco lucid_evo_vco[] = { 43 { 249600000, 2020000000, 0 }, 44 }; 45 46 static const struct alpha_pll_config video_pll0_config = { 47 .l = 0x39, 48 .alpha = 0x3000, 49 .config_ctl_val = 0x20485699, 50 .config_ctl_hi_val = 0x00182261, 51 .config_ctl_hi1_val = 0x32aa299c, 52 .user_ctl_val = 0x00000000, 53 .user_ctl_hi_val = 0x00400805, 54 }; 55 56 static struct clk_alpha_pll video_pll0 = { 57 .offset = 0x0, 58 .vco_table = lucid_evo_vco, 59 .num_vco = ARRAY_SIZE(lucid_evo_vco), 60 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], 61 .clkr = { 62 .hw.init = &(const struct clk_init_data) { 63 .name = "video_pll0", 64 .parent_data = &(const struct clk_parent_data) { 65 .index = DT_BI_TCXO, 66 }, 67 .num_parents = 1, 68 .ops = &clk_alpha_pll_lucid_evo_ops, 69 }, 70 }, 71 }; 72 73 static const struct alpha_pll_config video_pll1_config = { 74 .l = 0x39, 75 .alpha = 0x3000, 76 .config_ctl_val = 0x20485699, 77 .config_ctl_hi_val = 0x00182261, 78 .config_ctl_hi1_val = 0x32aa299c, 79 .user_ctl_val = 0x00000000, 80 .user_ctl_hi_val = 0x00400805, 81 }; 82 83 static struct clk_alpha_pll video_pll1 = { 84 .offset = 0x1000, 85 .vco_table = lucid_evo_vco, 86 .num_vco = ARRAY_SIZE(lucid_evo_vco), 87 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], 88 .clkr = { 89 .hw.init = &(const struct clk_init_data) { 90 .name = "video_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_ao[] = { 101 { P_BI_TCXO_AO, 0 }, 102 }; 103 104 static const struct clk_parent_data video_cc_parent_data_0_ao[] = { 105 { .index = DT_BI_TCXO_AO }, 106 }; 107 108 static const struct parent_map video_cc_parent_map_1[] = { 109 { P_BI_TCXO, 0 }, 110 { P_VIDEO_PLL0_OUT_MAIN, 1 }, 111 }; 112 113 static const struct clk_parent_data video_cc_parent_data_1[] = { 114 { .index = DT_BI_TCXO }, 115 { .hw = &video_pll0.clkr.hw }, 116 }; 117 118 static const struct parent_map video_cc_parent_map_2[] = { 119 { P_BI_TCXO, 0 }, 120 { P_VIDEO_PLL1_OUT_MAIN, 1 }, 121 }; 122 123 static const struct clk_parent_data video_cc_parent_data_2[] = { 124 { .index = DT_BI_TCXO }, 125 { .hw = &video_pll1.clkr.hw }, 126 }; 127 128 static const struct parent_map video_cc_parent_map_3[] = { 129 { P_SLEEP_CLK, 0 }, 130 }; 131 132 static const struct clk_parent_data video_cc_parent_data_3[] = { 133 { .index = DT_SLEEP_CLK }, 134 }; 135 136 static const struct freq_tbl ftbl_video_cc_ahb_clk_src[] = { 137 F(19200000, P_BI_TCXO_AO, 1, 0, 0), 138 { } 139 }; 140 141 static struct clk_rcg2 video_cc_ahb_clk_src = { 142 .cmd_rcgr = 0x8030, 143 .mnd_width = 0, 144 .hid_width = 5, 145 .parent_map = video_cc_parent_map_0_ao, 146 .freq_tbl = ftbl_video_cc_ahb_clk_src, 147 .clkr.hw.init = &(const struct clk_init_data) { 148 .name = "video_cc_ahb_clk_src", 149 .parent_data = video_cc_parent_data_0_ao, 150 .num_parents = ARRAY_SIZE(video_cc_parent_data_0_ao), 151 .flags = CLK_SET_RATE_PARENT, 152 .ops = &clk_rcg2_shared_ops, 153 }, 154 }; 155 156 static const struct freq_tbl ftbl_video_cc_mvs0_clk_src[] = { 157 F(1098000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), 158 F(1332000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), 159 F(1599000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), 160 F(1680000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), 161 { } 162 }; 163 164 static struct clk_rcg2 video_cc_mvs0_clk_src = { 165 .cmd_rcgr = 0x8000, 166 .mnd_width = 0, 167 .hid_width = 5, 168 .parent_map = video_cc_parent_map_1, 169 .freq_tbl = ftbl_video_cc_mvs0_clk_src, 170 .clkr.hw.init = &(const struct clk_init_data) { 171 .name = "video_cc_mvs0_clk_src", 172 .parent_data = video_cc_parent_data_1, 173 .num_parents = ARRAY_SIZE(video_cc_parent_data_1), 174 .flags = CLK_SET_RATE_PARENT, 175 .ops = &clk_rcg2_shared_ops, 176 }, 177 }; 178 179 static const struct freq_tbl ftbl_video_cc_mvs1_clk_src[] = { 180 F(1098000000, P_VIDEO_PLL1_OUT_MAIN, 1, 0, 0), 181 F(1332000000, P_VIDEO_PLL1_OUT_MAIN, 1, 0, 0), 182 F(1600000000, P_VIDEO_PLL1_OUT_MAIN, 1, 0, 0), 183 F(1800000000, P_VIDEO_PLL1_OUT_MAIN, 1, 0, 0), 184 { } 185 }; 186 187 static struct clk_rcg2 video_cc_mvs1_clk_src = { 188 .cmd_rcgr = 0x8018, 189 .mnd_width = 0, 190 .hid_width = 5, 191 .parent_map = video_cc_parent_map_2, 192 .freq_tbl = ftbl_video_cc_mvs1_clk_src, 193 .clkr.hw.init = &(const struct clk_init_data) { 194 .name = "video_cc_mvs1_clk_src", 195 .parent_data = video_cc_parent_data_2, 196 .num_parents = ARRAY_SIZE(video_cc_parent_data_2), 197 .flags = CLK_SET_RATE_PARENT, 198 .ops = &clk_rcg2_shared_ops, 199 }, 200 }; 201 202 static const struct freq_tbl ftbl_video_cc_sleep_clk_src[] = { 203 F(32000, P_SLEEP_CLK, 1, 0, 0), 204 { } 205 }; 206 207 static struct clk_rcg2 video_cc_sleep_clk_src = { 208 .cmd_rcgr = 0x812c, 209 .mnd_width = 0, 210 .hid_width = 5, 211 .parent_map = video_cc_parent_map_3, 212 .freq_tbl = ftbl_video_cc_sleep_clk_src, 213 .clkr.hw.init = &(const struct clk_init_data) { 214 .name = "video_cc_sleep_clk_src", 215 .parent_data = video_cc_parent_data_3, 216 .num_parents = ARRAY_SIZE(video_cc_parent_data_3), 217 .flags = CLK_SET_RATE_PARENT, 218 .ops = &clk_rcg2_shared_ops, 219 }, 220 }; 221 222 static struct clk_rcg2 video_cc_xo_clk_src = { 223 .cmd_rcgr = 0x8110, 224 .mnd_width = 0, 225 .hid_width = 5, 226 .parent_map = video_cc_parent_map_0_ao, 227 .freq_tbl = ftbl_video_cc_ahb_clk_src, 228 .clkr.hw.init = &(const struct clk_init_data) { 229 .name = "video_cc_xo_clk_src", 230 .parent_data = video_cc_parent_data_0_ao, 231 .num_parents = ARRAY_SIZE(video_cc_parent_data_0_ao), 232 .flags = CLK_SET_RATE_PARENT, 233 .ops = &clk_rcg2_shared_ops, 234 }, 235 }; 236 237 static struct clk_regmap_div video_cc_mvs0_div_clk_src = { 238 .reg = 0x80b8, 239 .shift = 0, 240 .width = 4, 241 .clkr.hw.init = &(const struct clk_init_data) { 242 .name = "video_cc_mvs0_div_clk_src", 243 .parent_hws = (const struct clk_hw*[]) { 244 &video_cc_mvs0_clk_src.clkr.hw, 245 }, 246 .num_parents = 1, 247 .flags = CLK_SET_RATE_PARENT, 248 .ops = &clk_regmap_div_ro_ops, 249 }, 250 }; 251 252 static struct clk_regmap_div video_cc_mvs0c_div2_div_clk_src = { 253 .reg = 0x806c, 254 .shift = 0, 255 .width = 4, 256 .clkr.hw.init = &(const struct clk_init_data) { 257 .name = "video_cc_mvs0c_div2_div_clk_src", 258 .parent_hws = (const struct clk_hw*[]) { 259 &video_cc_mvs0_clk_src.clkr.hw, 260 }, 261 .num_parents = 1, 262 .flags = CLK_SET_RATE_PARENT, 263 .ops = &clk_regmap_div_ro_ops, 264 }, 265 }; 266 267 static struct clk_regmap_div video_cc_mvs1_div_clk_src = { 268 .reg = 0x80dc, 269 .shift = 0, 270 .width = 4, 271 .clkr.hw.init = &(const struct clk_init_data) { 272 .name = "video_cc_mvs1_div_clk_src", 273 .parent_hws = (const struct clk_hw*[]) { 274 &video_cc_mvs1_clk_src.clkr.hw, 275 }, 276 .num_parents = 1, 277 .flags = CLK_SET_RATE_PARENT, 278 .ops = &clk_regmap_div_ro_ops, 279 }, 280 }; 281 282 static struct clk_regmap_div video_cc_mvs1c_div2_div_clk_src = { 283 .reg = 0x8094, 284 .shift = 0, 285 .width = 4, 286 .clkr.hw.init = &(const struct clk_init_data) { 287 .name = "video_cc_mvs1c_div2_div_clk_src", 288 .parent_hws = (const struct clk_hw*[]) { 289 &video_cc_mvs1_clk_src.clkr.hw, 290 }, 291 .num_parents = 1, 292 .flags = CLK_SET_RATE_PARENT, 293 .ops = &clk_regmap_div_ro_ops, 294 }, 295 }; 296 297 static struct clk_regmap_div video_cc_sm_div_clk_src = { 298 .reg = 0x8108, 299 .shift = 0, 300 .width = 4, 301 .clkr.hw.init = &(const struct clk_init_data) { 302 .name = "video_cc_sm_div_clk_src", 303 .ops = &clk_regmap_div_ro_ops, 304 }, 305 }; 306 307 static struct clk_branch video_cc_mvs0_clk = { 308 .halt_reg = 0x80b0, 309 .halt_check = BRANCH_HALT_VOTED, 310 .hwcg_reg = 0x80b0, 311 .hwcg_bit = 1, 312 .clkr = { 313 .enable_reg = 0x80b0, 314 .enable_mask = BIT(0), 315 .hw.init = &(const struct clk_init_data) { 316 .name = "video_cc_mvs0_clk", 317 .parent_hws = (const struct clk_hw*[]) { 318 &video_cc_mvs0_div_clk_src.clkr.hw, 319 }, 320 .num_parents = 1, 321 .flags = CLK_SET_RATE_PARENT, 322 .ops = &clk_branch2_ops, 323 }, 324 }, 325 }; 326 327 static struct clk_branch video_cc_mvs0c_clk = { 328 .halt_reg = 0x8064, 329 .halt_check = BRANCH_HALT, 330 .clkr = { 331 .enable_reg = 0x8064, 332 .enable_mask = BIT(0), 333 .hw.init = &(const struct clk_init_data) { 334 .name = "video_cc_mvs0c_clk", 335 .parent_hws = (const struct clk_hw*[]) { 336 &video_cc_mvs0c_div2_div_clk_src.clkr.hw, 337 }, 338 .num_parents = 1, 339 .flags = CLK_SET_RATE_PARENT, 340 .ops = &clk_branch2_ops, 341 }, 342 }, 343 }; 344 345 static struct clk_branch video_cc_mvs1_clk = { 346 .halt_reg = 0x80d4, 347 .halt_check = BRANCH_HALT_VOTED, 348 .hwcg_reg = 0x80d4, 349 .hwcg_bit = 1, 350 .clkr = { 351 .enable_reg = 0x80d4, 352 .enable_mask = BIT(0), 353 .hw.init = &(const struct clk_init_data) { 354 .name = "video_cc_mvs1_clk", 355 .parent_hws = (const struct clk_hw*[]) { 356 &video_cc_mvs1_div_clk_src.clkr.hw, 357 }, 358 .num_parents = 1, 359 .flags = CLK_SET_RATE_PARENT, 360 .ops = &clk_branch2_ops, 361 }, 362 }, 363 }; 364 365 static struct clk_branch video_cc_mvs1c_clk = { 366 .halt_reg = 0x808c, 367 .halt_check = BRANCH_HALT, 368 .clkr = { 369 .enable_reg = 0x808c, 370 .enable_mask = BIT(0), 371 .hw.init = &(const struct clk_init_data) { 372 .name = "video_cc_mvs1c_clk", 373 .parent_hws = (const struct clk_hw*[]) { 374 &video_cc_mvs1c_div2_div_clk_src.clkr.hw, 375 }, 376 .num_parents = 1, 377 .flags = CLK_SET_RATE_PARENT, 378 .ops = &clk_branch2_ops, 379 }, 380 }, 381 }; 382 383 static struct clk_branch video_cc_pll_lock_monitor_clk = { 384 .halt_reg = 0x9000, 385 .halt_check = BRANCH_HALT, 386 .clkr = { 387 .enable_reg = 0x9000, 388 .enable_mask = BIT(0), 389 .hw.init = &(const struct clk_init_data) { 390 .name = "video_cc_pll_lock_monitor_clk", 391 .parent_hws = (const struct clk_hw*[]) { 392 &video_cc_xo_clk_src.clkr.hw, 393 }, 394 .num_parents = 1, 395 .flags = CLK_SET_RATE_PARENT, 396 .ops = &clk_branch2_ops, 397 }, 398 }, 399 }; 400 401 static struct clk_branch video_cc_sm_obs_clk = { 402 .halt_reg = 0x810c, 403 .halt_check = BRANCH_HALT_SKIP, 404 .clkr = { 405 .enable_reg = 0x810c, 406 .enable_mask = BIT(0), 407 .hw.init = &(const struct clk_init_data) { 408 .name = "video_cc_sm_obs_clk", 409 .parent_hws = (const struct clk_hw*[]) { 410 &video_cc_sm_div_clk_src.clkr.hw, 411 }, 412 .num_parents = 1, 413 .flags = CLK_SET_RATE_PARENT, 414 .ops = &clk_branch2_ops, 415 }, 416 }, 417 }; 418 419 static struct gdsc video_cc_mvs0c_gdsc = { 420 .gdscr = 0x804c, 421 .en_rest_wait_val = 0x2, 422 .en_few_wait_val = 0x2, 423 .clk_dis_wait_val = 0x6, 424 .pd = { 425 .name = "video_cc_mvs0c_gdsc", 426 }, 427 .pwrsts = PWRSTS_OFF_ON, 428 .flags = RETAIN_FF_ENABLE | POLL_CFG_GDSCR, 429 }; 430 431 static struct gdsc video_cc_mvs0_gdsc = { 432 .gdscr = 0x809c, 433 .en_rest_wait_val = 0x2, 434 .en_few_wait_val = 0x2, 435 .clk_dis_wait_val = 0x6, 436 .pd = { 437 .name = "video_cc_mvs0_gdsc", 438 }, 439 .pwrsts = PWRSTS_OFF_ON, 440 .parent = &video_cc_mvs0c_gdsc.pd, 441 .flags = RETAIN_FF_ENABLE | POLL_CFG_GDSCR | HW_CTRL_TRIGGER, 442 }; 443 444 static struct gdsc video_cc_mvs1c_gdsc = { 445 .gdscr = 0x8074, 446 .en_rest_wait_val = 0x2, 447 .en_few_wait_val = 0x2, 448 .clk_dis_wait_val = 0x6, 449 .pd = { 450 .name = "video_cc_mvs1c_gdsc", 451 }, 452 .pwrsts = PWRSTS_OFF_ON, 453 .flags = RETAIN_FF_ENABLE | POLL_CFG_GDSCR, 454 }; 455 456 static struct gdsc video_cc_mvs1_gdsc = { 457 .gdscr = 0x80c0, 458 .en_rest_wait_val = 0x2, 459 .en_few_wait_val = 0x2, 460 .clk_dis_wait_val = 0x6, 461 .pd = { 462 .name = "video_cc_mvs1_gdsc", 463 }, 464 .pwrsts = PWRSTS_OFF_ON, 465 .parent = &video_cc_mvs1c_gdsc.pd, 466 .flags = RETAIN_FF_ENABLE | POLL_CFG_GDSCR | HW_CTRL_TRIGGER, 467 }; 468 469 static struct clk_regmap *video_cc_sa8775p_clocks[] = { 470 [VIDEO_CC_AHB_CLK_SRC] = &video_cc_ahb_clk_src.clkr, 471 [VIDEO_CC_MVS0_CLK] = &video_cc_mvs0_clk.clkr, 472 [VIDEO_CC_MVS0_CLK_SRC] = &video_cc_mvs0_clk_src.clkr, 473 [VIDEO_CC_MVS0_DIV_CLK_SRC] = &video_cc_mvs0_div_clk_src.clkr, 474 [VIDEO_CC_MVS0C_CLK] = &video_cc_mvs0c_clk.clkr, 475 [VIDEO_CC_MVS0C_DIV2_DIV_CLK_SRC] = &video_cc_mvs0c_div2_div_clk_src.clkr, 476 [VIDEO_CC_MVS1_CLK] = &video_cc_mvs1_clk.clkr, 477 [VIDEO_CC_MVS1_CLK_SRC] = &video_cc_mvs1_clk_src.clkr, 478 [VIDEO_CC_MVS1_DIV_CLK_SRC] = &video_cc_mvs1_div_clk_src.clkr, 479 [VIDEO_CC_MVS1C_CLK] = &video_cc_mvs1c_clk.clkr, 480 [VIDEO_CC_MVS1C_DIV2_DIV_CLK_SRC] = &video_cc_mvs1c_div2_div_clk_src.clkr, 481 [VIDEO_CC_PLL_LOCK_MONITOR_CLK] = &video_cc_pll_lock_monitor_clk.clkr, 482 [VIDEO_CC_SLEEP_CLK_SRC] = &video_cc_sleep_clk_src.clkr, 483 [VIDEO_CC_SM_DIV_CLK_SRC] = &video_cc_sm_div_clk_src.clkr, 484 [VIDEO_CC_SM_OBS_CLK] = &video_cc_sm_obs_clk.clkr, 485 [VIDEO_CC_XO_CLK_SRC] = &video_cc_xo_clk_src.clkr, 486 [VIDEO_PLL0] = &video_pll0.clkr, 487 [VIDEO_PLL1] = &video_pll1.clkr, 488 }; 489 490 static struct gdsc *video_cc_sa8775p_gdscs[] = { 491 [VIDEO_CC_MVS0_GDSC] = &video_cc_mvs0_gdsc, 492 [VIDEO_CC_MVS0C_GDSC] = &video_cc_mvs0c_gdsc, 493 [VIDEO_CC_MVS1_GDSC] = &video_cc_mvs1_gdsc, 494 [VIDEO_CC_MVS1C_GDSC] = &video_cc_mvs1c_gdsc, 495 }; 496 497 static const struct qcom_reset_map video_cc_sa8775p_resets[] = { 498 [VIDEO_CC_INTERFACE_BCR] = { 0x80e8 }, 499 [VIDEO_CC_MVS0_BCR] = { 0x8098 }, 500 [VIDEO_CC_MVS0C_CLK_ARES] = { 0x8064, 2 }, 501 [VIDEO_CC_MVS0C_BCR] = { 0x8048 }, 502 [VIDEO_CC_MVS1_BCR] = { 0x80bc }, 503 [VIDEO_CC_MVS1C_CLK_ARES] = { 0x808c, 2 }, 504 [VIDEO_CC_MVS1C_BCR] = { 0x8070 }, 505 }; 506 507 static const struct regmap_config video_cc_sa8775p_regmap_config = { 508 .reg_bits = 32, 509 .reg_stride = 4, 510 .val_bits = 32, 511 .max_register = 0xb000, 512 .fast_io = true, 513 }; 514 515 static struct qcom_cc_desc video_cc_sa8775p_desc = { 516 .config = &video_cc_sa8775p_regmap_config, 517 .clks = video_cc_sa8775p_clocks, 518 .num_clks = ARRAY_SIZE(video_cc_sa8775p_clocks), 519 .resets = video_cc_sa8775p_resets, 520 .num_resets = ARRAY_SIZE(video_cc_sa8775p_resets), 521 .gdscs = video_cc_sa8775p_gdscs, 522 .num_gdscs = ARRAY_SIZE(video_cc_sa8775p_gdscs), 523 }; 524 525 static const struct of_device_id video_cc_sa8775p_match_table[] = { 526 { .compatible = "qcom,sa8775p-videocc" }, 527 { } 528 }; 529 MODULE_DEVICE_TABLE(of, video_cc_sa8775p_match_table); 530 531 static int video_cc_sa8775p_probe(struct platform_device *pdev) 532 { 533 struct regmap *regmap; 534 int ret; 535 536 ret = devm_pm_runtime_enable(&pdev->dev); 537 if (ret) 538 return ret; 539 540 ret = pm_runtime_resume_and_get(&pdev->dev); 541 if (ret) 542 return ret; 543 544 regmap = qcom_cc_map(pdev, &video_cc_sa8775p_desc); 545 if (IS_ERR(regmap)) { 546 pm_runtime_put(&pdev->dev); 547 return PTR_ERR(regmap); 548 } 549 550 clk_lucid_evo_pll_configure(&video_pll0, regmap, &video_pll0_config); 551 clk_lucid_evo_pll_configure(&video_pll1, regmap, &video_pll1_config); 552 553 /* Keep some clocks always enabled */ 554 qcom_branch_set_clk_en(regmap, 0x80ec); /* VIDEO_CC_AHB_CLK */ 555 qcom_branch_set_clk_en(regmap, 0x8144); /* VIDEO_CC_SLEEP_CLK */ 556 qcom_branch_set_clk_en(regmap, 0x8128); /* VIDEO_CC_XO_CLK */ 557 558 ret = qcom_cc_really_probe(&pdev->dev, &video_cc_sa8775p_desc, regmap); 559 560 pm_runtime_put(&pdev->dev); 561 562 return ret; 563 } 564 565 static struct platform_driver video_cc_sa8775p_driver = { 566 .probe = video_cc_sa8775p_probe, 567 .driver = { 568 .name = "videocc-sa8775p", 569 .of_match_table = video_cc_sa8775p_match_table, 570 }, 571 }; 572 573 module_platform_driver(video_cc_sa8775p_driver); 574 575 MODULE_DESCRIPTION("QTI VIDEOCC SA8775P Driver"); 576 MODULE_LICENSE("GPL"); 577