/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #define LOG_TAG "ddp_drv" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ION */ /* #include */ /* #include */ #include #include #include #include #include #include //#include "mt-plat/mtk_smi.h" /* #include */ /* #include */ #include "ddp_clkmgr.h" /* #include "mach/mt_irq.h" */ #include "mt-plat/sync_write.h" //#include "mt-plat/mtk_smi.h" #include "ddp_drv.h" #include "ddp_reg.h" #include "ddp_hal.h" #include "ddp_log.h" #include "ddp_irq.h" #include "ddp_info.h" #include "ddp_m4u.h" #include "display_recorder.h" /* #define DISP_NO_DPI */ #ifndef DISP_NO_DPI #include "ddp_dpi_reg.h" #endif #include "disp_helper.h" #include #include "smi_public.h" #define DISP_DEVNAME "DISPSYS" #define OCCUPIED_BW_RATIO 1330 /* device and driver */ static dev_t disp_devno; static struct cdev *disp_cdev; static struct class *disp_class; struct disp_node_struct { pid_t open_pid; pid_t open_tgid; struct list_head testList; spinlock_t node_lock; }; static struct platform_device mydev; cmdqBackupSlotHandle dispsys_slot; static int _disp_init_cmdq_slots(cmdqBackupSlotHandle *pSlot, int count, int init_val) { int i; cmdqBackupAllocateSlot(pSlot, count); for (i = 0; i < count; i++) cmdqBackupWriteSlot(*pSlot, i, init_val); return 0; } static int _disp_get_cmdq_slots(cmdqBackupSlotHandle Slot, unsigned int slot_index, unsigned int *value) { int ret; ret = cmdqBackupReadSlot(Slot, slot_index, value); /* cmdq get slot fail */ if (ret) DDPERR("DISP CMDQ get slot failed:%d\n", ret); return ret; } int disp_get_ovl_bandwidth(unsigned long long in_fps, unsigned long long out_fps, unsigned long long *bandwidth) { int ret = 0; unsigned int is_dc; unsigned int ovl0_bw, ovl0_2l_bw, rdma0_bw, wdma0_bw; ret |= _disp_get_cmdq_slots(DISPSYS_SLOT_BASE, DISP_SLOT_IS_DC, &is_dc); ret |= _disp_get_cmdq_slots(DISPSYS_SLOT_BASE, DISP_SLOT_OVL0_BW, &ovl0_bw); ret |= _disp_get_cmdq_slots(DISPSYS_SLOT_BASE, DISP_SLOT_OVL0_2L_BW, &ovl0_2l_bw); ret |= _disp_get_cmdq_slots(DISPSYS_SLOT_BASE, DISP_SLOT_RDMA0_BW, &rdma0_bw); ret |= _disp_get_cmdq_slots(DISPSYS_SLOT_BASE, DISP_SLOT_WDMA0_BW, &wdma0_bw); /* cmdq get slot fail */ if (ret) { DDPERR("DISP CMDQ get slot failed:%d\n", ret); *bandwidth = 0; } else { if (is_dc) *bandwidth = ((((unsigned long long)ovl0_bw + (unsigned long long)ovl0_2l_bw + (unsigned long long)wdma0_bw) * in_fps) + ((unsigned long long)rdma0_bw * out_fps)) * OCCUPIED_BW_RATIO; else *bandwidth = ((unsigned long long)ovl0_bw + (unsigned long long)ovl0_2l_bw) * out_fps * OCCUPIED_BW_RATIO; do_div(*bandwidth, 1000 * 1000); if (is_dc) DISPDBG( "%s ovl0:%d, ovl0_2l:%d, wdma0:%d, rdma0:%d, in_fps:%llu, out_fps:%llu, bw:%llu\n", __func__, ovl0_bw, ovl0_2l_bw, wdma0_bw, rdma0_bw, in_fps, out_fps, *bandwidth); else DISPDBG( "%s ovl0:%d, ovl0_2l:%d, out_fps:%llu, bw:%llu\n", __func__, ovl0_bw, ovl0_2l_bw, out_fps, *bandwidth); } return ret; } int disp_get_rdma_bandwidth(unsigned long long out_fps, unsigned long long *bandwidth) { int ret = 0; unsigned int rdma0_bw; ret = _disp_get_cmdq_slots(DISPSYS_SLOT_BASE, DISP_SLOT_RDMA0_BW, &rdma0_bw); /* cmdq get slot fail */ if (ret) { DDPERR("DISP CMDQ get slot failed:%d\n", ret); *bandwidth = 0; } else { *bandwidth = (unsigned long long)rdma0_bw * out_fps * OCCUPIED_BW_RATIO; do_div(*bandwidth, 1000 * 1000); DISPDBG( "%s rdma0:%d, out_fps:%llu, bw:%llu\n", __func__, rdma0_bw, out_fps, *bandwidth); } return ret; } static long disp_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return 0; } static long disp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { #if defined(CONFIG_TRUSTONIC_TEE_SUPPORT) && \ defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT) if (cmd == DISP_IOCTL_SET_TPLAY_HANDLE) return disp_unlocked_ioctl(file, cmd, arg); #endif return 0; } static int disp_open(struct inode *inode, struct file *file) { struct disp_node_struct *pNode = NULL; DDPDBG("enter %s process:%s\n", __func__, current->comm); /* Allocate and initialize private data */ file->private_data = kmalloc(sizeof(struct disp_node_struct), GFP_ATOMIC); if (file->private_data == NULL) { DDPMSG("Not enough entry for DDP open operation\n"); return -ENOMEM; } pNode = (struct disp_node_struct *)file->private_data; pNode->open_pid = current->pid; pNode->open_tgid = current->tgid; INIT_LIST_HEAD(&(pNode->testList)); spin_lock_init(&pNode->node_lock); return 0; } static ssize_t disp_read(struct file *file, char __user *data, size_t len, loff_t *ppos) { return 0; } static int disp_release(struct inode *inode, struct file *file) { struct disp_node_struct *pNode = NULL; /* unsigned int index = 0; */ DDPDBG("enter %s process:%s\n", __func__, current->comm); pNode = (struct disp_node_struct *)file->private_data; spin_lock(&pNode->node_lock); spin_unlock(&pNode->node_lock); if (file->private_data != NULL) { kfree(file->private_data); file->private_data = NULL; } return 0; } static int disp_flush(struct file *file, fl_owner_t a_id) { return 0; } /* remap register to user space */ #if defined(CONFIG_MT_ENG_BUILD) static int disp_mmap(struct file *file, struct vm_area_struct *a_pstVMArea) { #if (defined(CONFIG_MTK_TEE_GP_SUPPORT) || \ defined(CONFIG_TRUSTONIC_TEE_SUPPORT)) && \ defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT) a_pstVMArea->vm_page_prot = pgprot_noncached(a_pstVMArea->vm_page_prot); if (remap_pfn_range(a_pstVMArea, a_pstVMArea->vm_start, a_pstVMArea->vm_pgoff, (a_pstVMArea->vm_end - a_pstVMArea->vm_start), a_pstVMArea->vm_page_prot)) { DDPERR("MMAP failed!!\n"); return -1; } #endif return 0; } #endif struct dispsys_device { struct device *dev; }; struct device *disp_get_device(void) { return &(mydev.dev); } #if (defined(CONFIG_MTK_TEE_GP_SUPPORT) || \ defined(CONFIG_TRUSTONIC_TEE_SUPPORT)) && \ defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT) static struct miscdevice disp_misc_dev; #endif /* Kernel interface */ static const struct file_operations disp_fops = { .owner = THIS_MODULE, .unlocked_ioctl = disp_unlocked_ioctl, .compat_ioctl = disp_compat_ioctl, .open = disp_open, .release = disp_release, .flush = disp_flush, .read = disp_read, #if defined(CONFIG_MT_ENG_BUILD) .mmap = disp_mmap #endif }; /* disp_clk_init * 1. parsing dtsi * 2. clk force on */ static void disp_clk_init(struct platform_device *pdev) { #ifdef ENABLE_CLK_MGR int i; struct clk *pclk = NULL; DDPMSG("DT disp clk parse beign\n"); for (i = 0; i < MAX_DISP_CLK_CNT; i++) { DDPMSG("DISPSYS get clock %s\n", ddp_get_clk_name(i)); pclk = devm_clk_get(&pdev->dev, ddp_get_clk_name(i)); if (IS_ERR(pclk)) { DDPERR("%s:%d, DISPSYS get %d,%s clock error!!!\n", __FILE__, __LINE__, i, ddp_get_clk_name(i)); continue; } ddp_set_clk_handle(pclk, i); } DDPMSG("DT disp clk parse end\n"); #endif } #ifdef CONFIG_MTK_IOMMU_V3 static struct disp_iommu_device disp_iommu; struct disp_iommu_device *disp_get_iommu_dev(void) { struct device_node *larb_node[DISP_LARB_COUNT]; struct platform_device *larb_pdev[DISP_LARB_COUNT]; int larb_idx = 0; struct device_node *np; if (disp_iommu.inited) return &disp_iommu; for (larb_idx = 0; larb_idx < DISP_LARB_COUNT; larb_idx++) { larb_node[larb_idx] = of_parse_phandle(mydev.dev.of_node, "mediatek,larb", larb_idx); if (!larb_node[larb_idx]) { DDPERR("disp driver get larb %d fail\n", larb_idx); return NULL; } larb_pdev[larb_idx] = of_find_device_by_node(larb_node[larb_idx]); of_node_put(larb_node[larb_idx]); if ((!larb_pdev[larb_idx]) || (!larb_pdev[larb_idx]->dev.driver)) { if (!larb_pdev[larb_idx]) DDPERR("earlier than SMI, larb_pdev null\n"); else DDPERR("earlier than SMI, larb drv null\n"); } disp_iommu.larb_pdev[larb_idx] = larb_pdev[larb_idx]; } /* add for mmp dump mva->pa */ np = of_find_compatible_node(NULL, NULL, "mediatek,mt-pseudo_m4u-port"); if (np == NULL) { DDPERR("DT,mediatek,mt-pseudo_m4u-port is not found\n"); } else { disp_iommu.iommu_pdev = of_find_device_by_node(np); of_node_put(np); if (!disp_iommu.iommu_pdev) DDPERR("get iommu device failed\n"); } disp_iommu.inited = 1; return &disp_iommu; } #endif /* begin for irq check */ static inline unsigned int gic_irq(struct irq_data *d) { return d->hwirq; } static inline unsigned int virq_to_hwirq(unsigned int virq) { struct irq_desc *desc; unsigned int hwirq = 0; desc = irq_to_desc(virq); if (!desc) WARN_ON(1); else hwirq = gic_irq(&desc->irq_data); return hwirq; } /* end for irq check */ static int disp_probe_1(void) { int ret = 0; int i; unsigned long va; unsigned int irq; pr_info("disp driver(1) %s begin\n", __func__); #if (defined(CONFIG_MTK_TEE_GP_SUPPORT) || \ defined(CONFIG_TRUSTONIC_TEE_SUPPORT)) && \ defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT) disp_misc_dev.minor = MISC_DYNAMIC_MINOR; disp_misc_dev.name = "mtk_disp"; disp_misc_dev.fops = &disp_fops; disp_misc_dev.parent = NULL; ret = misc_register(&disp_misc_dev); if (ret) { pr_info("disp: fail to create mtk_disp node\n"); return (unsigned long)(ERR_PTR(ret)); } #endif /* do disp_init_irq before register irq */ disp_init_irq(); /* iomap registers and irq */ for (i = 0; i < DISP_MODULE_NUM; i++) { int status; struct device_node *node = NULL; struct resource res; if (!is_ddp_module_has_reg_info(i)) continue; node = of_find_compatible_node(NULL, NULL, ddp_get_module_dtname(i)); if (node == NULL) { DDPERR("DT, i=%d, module=%s, can't find %s node\n", i, ddp_get_module_name(i), ddp_get_module_dtname(i)); continue; } va = (unsigned long)of_iomap(node, 0); if (!va) { DDPERR("DT, i=%d, module=%s, can't get VA\n", i, ddp_get_module_name(i)); continue; } else { ddp_set_module_va(i, va); } status = of_address_to_resource(node, 0, &res); if (status < 0) { DDPERR("DT, i=%d, module=%s, unable to get PA\n", i, ddp_get_module_name(i)); continue; } if (ddp_get_module_pa(i) != res.start) DDPERR("DT, i=%d, module=%s, VA=%p, PA=0x%lx!=0x%pa\n", i, ddp_get_module_name(i), (void *)ddp_get_module_va(i), ddp_get_module_pa(i), (void *)(uintptr_t)res.start); /* get IRQ ID and request IRQ */ irq = irq_of_parse_and_map(node, 0); ddp_set_module_irq(i, irq); DDPMSG("DT,i=%d, module=%s, VA=%p map_irq=%d, PA=%lx irq:%u\n", i, ddp_get_module_name(i), (void *)ddp_get_module_va(i), ddp_get_module_irq(i), ddp_get_module_pa(i), irq); } /* initialize display slot */ if (disp_helper_get_option(DISP_OPT_USE_CMDQ)) _disp_init_cmdq_slots(&(dispsys_slot), DISP_SLOT_NUM, 0); /* register irq */ for (i = 0; i < DISP_MODULE_NUM; i++) { if (ddp_is_irq_enable(i) != 1) continue; if (ddp_get_module_irq(i) == 0) { DDPERR("DT, i=%d, module=%s, map_irq=%d\n", i, ddp_get_module_name(i), ddp_get_module_irq(i)); ddp_module_irq_disable(i); continue; } if (ddp_get_module_checkirq(i) != virq_to_hwirq(ddp_get_module_irq(i))) { DDPERR( "[ERR]DT, i=%d, module=%s, map_irq=%d, virtohw_irq=%d, check_irq=%d\n", i, ddp_get_module_name(i), ddp_get_module_irq(i), virq_to_hwirq(ddp_get_module_irq(i)), ddp_get_module_checkirq(i)); ddp_module_irq_disable(i); continue; } /* IRQF_TRIGGER_NONE dose not take effect here, * real trigger mode set in dts file */ ret = request_irq(ddp_get_module_irq(i), (irq_handler_t)disp_irq_handler, IRQF_TRIGGER_NONE, ddp_get_module_name(i), NULL); if (ret) { DDPERR("DT, i=%d, module=%s, request_irq(%d) fail\n", i, ddp_get_module_name(i), ddp_get_module_irq(i)); continue; } DDPMSG("irq enabled, module=%s, irq=%d\n", ddp_get_module_name(i), ddp_get_module_irq(i)); } /* power on MMSYS for early porting */ if (disp_helper_get_stage() != DISP_HELPER_STAGE_NORMAL) { DDPMSG("[FPGA Only] before power on MMSYS:0x%x,0x%x,0x%x\n", DISP_REG_GET(DISP_REG_CONFIG_MMSYS_CG_CON0), DISP_REG_GET(DISP_REG_CONFIG_MMSYS_CG_CON1), DISP_REG_GET(DISP_REG_CONFIG_MMSYS_CG_CON2)); DISP_REG_SET(NULL, DISP_REG_CONFIG_MMSYS_CG_CLR0, 0xFFFFFFFF); DISP_REG_SET(NULL, DISP_REG_CONFIG_MMSYS_CG_CLR1, 0xFFFFFFFF); DISP_REG_SET(NULL, DISP_REG_CONFIG_MMSYS_CG_CLR2, 0xFFFFFFFF); DDPMSG("[FPGA Only] after power on MMSYS:0x%x,0x%x,0x%x\n", DISP_REG_GET(DISP_REG_CONFIG_MMSYS_CG_CON0), DISP_REG_GET(DISP_REG_CONFIG_MMSYS_CG_CON1), DISP_REG_GET(DISP_REG_CONFIG_MMSYS_CG_CON2)); } ddp_path_init(); disp_m4u_init(); pr_info("disp driver(1) %s end\n", __func__); /* NOT_REFERENCED(class_dev); */ return ret; } static int disp_probe(struct platform_device *pdev) { static unsigned int disp_probe_cnt; if (disp_helper_get_stage() == DISP_HELPER_STAGE_NORMAL) { pr_info("%s: %d\n", __func__, smi_mm_first_get()); if (!smi_mm_first_get()) { pr_notice("SMI not start probe\n"); return -EPROBE_DEFER; } } if (disp_probe_cnt != 0) return 0; pr_info("disp driver(1) %s begin\n", __func__); /* save pdev for disp_probe_1 */ memcpy(&mydev, pdev, sizeof(mydev)); disp_helper_option_init(); if (disp_helper_get_stage() == DISP_HELPER_STAGE_NORMAL) disp_clk_init(pdev); disp_probe_cnt++; pr_info("disp driver(1) %s end\n", __func__); disp_probe_1(); return 0; } static int disp_remove(struct platform_device *pdev) { #if (defined(CONFIG_MTK_TEE_GP_SUPPORT) || \ defined(CONFIG_TRUSTONIC_TEE_SUPPORT)) && \ defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT) misc_deregister(&disp_misc_dev); #endif return 0; } static void disp_shutdown(struct platform_device *pdev) { /* Nothing yet */ } /* PM suspend */ static int disp_suspend(struct platform_device *pdev, pm_message_t mesg) { return 0; } /* PM resume */ static int disp_resume(struct platform_device *pdev) { return 0; } static const struct of_device_id dispsys_of_ids[] = { {.compatible = "mediatek,dispsys",}, {} }; static struct platform_driver dispsys_of_driver = { .driver = { .name = DISP_DEVNAME, .owner = THIS_MODULE, .of_match_table = dispsys_of_ids, }, .probe = disp_probe, .remove = disp_remove, .shutdown = disp_shutdown, .suspend = disp_suspend, .resume = disp_resume, }; static int __init disp_init(void) { int ret = 0; init_log_buffer(); DDPMSG("Register the disp driver\n"); if (platform_driver_register(&dispsys_of_driver)) { DDPERR("failed to register disp driver\n"); /* platform_device_unregister(&disp_device); */ ret = -ENODEV; return ret; } DDPMSG("disp driver init done\n"); return 0; } static void __exit disp_exit(void) { ASSERT(0); /* disp-clk force on disable ??? */ cdev_del(disp_cdev); unregister_chrdev_region(disp_devno, 1); platform_driver_unregister(&dispsys_of_driver); device_destroy(disp_class, disp_devno); class_destroy(disp_class); } static int __init disp_late(void) { int ret = 0; DDPMSG("disp driver(1) %s begin\n", __func__); /* for rt5081 */ ret = display_bias_regulator_init(); if (ret < 0) pr_info("get dsv_pos fail, ret = %d\n", ret); disp_late_bias_enable(); DDPMSG("disp driver(1) %s end\n", __func__); return 0; } #ifndef MTK_FB_DO_NOTHING module_init(disp_init); module_exit(disp_exit); late_initcall(disp_late); #endif MODULE_AUTHOR("Tzu-Meng, Chung "); MODULE_DESCRIPTION("Display subsystem Driver"); MODULE_LICENSE("GPL");