1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. 4 * Copyright (C) 2017 Linaro Ltd. 5 */ 6 #include <linux/init.h> 7 #include <linux/interconnect.h> 8 #include <linux/io.h> 9 #include <linux/ioctl.h> 10 #include <linux/delay.h> 11 #include <linux/devcoredump.h> 12 #include <linux/list.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/of_platform.h> 16 #include <linux/platform_device.h> 17 #include <linux/slab.h> 18 #include <linux/types.h> 19 #include <linux/pm_domain.h> 20 #include <linux/pm_runtime.h> 21 #include <linux/videodev2.h> 22 #include <media/videobuf2-v4l2.h> 23 #include <media/v4l2-ctrls.h> 24 #include <media/v4l2-mem2mem.h> 25 #include <media/v4l2-ioctl.h> 26 27 #include "core.h" 28 #include "firmware.h" 29 #include "pm_helpers.h" 30 #include "hfi_venus_io.h" 31 32 static void venus_coredump(struct venus_core *core) 33 { 34 struct device *dev; 35 phys_addr_t mem_phys; 36 size_t mem_size; 37 void *mem_va; 38 void *data; 39 40 dev = core->dev; 41 mem_phys = core->fw.mem_phys; 42 mem_size = core->fw.mem_size; 43 44 mem_va = memremap(mem_phys, mem_size, MEMREMAP_WC); 45 if (!mem_va) 46 return; 47 48 data = vmalloc(mem_size); 49 if (!data) { 50 memunmap(mem_va); 51 return; 52 } 53 54 memcpy(data, mem_va, mem_size); 55 memunmap(mem_va); 56 dev_coredumpv(dev, data, mem_size, GFP_KERNEL); 57 } 58 59 static void venus_event_notify(struct venus_core *core, u32 event) 60 { 61 struct venus_inst *inst; 62 63 switch (event) { 64 case EVT_SYS_WATCHDOG_TIMEOUT: 65 case EVT_SYS_ERROR: 66 break; 67 default: 68 return; 69 } 70 71 mutex_lock(&core->lock); 72 set_bit(0, &core->sys_error); 73 set_bit(0, &core->dump_core); 74 list_for_each_entry(inst, &core->instances, list) 75 inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL); 76 mutex_unlock(&core->lock); 77 78 disable_irq_nosync(core->irq); 79 schedule_delayed_work(&core->work, msecs_to_jiffies(10)); 80 } 81 82 static const struct hfi_core_ops venus_core_ops = { 83 .event_notify = venus_event_notify, 84 }; 85 86 #define RPM_WAIT_FOR_IDLE_MAX_ATTEMPTS 10 87 88 static void venus_sys_error_handler(struct work_struct *work) 89 { 90 struct venus_core *core = 91 container_of(work, struct venus_core, work.work); 92 int ret, i, max_attempts = RPM_WAIT_FOR_IDLE_MAX_ATTEMPTS; 93 const char *err_msg = ""; 94 bool failed = false; 95 96 ret = pm_runtime_get_sync(core->dev); 97 if (ret < 0) { 98 err_msg = "resume runtime PM"; 99 max_attempts = 0; 100 failed = true; 101 } 102 103 core->ops->core_deinit(core); 104 core->state = CORE_UNINIT; 105 106 for (i = 0; i < max_attempts; i++) { 107 if (!pm_runtime_active(core->dev_dec) && !pm_runtime_active(core->dev_enc)) 108 break; 109 msleep(10); 110 } 111 112 mutex_lock(&core->lock); 113 114 venus_shutdown(core); 115 116 if (test_bit(0, &core->dump_core)) { 117 venus_coredump(core); 118 clear_bit(0, &core->dump_core); 119 } 120 121 pm_runtime_put_sync(core->dev); 122 123 for (i = 0; i < max_attempts; i++) { 124 if (!core->pmdomains || 125 !pm_runtime_active(core->pmdomains->pd_devs[0])) 126 break; 127 usleep_range(1000, 1500); 128 } 129 130 hfi_reinit(core); 131 132 ret = pm_runtime_get_sync(core->dev); 133 if (ret < 0) { 134 err_msg = "resume runtime PM"; 135 failed = true; 136 } 137 138 ret = venus_boot(core); 139 if (ret && !failed) { 140 err_msg = "boot Venus"; 141 failed = true; 142 } 143 144 ret = hfi_core_resume(core, true); 145 if (ret && !failed) { 146 err_msg = "resume HFI"; 147 failed = true; 148 } 149 150 enable_irq(core->irq); 151 152 mutex_unlock(&core->lock); 153 154 ret = hfi_core_init(core); 155 if (ret && !failed) { 156 err_msg = "init HFI"; 157 failed = true; 158 } 159 160 pm_runtime_put_sync(core->dev); 161 162 if (failed) { 163 disable_irq_nosync(core->irq); 164 dev_warn_ratelimited(core->dev, 165 "System error has occurred, recovery failed to %s\n", 166 err_msg); 167 schedule_delayed_work(&core->work, msecs_to_jiffies(10)); 168 return; 169 } 170 171 dev_warn(core->dev, "system error has occurred (recovered)\n"); 172 173 mutex_lock(&core->lock); 174 clear_bit(0, &core->sys_error); 175 wake_up_all(&core->sys_err_done); 176 mutex_unlock(&core->lock); 177 } 178 179 static u32 to_v4l2_codec_type(u32 codec) 180 { 181 switch (codec) { 182 case HFI_VIDEO_CODEC_HEVC: 183 return V4L2_PIX_FMT_HEVC; 184 case HFI_VIDEO_CODEC_H264: 185 return V4L2_PIX_FMT_H264; 186 case HFI_VIDEO_CODEC_H263: 187 return V4L2_PIX_FMT_H263; 188 case HFI_VIDEO_CODEC_MPEG1: 189 return V4L2_PIX_FMT_MPEG1; 190 case HFI_VIDEO_CODEC_MPEG2: 191 return V4L2_PIX_FMT_MPEG2; 192 case HFI_VIDEO_CODEC_MPEG4: 193 return V4L2_PIX_FMT_MPEG4; 194 case HFI_VIDEO_CODEC_VC1: 195 return V4L2_PIX_FMT_VC1_ANNEX_G; 196 case HFI_VIDEO_CODEC_VP8: 197 return V4L2_PIX_FMT_VP8; 198 case HFI_VIDEO_CODEC_VP9: 199 return V4L2_PIX_FMT_VP9; 200 case HFI_VIDEO_CODEC_DIVX: 201 case HFI_VIDEO_CODEC_DIVX_311: 202 return V4L2_PIX_FMT_XVID; 203 default: 204 return 0; 205 } 206 } 207 208 static int venus_enumerate_codecs(struct venus_core *core, u32 type) 209 { 210 const struct hfi_inst_ops dummy_ops = {}; 211 struct venus_inst *inst; 212 u32 codec, codecs; 213 unsigned int i; 214 int ret; 215 216 if (core->res->hfi_version != HFI_VERSION_1XX) 217 return 0; 218 219 inst = kzalloc_obj(*inst); 220 if (!inst) 221 return -ENOMEM; 222 223 mutex_init(&inst->lock); 224 inst->core = core; 225 inst->session_type = type; 226 if (type == VIDC_SESSION_TYPE_DEC) 227 codecs = core->dec_codecs; 228 else 229 codecs = core->enc_codecs; 230 231 ret = hfi_session_create(inst, &dummy_ops); 232 if (ret) 233 goto err; 234 235 for (i = 0; i < MAX_CODEC_NUM; i++) { 236 codec = (1UL << i) & codecs; 237 if (!codec) 238 continue; 239 240 ret = hfi_session_init(inst, to_v4l2_codec_type(codec)); 241 if (ret) 242 goto done; 243 244 ret = hfi_session_deinit(inst); 245 if (ret) 246 goto done; 247 } 248 249 done: 250 hfi_session_destroy(inst); 251 err: 252 mutex_destroy(&inst->lock); 253 kfree(inst); 254 255 return ret; 256 } 257 258 static void venus_assign_register_offsets(struct venus_core *core) 259 { 260 if (IS_IRIS2(core) || IS_IRIS2_1(core) || IS_AR50_LITE(core)) { 261 core->cpu_base = core->base + CPU_BASE_V6; 262 core->cpu_cs_base = core->base + CPU_CS_BASE_V6; 263 core->cpu_ic_base = core->base + CPU_IC_BASE_V6; 264 core->wrapper_base = core->base + WRAPPER_BASE_V6; 265 core->wrapper_tz_base = core->base + WRAPPER_TZ_BASE_V6; 266 if (IS_AR50_LITE(core)) { 267 core->vbif_base = NULL; 268 core->aon_base = NULL; 269 } else { 270 core->vbif_base = core->base + VBIF_BASE; 271 core->aon_base = core->base + AON_BASE_V6; 272 } 273 } else { 274 core->vbif_base = core->base + VBIF_BASE; 275 core->cpu_base = core->base + CPU_BASE; 276 core->cpu_cs_base = core->base + CPU_CS_BASE; 277 core->cpu_ic_base = core->base + CPU_IC_BASE; 278 core->wrapper_base = core->base + WRAPPER_BASE; 279 core->wrapper_tz_base = NULL; 280 core->aon_base = NULL; 281 } 282 } 283 284 static irqreturn_t venus_isr_thread(int irq, void *dev_id) 285 { 286 struct venus_core *core = dev_id; 287 irqreturn_t ret; 288 289 ret = hfi_isr_thread(irq, dev_id); 290 291 if (ret == IRQ_HANDLED && venus_fault_inject_ssr()) 292 hfi_core_trigger_ssr(core, HFI_TEST_SSR_SW_ERR_FATAL); 293 294 return ret; 295 } 296 297 #if defined(CONFIG_OF_DYNAMIC) 298 static int venus_add_video_core(struct venus_core *core, const char *node_name, 299 const char *compat) 300 { 301 struct of_changeset *ocs = core->ocs; 302 struct device *dev = core->dev; 303 struct device_node *np, *enp; 304 int ret; 305 306 if (!node_name) 307 return 0; 308 309 enp = of_find_node_by_name(dev->of_node, node_name); 310 if (enp) { 311 of_node_put(enp); 312 return 0; 313 } 314 315 np = of_changeset_create_node(ocs, dev->of_node, node_name); 316 if (!np) { 317 dev_err(dev, "Unable to create new node\n"); 318 return -ENODEV; 319 } 320 321 ret = of_changeset_add_prop_string(ocs, np, "compatible", compat); 322 if (ret) 323 dev_err(dev, "unable to add %s\n", compat); 324 325 of_node_put(np); 326 327 return ret; 328 } 329 330 static int venus_add_dynamic_nodes(struct venus_core *core) 331 { 332 struct device *dev = core->dev; 333 int ret; 334 335 core->ocs = kmalloc_obj(*core->ocs); 336 if (!core->ocs) 337 return -ENOMEM; 338 339 of_changeset_init(core->ocs); 340 341 ret = venus_add_video_core(core, core->res->dec_nodename, "venus-decoder"); 342 if (ret) 343 goto err; 344 345 ret = venus_add_video_core(core, core->res->enc_nodename, "venus-encoder"); 346 if (ret) 347 goto err; 348 349 ret = of_changeset_apply(core->ocs); 350 if (ret) { 351 dev_err(dev, "applying changeset fail ret %d\n", ret); 352 goto err; 353 } 354 355 return 0; 356 err: 357 of_changeset_destroy(core->ocs); 358 kfree(core->ocs); 359 core->ocs = NULL; 360 return ret; 361 } 362 363 static void venus_remove_dynamic_nodes(struct venus_core *core) 364 { 365 if (core->ocs) { 366 of_changeset_revert(core->ocs); 367 of_changeset_destroy(core->ocs); 368 kfree(core->ocs); 369 } 370 } 371 #else 372 static int venus_add_dynamic_nodes(struct venus_core *core) 373 { 374 return 0; 375 } 376 377 static void venus_remove_dynamic_nodes(struct venus_core *core) {} 378 #endif 379 380 static int venus_probe(struct platform_device *pdev) 381 { 382 struct device *dev = &pdev->dev; 383 struct venus_core *core; 384 int ret; 385 386 core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); 387 if (!core) 388 return -ENOMEM; 389 390 core->dev = dev; 391 392 core->base = devm_platform_ioremap_resource(pdev, 0); 393 if (IS_ERR(core->base)) 394 return PTR_ERR(core->base); 395 396 core->video_path = devm_of_icc_get(dev, "video-mem"); 397 if (IS_ERR(core->video_path)) 398 return PTR_ERR(core->video_path); 399 400 core->cpucfg_path = devm_of_icc_get(dev, "cpu-cfg"); 401 if (IS_ERR(core->cpucfg_path)) 402 return PTR_ERR(core->cpucfg_path); 403 404 core->irq = platform_get_irq(pdev, 0); 405 if (core->irq < 0) 406 return core->irq; 407 408 core->res = of_device_get_match_data(dev); 409 if (!core->res) 410 return -ENODEV; 411 412 mutex_init(&core->pm_lock); 413 414 core->pm_ops = venus_pm_get(core->res->hfi_version); 415 if (!core->pm_ops) 416 return -ENODEV; 417 418 if (core->pm_ops->core_get) { 419 ret = core->pm_ops->core_get(core); 420 if (ret) 421 return ret; 422 } 423 424 ret = dma_set_mask_and_coherent(dev, core->res->dma_mask); 425 if (ret) 426 goto err_core_put; 427 428 dma_set_max_seg_size(dev, UINT_MAX); 429 430 INIT_LIST_HEAD(&core->instances); 431 mutex_init(&core->lock); 432 INIT_DELAYED_WORK(&core->work, venus_sys_error_handler); 433 init_waitqueue_head(&core->sys_err_done); 434 435 ret = hfi_create(core, &venus_core_ops); 436 if (ret) 437 goto err_core_put; 438 439 ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread, 440 IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 441 "venus", core); 442 if (ret) 443 goto err_core_put; 444 445 venus_assign_register_offsets(core); 446 447 ret = v4l2_device_register(dev, &core->v4l2_dev); 448 if (ret) 449 goto err_hfi_destroy; 450 451 platform_set_drvdata(pdev, core); 452 453 pm_runtime_enable(dev); 454 455 ret = pm_runtime_get_sync(dev); 456 if (ret < 0) 457 goto err_runtime_disable; 458 459 ret = venus_firmware_init(core); 460 if (ret) 461 goto err_runtime_disable; 462 463 ret = venus_boot(core); 464 if (ret) 465 goto err_firmware_deinit; 466 467 ret = venus_firmware_cfg(core); 468 if (ret) 469 goto err_venus_shutdown; 470 471 ret = hfi_core_resume(core, true); 472 if (ret) 473 goto err_venus_shutdown; 474 475 ret = hfi_core_init(core); 476 if (ret) 477 goto err_venus_shutdown; 478 479 ret = venus_firmware_check(core); 480 if (ret) 481 goto err_core_deinit; 482 483 if (core->res->dec_nodename || core->res->enc_nodename) { 484 ret = venus_add_dynamic_nodes(core); 485 if (ret) 486 goto err_core_deinit; 487 } 488 489 ret = of_platform_populate(dev->of_node, NULL, NULL, dev); 490 if (ret) 491 goto err_remove_dynamic_nodes; 492 493 ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC); 494 if (ret) 495 goto err_of_depopulate; 496 497 ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC); 498 if (ret) 499 goto err_of_depopulate; 500 501 ret = pm_runtime_put_sync(dev); 502 if (ret) { 503 pm_runtime_get_noresume(dev); 504 goto err_of_depopulate; 505 } 506 507 venus_dbgfs_init(core); 508 509 return 0; 510 511 err_of_depopulate: 512 of_platform_depopulate(dev); 513 err_remove_dynamic_nodes: 514 venus_remove_dynamic_nodes(core); 515 err_core_deinit: 516 hfi_core_deinit(core, false); 517 err_venus_shutdown: 518 venus_shutdown(core); 519 err_firmware_deinit: 520 venus_firmware_deinit(core); 521 err_runtime_disable: 522 pm_runtime_put_noidle(dev); 523 pm_runtime_disable(dev); 524 pm_runtime_set_suspended(dev); 525 v4l2_device_unregister(&core->v4l2_dev); 526 err_hfi_destroy: 527 hfi_destroy(core); 528 err_core_put: 529 if (core->pm_ops->core_put) 530 core->pm_ops->core_put(core); 531 return ret; 532 } 533 534 static void venus_remove(struct platform_device *pdev) 535 { 536 struct venus_core *core = platform_get_drvdata(pdev); 537 const struct venus_pm_ops *pm_ops = core->pm_ops; 538 struct device *dev = core->dev; 539 int ret; 540 541 cancel_delayed_work_sync(&core->work); 542 ret = pm_runtime_get_sync(dev); 543 WARN_ON(ret < 0); 544 545 ret = hfi_core_deinit(core, true); 546 WARN_ON(ret); 547 548 venus_shutdown(core); 549 of_platform_depopulate(dev); 550 551 venus_firmware_deinit(core); 552 553 venus_remove_dynamic_nodes(core); 554 555 pm_runtime_put_sync(dev); 556 pm_runtime_disable(dev); 557 558 if (pm_ops->core_put) 559 pm_ops->core_put(core); 560 561 v4l2_device_unregister(&core->v4l2_dev); 562 563 hfi_destroy(core); 564 565 mutex_destroy(&core->pm_lock); 566 mutex_destroy(&core->lock); 567 venus_dbgfs_deinit(core); 568 } 569 570 static void venus_core_shutdown(struct platform_device *pdev) 571 { 572 struct venus_core *core = platform_get_drvdata(pdev); 573 574 pm_runtime_get_sync(core->dev); 575 venus_shutdown(core); 576 venus_firmware_deinit(core); 577 pm_runtime_put_sync(core->dev); 578 } 579 580 static __maybe_unused int venus_runtime_suspend(struct device *dev) 581 { 582 struct venus_core *core = dev_get_drvdata(dev); 583 const struct venus_pm_ops *pm_ops = core->pm_ops; 584 int ret; 585 586 ret = hfi_core_suspend(core); 587 if (ret) 588 return ret; 589 590 if (pm_ops->core_power) { 591 ret = pm_ops->core_power(core, POWER_OFF); 592 if (ret) 593 return ret; 594 } 595 596 ret = icc_set_bw(core->cpucfg_path, 0, 0); 597 if (ret) 598 goto err_cpucfg_path; 599 600 ret = icc_set_bw(core->video_path, 0, 0); 601 if (ret) 602 goto err_video_path; 603 604 return ret; 605 606 err_video_path: 607 icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0); 608 err_cpucfg_path: 609 if (pm_ops->core_power) 610 pm_ops->core_power(core, POWER_ON); 611 612 return ret; 613 } 614 615 void venus_close_common(struct venus_inst *inst, struct file *filp) 616 { 617 /* 618 * Make sure we don't have IRQ/IRQ-thread currently running 619 * or pending execution, which would race with the inst destruction. 620 */ 621 synchronize_irq(inst->core->irq); 622 623 v4l2_m2m_ctx_release(inst->m2m_ctx); 624 v4l2_m2m_release(inst->m2m_dev); 625 hfi_session_destroy(inst); 626 v4l2_fh_del(&inst->fh, filp); 627 v4l2_fh_exit(&inst->fh); 628 v4l2_ctrl_handler_free(&inst->ctrl_handler); 629 630 mutex_destroy(&inst->lock); 631 mutex_destroy(&inst->ctx_q_lock); 632 } 633 EXPORT_SYMBOL_GPL(venus_close_common); 634 635 static __maybe_unused int venus_runtime_resume(struct device *dev) 636 { 637 struct venus_core *core = dev_get_drvdata(dev); 638 const struct venus_pm_ops *pm_ops = core->pm_ops; 639 int ret; 640 641 ret = icc_set_bw(core->video_path, kbps_to_icc(20000), 0); 642 if (ret) 643 return ret; 644 645 ret = icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0); 646 if (ret) 647 return ret; 648 649 if (pm_ops->core_power) { 650 ret = pm_ops->core_power(core, POWER_ON); 651 if (ret) 652 return ret; 653 } 654 655 return hfi_core_resume(core, false); 656 } 657 658 static const struct dev_pm_ops venus_pm_ops = { 659 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 660 pm_runtime_force_resume) 661 SET_RUNTIME_PM_OPS(venus_runtime_suspend, venus_runtime_resume, NULL) 662 }; 663 664 static const struct freq_tbl msm8916_freq_table[] = { 665 { 352800, 228570000 }, /* 1920x1088 @ 30 + 1280x720 @ 30 */ 666 { 244800, 160000000 }, /* 1920x1088 @ 30 */ 667 { 108000, 100000000 }, /* 1280x720 @ 30 */ 668 }; 669 670 static const struct reg_val msm8916_reg_preset[] = { 671 { 0xe0020, 0x05555556 }, 672 { 0xe0024, 0x05555556 }, 673 { 0x80124, 0x00000003 }, 674 }; 675 676 static const struct venus_resources msm8916_res = { 677 .freq_tbl = msm8916_freq_table, 678 .freq_tbl_size = ARRAY_SIZE(msm8916_freq_table), 679 .reg_tbl = msm8916_reg_preset, 680 .reg_tbl_size = ARRAY_SIZE(msm8916_reg_preset), 681 .clks = { "core", "iface", "bus", }, 682 .clks_num = 3, 683 .max_load = 352800, /* 720p@30 + 1080p@30 */ 684 .hfi_version = HFI_VERSION_1XX, 685 .vmem_id = VIDC_RESOURCE_NONE, 686 .vmem_size = 0, 687 .vmem_addr = 0, 688 .dma_mask = 0xddc00000 - 1, 689 .fwname = "qcom/venus-1.8/venus.mbn", 690 .dec_codec_blacklist = HFI_VIDEO_CODEC_HEVC | HFI_VIDEO_CODEC_SPARK, 691 .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, 692 .dec_nodename = "video-decoder", 693 .enc_nodename = "video-encoder", 694 }; 695 696 static const struct freq_tbl msm8939_freq_table[] = { 697 { 489600, 266670000 }, /* 1080p @ 60 */ 698 { 244800, 133330000 }, /* 1080p @ 30 */ 699 { 220800, 133330000 }, /* 720p @ 60 */ 700 { 108000, 133330000 }, /* 720p @ 30 */ 701 { 72000, 133330000 }, /* VGA @ 60 */ 702 { 36000, 133330000 }, /* VGA @ 30 */ 703 }; 704 705 static const struct reg_val msm8939_reg_preset[] = { 706 { 0xe0020, 0x0aaaaaaa }, 707 { 0xe0024, 0x0aaaaaaa }, 708 { 0x80124, 0x00000003 }, 709 }; 710 711 static const struct venus_resources msm8939_res = { 712 .freq_tbl = msm8939_freq_table, 713 .freq_tbl_size = ARRAY_SIZE(msm8939_freq_table), 714 .reg_tbl = msm8939_reg_preset, 715 .reg_tbl_size = ARRAY_SIZE(msm8939_reg_preset), 716 .clks = { "core", "iface", "bus", }, 717 .clks_num = 3, 718 .vcodec_clks = { "vcodec0_core", "vcodec1_core" }, 719 .vcodec_clks_num = 2, 720 .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, 721 .vcodec_pmdomains_num = 3, 722 .max_load = 489600, /* 1080p@30 + 1080p@30 */ 723 .hfi_version = HFI_VERSION_1XX, 724 .vmem_id = VIDC_RESOURCE_NONE, 725 .vmem_size = 0, 726 .vmem_addr = 0, 727 .dma_mask = 0xddc00000 - 1, 728 .fwname = "qcom/venus-1.8/venus.mbn", 729 .dec_codec_blacklist = HFI_VIDEO_CODEC_SPARK, 730 .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, 731 .dec_nodename = "video-decoder", 732 .enc_nodename = "video-encoder", 733 }; 734 735 static const struct freq_tbl msm8996_freq_table[] = { 736 { 1944000, 520000000 }, /* 4k UHD @ 60 (decode only) */ 737 { 972000, 520000000 }, /* 4k UHD @ 30 */ 738 { 489600, 346666667 }, /* 1080p @ 60 */ 739 { 244800, 150000000 }, /* 1080p @ 30 */ 740 { 108000, 75000000 }, /* 720p @ 30 */ 741 }; 742 743 static const struct reg_val msm8996_reg_preset[] = { 744 { 0x80010, 0xffffffff }, 745 { 0x80018, 0x00001556 }, 746 { 0x8001C, 0x00001556 }, 747 }; 748 749 static const struct venus_resources msm8996_res = { 750 .freq_tbl = msm8996_freq_table, 751 .freq_tbl_size = ARRAY_SIZE(msm8996_freq_table), 752 .reg_tbl = msm8996_reg_preset, 753 .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), 754 .clks = {"core", "iface", "bus", "mbus" }, 755 .clks_num = 4, 756 .vcodec0_clks = { "core" }, 757 .vcodec1_clks = { "core" }, 758 .vcodec_clks_num = 1, 759 .max_load = 2563200, 760 .hfi_version = HFI_VERSION_3XX, 761 .vmem_id = VIDC_RESOURCE_NONE, 762 .vmem_size = 0, 763 .vmem_addr = 0, 764 .dma_mask = 0xddc00000 - 1, 765 .fwname = "qcom/venus-4.2/venus.mbn", 766 }; 767 768 static const struct freq_tbl msm8998_freq_table[] = { 769 { 1728000, 533000000 }, /* 4k UHD @ 60 (decode only) */ 770 { 1036800, 444000000 }, /* 2k @ 120 */ 771 { 829440, 355200000 }, /* 4k @ 44 */ 772 { 489600, 269330000 },/* 4k @ 30 */ 773 { 108000, 200000000 }, /* 1080p @ 60 */ 774 }; 775 776 static const struct reg_val msm8998_reg_preset[] = { 777 { 0x80124, 0x00000003 }, 778 { 0x80550, 0x01111111 }, 779 { 0x80560, 0x01111111 }, 780 { 0x80568, 0x01111111 }, 781 { 0x80570, 0x01111111 }, 782 { 0x80580, 0x01111111 }, 783 { 0x80588, 0x01111111 }, 784 { 0xe2010, 0x00000000 }, 785 }; 786 787 static const struct venus_resources msm8998_res = { 788 .freq_tbl = msm8998_freq_table, 789 .freq_tbl_size = ARRAY_SIZE(msm8998_freq_table), 790 .reg_tbl = msm8998_reg_preset, 791 .reg_tbl_size = ARRAY_SIZE(msm8998_reg_preset), 792 .clks = { "core", "iface", "bus", "mbus" }, 793 .clks_num = 4, 794 .vcodec0_clks = { "core" }, 795 .vcodec1_clks = { "core" }, 796 .vcodec_clks_num = 1, 797 .max_load = 2563200, 798 .hfi_version = HFI_VERSION_3XX, 799 .vmem_id = VIDC_RESOURCE_NONE, 800 .vmem_size = 0, 801 .vmem_addr = 0, 802 .dma_mask = 0xddc00000 - 1, 803 .fwname = "qcom/venus-4.4/venus.mbn", 804 }; 805 806 static const struct freq_tbl sdm660_freq_table[] = { 807 { 979200, 518400000 }, 808 { 489600, 441600000 }, 809 { 432000, 404000000 }, 810 { 244800, 320000000 }, 811 { 216000, 269330000 }, 812 { 108000, 133330000 }, 813 }; 814 815 static const struct reg_val sdm660_reg_preset[] = { 816 { 0x80010, 0x001f001f }, 817 { 0x80018, 0x00000156 }, 818 { 0x8001c, 0x00000156 }, 819 }; 820 821 static const struct bw_tbl sdm660_bw_table_enc[] = { 822 { 979200, 1044000, 0, 2446336, 0 }, /* 4k UHD @ 30 */ 823 { 864000, 887000, 0, 2108416, 0 }, /* 720p @ 240 */ 824 { 489600, 666000, 0, 1207296, 0 }, /* 1080p @ 60 */ 825 { 432000, 578000, 0, 1058816, 0 }, /* 720p @ 120 */ 826 { 244800, 346000, 0, 616448, 0 }, /* 1080p @ 30 */ 827 { 216000, 293000, 0, 534528, 0 }, /* 720p @ 60 */ 828 { 108000, 151000, 0, 271360, 0 }, /* 720p @ 30 */ 829 }; 830 831 static const struct bw_tbl sdm660_bw_table_dec[] = { 832 { 979200, 2365000, 0, 1892000, 0 }, /* 4k UHD @ 30 */ 833 { 864000, 1978000, 0, 1554000, 0 }, /* 720p @ 240 */ 834 { 489600, 1133000, 0, 895000, 0 }, /* 1080p @ 60 */ 835 { 432000, 994000, 0, 781000, 0 }, /* 720p @ 120 */ 836 { 244800, 580000, 0, 460000, 0 }, /* 1080p @ 30 */ 837 { 216000, 501000, 0, 301000, 0 }, /* 720p @ 60 */ 838 { 108000, 255000, 0, 202000, 0 }, /* 720p @ 30 */ 839 }; 840 841 static const struct venus_resources sdm660_res = { 842 .freq_tbl = sdm660_freq_table, 843 .freq_tbl_size = ARRAY_SIZE(sdm660_freq_table), 844 .reg_tbl = sdm660_reg_preset, 845 .reg_tbl_size = ARRAY_SIZE(sdm660_reg_preset), 846 .bw_tbl_enc = sdm660_bw_table_enc, 847 .bw_tbl_enc_size = ARRAY_SIZE(sdm660_bw_table_enc), 848 .bw_tbl_dec = sdm660_bw_table_dec, 849 .bw_tbl_dec_size = ARRAY_SIZE(sdm660_bw_table_dec), 850 .clks = {"core", "iface", "bus", "bus_throttle" }, 851 .clks_num = 4, 852 .vcodec0_clks = { "vcodec0_core" }, 853 .vcodec1_clks = { "vcodec0_core" }, 854 .vcodec_clks_num = 1, 855 .vcodec_num = 1, 856 .max_load = 1036800, 857 .hfi_version = HFI_VERSION_3XX, 858 .vmem_id = VIDC_RESOURCE_NONE, 859 .vmem_size = 0, 860 .vmem_addr = 0, 861 .cp_start = 0, 862 .cp_size = 0x79000000, 863 .cp_nonpixel_start = 0x1000000, 864 .cp_nonpixel_size = 0x28000000, 865 .dma_mask = 0xd9000000 - 1, 866 .fwname = "qcom/venus-4.4/venus.mdt", 867 }; 868 869 static const struct freq_tbl sdm845_freq_table[] = { 870 { 3110400, 533000000 }, /* 4096x2160@90 */ 871 { 2073600, 444000000 }, /* 4096x2160@60 */ 872 { 1944000, 404000000 }, /* 3840x2160@60 */ 873 { 972000, 330000000 }, /* 3840x2160@30 */ 874 { 489600, 200000000 }, /* 1920x1080@60 */ 875 { 244800, 100000000 }, /* 1920x1080@30 */ 876 }; 877 878 static const struct bw_tbl sdm845_bw_table_enc[] = { 879 { 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */ 880 { 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */ 881 { 489600, 723000, 0, 973000, 0 }, /* 1920x1080@60 */ 882 { 244800, 370000, 0, 495000, 0 }, /* 1920x1080@30 */ 883 }; 884 885 static const struct bw_tbl sdm845_bw_table_dec[] = { 886 { 2073600, 3929000, 0, 5551000, 0 }, /* 4096x2160@60 */ 887 { 1036800, 1987000, 0, 2797000, 0 }, /* 4096x2160@30 */ 888 { 489600, 1040000, 0, 1298000, 0 }, /* 1920x1080@60 */ 889 { 244800, 530000, 0, 659000, 0 }, /* 1920x1080@30 */ 890 }; 891 892 static const struct venus_resources sdm845_res = { 893 .freq_tbl = sdm845_freq_table, 894 .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), 895 .bw_tbl_enc = sdm845_bw_table_enc, 896 .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), 897 .bw_tbl_dec = sdm845_bw_table_dec, 898 .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), 899 .clks = {"core", "iface", "bus" }, 900 .clks_num = 3, 901 .vcodec0_clks = { "core", "bus" }, 902 .vcodec1_clks = { "core", "bus" }, 903 .vcodec_clks_num = 2, 904 .max_load = 3110400, /* 4096x2160@90 */ 905 .hfi_version = HFI_VERSION_4XX, 906 .vpu_version = VPU_VERSION_AR50, 907 .vmem_id = VIDC_RESOURCE_NONE, 908 .vmem_size = 0, 909 .vmem_addr = 0, 910 .dma_mask = 0xe0000000 - 1, 911 .fwname = "qcom/venus-5.2/venus.mbn", 912 }; 913 914 static const struct venus_resources sdm845_res_v2 = { 915 .freq_tbl = sdm845_freq_table, 916 .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), 917 .bw_tbl_enc = sdm845_bw_table_enc, 918 .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), 919 .bw_tbl_dec = sdm845_bw_table_dec, 920 .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), 921 .clks = {"core", "iface", "bus" }, 922 .clks_num = 3, 923 .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, 924 .vcodec1_clks = { "vcodec1_core", "vcodec1_bus" }, 925 .vcodec_clks_num = 2, 926 .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, 927 .vcodec_pmdomains_num = 3, 928 .opp_pmdomain = (const char *[]) { "cx" }, 929 .opp_pmdomain_num = 1, 930 .vcodec_num = 2, 931 .max_load = 3110400, /* 4096x2160@90 */ 932 .hfi_version = HFI_VERSION_4XX, 933 .vpu_version = VPU_VERSION_AR50, 934 .vmem_id = VIDC_RESOURCE_NONE, 935 .vmem_size = 0, 936 .vmem_addr = 0, 937 .dma_mask = 0xe0000000 - 1, 938 .cp_start = 0, 939 .cp_size = 0x70800000, 940 .cp_nonpixel_start = 0x1000000, 941 .cp_nonpixel_size = 0x24800000, 942 .fwname = "qcom/venus-5.2/venus.mbn", 943 .dec_nodename = "video-core0", 944 .enc_nodename = "video-core1", 945 }; 946 947 static const struct freq_tbl sc7180_freq_table[] = { 948 { 0, 500000000 }, 949 { 0, 434000000 }, 950 { 0, 340000000 }, 951 { 0, 270000000 }, 952 { 0, 150000000 }, 953 }; 954 955 static const struct bw_tbl sc7180_bw_table_enc[] = { 956 { 972000, 750000, 0, 0, 0 }, /* 3840x2160@30 */ 957 { 489600, 451000, 0, 0, 0 }, /* 1920x1080@60 */ 958 { 244800, 234000, 0, 0, 0 }, /* 1920x1080@30 */ 959 }; 960 961 static const struct bw_tbl sc7180_bw_table_dec[] = { 962 { 1036800, 1386000, 0, 1875000, 0 }, /* 4096x2160@30 */ 963 { 489600, 865000, 0, 1146000, 0 }, /* 1920x1080@60 */ 964 { 244800, 530000, 0, 583000, 0 }, /* 1920x1080@30 */ 965 }; 966 967 static const struct venus_resources sc7180_res = { 968 .freq_tbl = sc7180_freq_table, 969 .freq_tbl_size = ARRAY_SIZE(sc7180_freq_table), 970 .bw_tbl_enc = sc7180_bw_table_enc, 971 .bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc), 972 .bw_tbl_dec = sc7180_bw_table_dec, 973 .bw_tbl_dec_size = ARRAY_SIZE(sc7180_bw_table_dec), 974 .clks = {"core", "iface", "bus" }, 975 .clks_num = 3, 976 .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, 977 .vcodec_clks_num = 2, 978 .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, 979 .vcodec_pmdomains_num = 2, 980 .opp_pmdomain = (const char *[]) { "cx" }, 981 .opp_pmdomain_num = 1, 982 .vcodec_num = 1, 983 .hfi_version = HFI_VERSION_4XX, 984 .vpu_version = VPU_VERSION_AR50, 985 .vmem_id = VIDC_RESOURCE_NONE, 986 .vmem_size = 0, 987 .vmem_addr = 0, 988 .dma_mask = 0xe0000000 - 1, 989 .cp_start = 0, 990 .cp_size = 0x70800000, 991 .cp_nonpixel_start = 0x1000000, 992 .cp_nonpixel_size = 0x24800000, 993 .fwname = "qcom/venus-5.4/venus.mbn", 994 .dec_nodename = "video-decoder", 995 .enc_nodename = "video-encoder", 996 }; 997 998 #if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) 999 static const struct freq_tbl sm8250_freq_table[] = { 1000 { 0, 444000000 }, 1001 { 0, 366000000 }, 1002 { 0, 338000000 }, 1003 { 0, 240000000 }, 1004 }; 1005 1006 static const struct bw_tbl sm8250_bw_table_enc[] = { 1007 { 1944000, 1954000, 0, 3711000, 0 }, /* 3840x2160@60 */ 1008 { 972000, 996000, 0, 1905000, 0 }, /* 3840x2160@30 */ 1009 { 489600, 645000, 0, 977000, 0 }, /* 1920x1080@60 */ 1010 { 244800, 332000, 0, 498000, 0 }, /* 1920x1080@30 */ 1011 }; 1012 1013 static const struct bw_tbl sm8250_bw_table_dec[] = { 1014 { 2073600, 2403000, 0, 4113000, 0 }, /* 4096x2160@60 */ 1015 { 1036800, 1224000, 0, 2079000, 0 }, /* 4096x2160@30 */ 1016 { 489600, 812000, 0, 998000, 0 }, /* 1920x1080@60 */ 1017 { 244800, 416000, 0, 509000, 0 }, /* 1920x1080@30 */ 1018 }; 1019 1020 static const struct reg_val sm8250_reg_preset[] = { 1021 { 0xb0088, 0 }, 1022 }; 1023 1024 static const struct venus_resources sm8250_res = { 1025 .freq_tbl = sm8250_freq_table, 1026 .freq_tbl_size = ARRAY_SIZE(sm8250_freq_table), 1027 .reg_tbl = sm8250_reg_preset, 1028 .reg_tbl_size = ARRAY_SIZE(sm8250_reg_preset), 1029 .bw_tbl_enc = sm8250_bw_table_enc, 1030 .bw_tbl_enc_size = ARRAY_SIZE(sm8250_bw_table_enc), 1031 .bw_tbl_dec = sm8250_bw_table_dec, 1032 .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), 1033 .clks = {"core", "iface"}, 1034 .clks_num = 2, 1035 .resets = { "bus", "core" }, 1036 .resets_num = 2, 1037 .vcodec0_clks = { "vcodec0_core" }, 1038 .vcodec_clks_num = 1, 1039 .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, 1040 .vcodec_pmdomains_num = 2, 1041 .opp_pmdomain = (const char *[]) { "mx", "mmcx" }, 1042 .opp_pmdomain_num = 2, 1043 .vcodec_num = 1, 1044 .max_load = 7833600, 1045 .hfi_version = HFI_VERSION_6XX, 1046 .vpu_version = VPU_VERSION_IRIS2, 1047 .num_vpp_pipes = 4, 1048 .vmem_id = VIDC_RESOURCE_NONE, 1049 .vmem_size = 0, 1050 .vmem_addr = 0, 1051 .dma_mask = 0xe0000000 - 1, 1052 .fwname = "qcom/vpu-1.0/venus.mbn", 1053 .dec_nodename = "video-decoder", 1054 .enc_nodename = "video-encoder", 1055 }; 1056 1057 static const struct freq_tbl sc7280_freq_table[] = { 1058 { 0, 460000000 }, 1059 { 0, 424000000 }, 1060 { 0, 335000000 }, 1061 { 0, 240000000 }, 1062 { 0, 133333333 }, 1063 }; 1064 1065 static const struct bw_tbl sc7280_bw_table_enc[] = { 1066 { 1944000, 1896000, 0, 3657000, 0 }, /* 3840x2160@60 */ 1067 { 972000, 968000, 0, 1848000, 0 }, /* 3840x2160@30 */ 1068 { 489600, 618000, 0, 941000, 0 }, /* 1920x1080@60 */ 1069 { 244800, 318000, 0, 480000, 0 }, /* 1920x1080@30 */ 1070 }; 1071 1072 static const struct bw_tbl sc7280_bw_table_dec[] = { 1073 { 2073600, 2128000, 0, 3831000, 0 }, /* 4096x2160@60 */ 1074 { 1036800, 1085000, 0, 1937000, 0 }, /* 4096x2160@30 */ 1075 { 489600, 779000, 0, 998000, 0 }, /* 1920x1080@60 */ 1076 { 244800, 400000, 0, 509000, 0 }, /* 1920x1080@30 */ 1077 }; 1078 1079 static const struct reg_val sm7280_reg_preset[] = { 1080 { 0xb0088, 0 }, 1081 }; 1082 1083 static const struct hfi_ubwc_config sc7280_ubwc_config = { 1084 0, 0, {1, 1, 1, 0, 0, 0}, 8, 32, 14, 0, 0, {0, 0} 1085 }; 1086 1087 static const struct venus_resources sc7280_res = { 1088 .freq_tbl = sc7280_freq_table, 1089 .freq_tbl_size = ARRAY_SIZE(sc7280_freq_table), 1090 .reg_tbl = sm7280_reg_preset, 1091 .reg_tbl_size = ARRAY_SIZE(sm7280_reg_preset), 1092 .bw_tbl_enc = sc7280_bw_table_enc, 1093 .bw_tbl_enc_size = ARRAY_SIZE(sc7280_bw_table_enc), 1094 .bw_tbl_dec = sc7280_bw_table_dec, 1095 .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), 1096 .ubwc_conf = &sc7280_ubwc_config, 1097 .clks = {"core", "bus", "iface"}, 1098 .clks_num = 3, 1099 .vcodec0_clks = {"vcodec_core", "vcodec_bus"}, 1100 .vcodec_clks_num = 2, 1101 .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, 1102 .vcodec_pmdomains_num = 2, 1103 .opp_pmdomain = (const char *[]) { "cx" }, 1104 .opp_pmdomain_num = 1, 1105 .vcodec_num = 1, 1106 .hfi_version = HFI_VERSION_6XX, 1107 .vpu_version = VPU_VERSION_IRIS2_1, 1108 .num_vpp_pipes = 1, 1109 .vmem_id = VIDC_RESOURCE_NONE, 1110 .vmem_size = 0, 1111 .vmem_addr = 0, 1112 .dma_mask = 0xe0000000 - 1, 1113 .cp_start = 0, 1114 .cp_size = 0x25800000, 1115 .cp_nonpixel_start = 0x1000000, 1116 .cp_nonpixel_size = 0x24800000, 1117 .fwname = "qcom/vpu-2.0/venus.mbn", 1118 .dec_nodename = "video-decoder", 1119 .enc_nodename = "video-encoder", 1120 }; 1121 #endif 1122 1123 static const struct bw_tbl qcm2290_bw_table_dec[] = { 1124 { 352800, 597000, 0, 746000, 0 }, /* 1080p@30 + 720p@30 */ 1125 { 244800, 413000, 0, 516000, 0 }, /* 1080p@30 */ 1126 { 216000, 364000, 0, 454000, 0 }, /* 720p@60 */ 1127 { 108000, 182000, 0, 227000, 0 }, /* 720p@30 */ 1128 }; 1129 1130 static const struct bw_tbl qcm2290_bw_table_enc[] = { 1131 { 352800, 396000, 0, 0, 0 }, /* 1080p@30 + 720p@30 */ 1132 { 244800, 275000, 0, 0, 0 }, /* 1080p@30 */ 1133 { 216000, 242000, 0, 0, 0 }, /* 720p@60 */ 1134 { 108000, 121000, 0, 0, 0 }, /* 720p@30 */ 1135 }; 1136 1137 static const struct firmware_version min_fw = { 1138 .major = 6, .minor = 0, .rev = 55, 1139 }; 1140 1141 static const struct venus_resources qcm2290_res = { 1142 .bw_tbl_dec = qcm2290_bw_table_dec, 1143 .bw_tbl_dec_size = ARRAY_SIZE(qcm2290_bw_table_dec), 1144 .bw_tbl_enc = qcm2290_bw_table_enc, 1145 .bw_tbl_enc_size = ARRAY_SIZE(qcm2290_bw_table_enc), 1146 .clks = { "core", "iface", "bus", "throttle" }, 1147 .clks_num = 4, 1148 .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, 1149 .vcodec_clks_num = 2, 1150 .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, 1151 .vcodec_pmdomains_num = 2, 1152 .opp_pmdomain = (const char *[]) { "cx" }, 1153 .opp_pmdomain_num = 1, 1154 .vcodec_num = 1, 1155 .hfi_version = HFI_VERSION_4XX, 1156 .vpu_version = VPU_VERSION_AR50_LITE, 1157 .max_load = 352800, 1158 .num_vpp_pipes = 1, 1159 .vmem_id = VIDC_RESOURCE_NONE, 1160 .vmem_size = 0, 1161 .vmem_addr = 0, 1162 .cp_start = 0, 1163 .cp_size = 0x70800000, 1164 .cp_nonpixel_start = 0x1000000, 1165 .cp_nonpixel_size = 0x24800000, 1166 .dma_mask = 0xe0000000 - 1, 1167 .fwname = "qcom/venus-6.0/venus.mbn", 1168 .dec_nodename = "video-decoder", 1169 .enc_nodename = "video-encoder", 1170 .min_fw = &min_fw, 1171 }; 1172 1173 static const struct of_device_id venus_dt_match[] = { 1174 { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, 1175 { .compatible = "qcom,msm8939-venus", .data = &msm8939_res, }, 1176 { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, 1177 { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, 1178 { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, }, 1179 { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, 1180 { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, 1181 { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, 1182 { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, 1183 #if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) 1184 { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, 1185 { .compatible = "qcom,sm8250-venus", .data = &sm8250_res, }, 1186 #endif 1187 { } 1188 }; 1189 MODULE_DEVICE_TABLE(of, venus_dt_match); 1190 1191 static struct platform_driver qcom_venus_driver = { 1192 .probe = venus_probe, 1193 .remove = venus_remove, 1194 .driver = { 1195 .name = "qcom-venus", 1196 .of_match_table = venus_dt_match, 1197 .pm = &venus_pm_ops, 1198 }, 1199 .shutdown = venus_core_shutdown, 1200 }; 1201 module_platform_driver(qcom_venus_driver); 1202 1203 MODULE_DESCRIPTION("Qualcomm Venus video encoder and decoder driver"); 1204 MODULE_LICENSE("GPL v2"); 1205