/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #include "mdp_cmdq_helper_ext.h" #include "cmdq_reg.h" #include "mdp_cmdq_device.h" #include "cmdq_virtual.h" #include #ifdef CMDQ_CG_M4U_LARB0 #include "m4u.h" #endif #ifdef CONFIG_MTK_SMI_EXT #include "smi_public.h" #endif static struct cmdqCoreFuncStruct gFunctionPointer; u64 cmdq_virtual_flag_from_scenario_default(enum CMDQ_SCENARIO_ENUM scn) { u64 flag = 0; switch (scn) { case CMDQ_SCENARIO_TRIGGER_LOOP: /* Trigger loop does not related to any HW by itself. */ flag = 0LL; break; case CMDQ_SCENARIO_USER_SPACE: /* user space case, engine flag is passed seprately */ flag = 0LL; break; case CMDQ_SCENARIO_DEBUG: case CMDQ_SCENARIO_DEBUG_PREFETCH: flag = 0LL; break; case CMDQ_SCENARIO_USER_DISP_COLOR: /* color path */ flag = 0LL; break; default: if (scn < 0 || scn >= CMDQ_MAX_SCENARIO_COUNT) { /* Error status print */ CMDQ_ERR("Unknown scenario type %d\n", scn); } flag = 0LL; break; } return flag; } const char *cmdq_virtual_parse_module_from_reg_addr_legacy(u32 reg_addr) { const u32 addr_base_and_page = (reg_addr & 0xFFFFF000); /* for well-known base, we check them with 12-bit mask * defined in mt_reg_base.h * TODO: comfirm with SS if IO_VIRT_TO_PHYS workable when enable device * tree? */ switch (addr_base_and_page) { case 0x14000000: return "MMSYS"; case 0x14001000: return "MDP_RDMA0"; case 0x14002000: return "MDP_RDMA1"; case 0x14003000: return "MDP_RSZ0"; case 0x14004000: return "MDP_RSZ1"; case 0x14005000: return "MDP_RSZ2"; case 0x14006000: return "MDP_WDMA"; case 0x14007000: return "MDP_WROT0"; case 0x14008000: return "MDP_WROT1"; case 0x14009000: return "MDP_TDSHP0"; case 0x1400A000: return "MDP_TDSHP1"; case 0x1400B000: return "MDP_CROP"; case 0x1400C000: return "DISP_OVL0"; case 0x1400D000: return "DISP_OVL1"; case 0x14013000: return "COLOR0"; case 0x14014000: return "COLOR1"; case 0x14015000: return "AAL"; case 0x14016000: return "GAMA"; case 0x14020000: return "MMSYS_MUTEX"; case 0x18000000: return "VENC_GCON"; case 0x18002000: return "VENC"; case 0x18003000: return "JPGENC"; case 0x18004000: return "JPGDEC"; } /* for other register address we rely on GCE subsys to group them * with 16-bit mask. */ return cmdq_core_parse_subsys_from_reg_addr(reg_addr); } /* * GCE capability */ u32 cmdq_virtual_get_subsys_LSB_in_arg_a(void) { return 16; } /* HW thread related */ bool cmdq_virtual_is_a_secure_thread(const s32 thread) { #ifdef CMDQ_SECURE_PATH_SUPPORT if ((thread >= CMDQ_MIN_SECURE_THREAD_ID) && (thread < CMDQ_MIN_SECURE_THREAD_ID + CMDQ_MAX_SECURE_THREAD_COUNT)) { return true; } #endif return false; } /** * Scenario related * */ bool cmdq_virtual_is_disp_scenario(const enum CMDQ_SCENARIO_ENUM scenario) { bool dispScenario = false; switch (scenario) { case CMDQ_SCENARIO_TRIGGER_LOOP: case CMDQ_SCENARIO_USER_DISP_COLOR: dispScenario = true; break; default: break; } /* freely dispatch */ return dispScenario; } bool cmdq_virtual_is_dynamic_scenario( const enum CMDQ_SCENARIO_ENUM scenario) { bool dynamic_thread; switch (scenario) { case CMDQ_SCENARIO_USER_SPACE: case CMDQ_SCENARIO_USER_MDP: case CMDQ_SCENARIO_DEBUG_MDP: dynamic_thread = true; break; default: dynamic_thread = false; break; } return dynamic_thread; } int cmdq_virtual_disp_thread(enum CMDQ_SCENARIO_ENUM scenario) { switch (scenario) { /* HACK: force debug into 0/1 thread */ case CMDQ_SCENARIO_DEBUG_PREFETCH: /* primary config: thread 0 */ return 0; case CMDQ_SCENARIO_USER_DISP_COLOR: return 4; case CMDQ_SCENARIO_TRIGGER_LOOP: return 7; default: /* freely dispatch */ return CMDQ_INVALID_THREAD; } /* freely dispatch */ return CMDQ_INVALID_THREAD; } int cmdq_virtual_get_thread_index(enum CMDQ_SCENARIO_ENUM scenario, const bool secure) { if (!secure) return cmdq_get_func()->dispThread(scenario); /* dispatch secure thread according to scenario */ switch (scenario) { case CMDQ_SCENARIO_DEBUG_PREFETCH: /* CMDQ_MIN_SECURE_THREAD_ID */ return CMDQ_THREAD_SEC_PRIMARY_DISP; case CMDQ_SCENARIO_USER_MDP: case CMDQ_SCENARIO_USER_SPACE: case CMDQ_SCENARIO_DEBUG: case CMDQ_SCENARIO_DEBUG_MDP: /* because there is one input engine for MDP, reserve one * secure thread is enough */ return CMDQ_THREAD_SEC_MDP; case CMDQ_SCENARIO_ISP_FDVT: case CMDQ_SCENARIO_ISP_FDVT_OFF: return CMDQ_THREAD_SEC_ISP; default: CMDQ_ERR("no dedicated secure thread for senario:%d\n", scenario); return CMDQ_INVALID_THREAD; } } enum CMDQ_HW_THREAD_PRIORITY_ENUM cmdq_virtual_priority_from_scenario( enum CMDQ_SCENARIO_ENUM scenario) { switch (scenario) { case CMDQ_SCENARIO_USER_DISP_COLOR: /* currently, a prefetch thread is always in high priority. */ return CMDQ_THR_PRIO_DISPLAY_CONFIG; /* HACK: force debug into 0/1 thread */ case CMDQ_SCENARIO_DEBUG_PREFETCH: return CMDQ_THR_PRIO_DISPLAY_CONFIG; default: /* other cases need exta logic, see below. */ break; } if (cmdq_get_func()->is_disp_loop(scenario)) return CMDQ_THR_PRIO_DISPLAY_TRIGGER; else return CMDQ_THR_PRIO_NORMAL; } bool cmdq_virtual_is_disp_loop(enum CMDQ_SCENARIO_ENUM scenario) { bool is_disp_loop = false; if (scenario == CMDQ_SCENARIO_TRIGGER_LOOP) is_disp_loop = true; return is_disp_loop; } /** * Module dependent * */ void cmdq_virtual_get_reg_id_from_hwflag(u64 hwflag, enum cmdq_gpr_reg *valueRegId, enum cmdq_gpr_reg *destRegId, enum cmdq_event *regAccessToken) { *regAccessToken = CMDQ_SYNC_TOKEN_INVALID; if (hwflag & (1LL << CMDQ_ENG_MDP_TDSHP0)) { *valueRegId = CMDQ_DATA_REG_2D_SHARPNESS_0; *destRegId = CMDQ_DATA_REG_2D_SHARPNESS_0_DST; *regAccessToken = CMDQ_SYNC_TOKEN_GPR_SET_1; } else if (hwflag & (1LL << CMDQ_ENG_MDP_TDSHP1)) { *valueRegId = CMDQ_DATA_REG_2D_SHARPNESS_1; *destRegId = CMDQ_DATA_REG_2D_SHARPNESS_1_DST; *regAccessToken = CMDQ_SYNC_TOKEN_GPR_SET_2; } else { /* assume others are debug cases */ *valueRegId = CMDQ_DATA_REG_DEBUG; *destRegId = CMDQ_DATA_REG_DEBUG_DST; *regAccessToken = CMDQ_SYNC_TOKEN_GPR_SET_4; } } const char *cmdq_virtual_module_from_event_id(const s32 event, struct CmdqCBkStruct *groupCallback, u64 engineFlag) { const char *module = "CMDQ"; enum CMDQ_GROUP_ENUM group = CMDQ_MAX_GROUP_COUNT; switch (event) { case CMDQ_EVENT_MDP_RDMA0_SOF ... CMDQ_EVENT_MDP_WROT3_SOF: case CMDQ_EVENT_MDP_RDMA0_EOF ... CMDQ_EVENT_MDP_WROT3_WRITE_EOF: case CMDQ_EVENT_IMG_DL_RELAY_SOF ... CMDQ_EVENT_IMG_DL_RELAY3_SOF: module = "MDP"; group = CMDQ_GROUP_MDP; break; case CMDQ_EVENT_ISP_PASS2_2_EOF ... CMDQ_EVENT_ISP_PASS2_0_EOF: case CMDQ_EVENT_DIP_CQ_THREAD0_EOF ... CMDQ_EVENT_DIP_CQ_THREAD18_EOF: case CMDQ_EVENT_IMG1_EVENT_TX_FRAME_DONE_0 ... CMDQ_EVENT_IMG2_EVENT_TX_FRAME_DONE_23: module = "DIP"; group = CMDQ_GROUP_ISP; break; case CMDQ_EVENT_WPE_A_EOF: case CMDQ_EVENT_WPE_B_FRAME_DONE: module = "WPE"; group = CMDQ_GROUP_WPE; break; default: module = "CMDQ"; group = CMDQ_MAX_GROUP_COUNT; break; } if (group < CMDQ_MAX_GROUP_COUNT && groupCallback[group].dispatchMod) module = groupCallback[group].dispatchMod(engineFlag); return module; } const char *cmdq_virtual_parse_module_from_reg_addr(u32 reg_addr) { const u32 addr_base_and_page = (reg_addr & 0xFFFFF000); #ifdef CMDQ_USE_LEGACY return cmdq_virtual_parse_module_from_reg_addr_legacy(reg_addr); #else /* for well-known base, we check them with 12-bit mask * defined in mt_reg_base.h * TODO: comfirm with SS if IO_VIRT_TO_PHYS workable when enable * device tree? */ switch (addr_base_and_page) { case 0x14001000: /* MDP_RDMA0 */ case 0x14002000: /* MDP_RDMA1 */ case 0x14003000: /* MDP_RSZ0 */ case 0x14004000: /* MDP_RSZ1 */ case 0x14005000: /* MDP_RSZ2 */ case 0x14006000: /* MDP_WDMA */ case 0x14007000: /* MDP_WROT0 */ case 0x14008000: /* MDP_WROT1 */ case 0x14009000: /* MDP_TDSHP */ return "MDP"; case 0x14014000: /* DISP_COLOR0 */ case 0x14015000: /* DISP_COLOR1 */ return "COLOR"; case 0x14016000: /* DISP_COLOR0 */ case 0x14017000: /* DISP_COLOR1 */ return "CCORR"; case 0x1400B000: /* DISP_OVL0 */ case 0x1400D000: /* DISP_OVL0_2L */ return "OVL0"; case 0x1400C000: /* DISP_OVL1 */ case 0x1400E000: /* DISP_OVL1_2L */ return "OVL1"; case 0x14018000: /* DISP_AAL0 */ case 0x14019000: /* DISP_AAL1 */ case 0x1401a000: /* DISP_GAMMA0 */ case 0x1401b000: /* DISP_GAMMA1 */ return "AAL"; case 0x17020000: /* VENC */ return "VENC"; case 0x17030000: /* JPGENC */ return "JPGENC"; case 0x17040000: /* JPGDEC */ return "JPGDEC"; } /* for other register address we rely on GCE subsys to group them * with 16-bit mask. */ return cmdq_core_parse_subsys_from_reg_addr(reg_addr); #endif } s32 cmdq_virtual_can_module_entry_suspend(struct EngineStruct *engineList) { s32 status = 0; int i; enum CMDQ_ENG_ENUM e = 0; u32 mdpEngines[] = { CMDQ_ENG_ISP_IMGI, CMDQ_ENG_MDP_RDMA0, CMDQ_ENG_MDP_RDMA1, CMDQ_ENG_MDP_RSZ0, CMDQ_ENG_MDP_RSZ1, CMDQ_ENG_MDP_RSZ2, CMDQ_ENG_MDP_TDSHP0, CMDQ_ENG_MDP_TDSHP1, CMDQ_ENG_MDP_COLOR0, CMDQ_ENG_MDP_WROT0, CMDQ_ENG_MDP_WROT1, CMDQ_ENG_MDP_WDMA, }; for (i = 0; i < ARRAY_SIZE(mdpEngines); i++) { e = mdpEngines[i]; if (engineList[e].userCount != 0) { CMDQ_ERR( "suspend but engine %d has userCount %d, owner=%d\n", e, engineList[e].userCount, engineList[e].currOwner); status = -EBUSY; } } return status; } ssize_t cmdq_virtual_print_status_clock(char *buf) { s32 length = 0; char *pBuffer = buf; #ifdef CMDQ_PWR_AWARE /* MT_CG_DISP0_MUTEX_32K is removed in this platform */ pBuffer += sprintf(pBuffer, "MT_CG_INFRA_GCE: %d\n", cmdq_dev_gce_clock_is_enable()); pBuffer += sprintf(pBuffer, "\n"); #endif length = pBuffer - buf; return length; } void cmdq_virtual_print_status_seq_clock(struct seq_file *m) { #ifdef CMDQ_PWR_AWARE /* MT_CG_DISP0_MUTEX_32K is removed in this platform */ seq_printf(m, "MT_CG_INFRA_GCE: %d", cmdq_dev_gce_clock_is_enable()); seq_puts(m, "\n"); #endif } void cmdq_virtual_enable_common_clock_locked(bool enable) { #ifdef CMDQ_PWR_AWARE if (enable) { CMDQ_VERBOSE("[CLOCK] Enable SMI & LARB0 Clock\n"); /* Use SMI clock API */ #ifdef CONFIG_MTK_SMI_EXT smi_bus_prepare_enable(SMI_LARB0, "CMDQ"); #endif } else { CMDQ_VERBOSE("[CLOCK] Disable SMI & LARB0 Clock\n"); /* disable, reverse the sequence */ #ifdef CONFIG_MTK_SMI_EXT smi_bus_disable_unprepare(SMI_LARB0, "CMDQ"); #endif } #endif /* CMDQ_PWR_AWARE */ } void cmdq_virtual_enable_gce_clock_locked(bool enable) { #ifdef CMDQ_PWR_AWARE if (enable) { CMDQ_VERBOSE("[CLOCK] Enable CMDQ(GCE) Clock\n"); cmdq_dev_enable_gce_clock(enable); } else { CMDQ_VERBOSE("[CLOCK] Disable CMDQ(GCE) Clock\n"); cmdq_dev_enable_gce_clock(enable); } #endif } const char *cmdq_virtual_parse_handle_error_module_by_hwflag_impl( const struct cmdqRecStruct *pHandle) { const char *module = NULL; if (cmdq_get_func()->isDispScenario(pHandle->scenario)) module = "DISP"; else module = cmdq_mdp_parse_handle_error_module_by_hwflag(pHandle); /* other case, we need to analysis instruction for more detail */ return module; } const char *cmdq_virtual_parse_error_module_by_hwflag_impl( const struct cmdqRecStruct *task) { const char *module = NULL; /* TODO: fill in correct dispatch module */ if (cmdq_get_func()->isDispScenario(task->scenario)) module = "DISP"; else module = cmdq_mdp_parse_handle_error_module_by_hwflag(task); /* other case, we need to analysis instruction for more detail */ return module; } /** * Debug * */ int cmdq_virtual_dump_smi(const int showSmiDump) { int isSMIHang = 0; #if defined(CONFIG_MTK_SMI_EXT) && !defined(CONFIG_FPGA_EARLY_PORTING) && \ !defined(CONFIG_MTK_SMI_VARIANT) isSMIHang = smi_debug_bus_hang_detect(showSmiDump, "CMDQ"); CMDQ_ERR("SMI Hang? = %d\n", isSMIHang); #else CMDQ_LOG("[WARNING]not enable SMI dump now\n"); #endif return isSMIHang; } void cmdq_virtual_dump_gpr(void) { int i = 0; long offset = 0; u32 value = 0; CMDQ_LOG("========= GPR dump =========\n"); for (i = 0; i < 16; i++) { offset = CMDQ_GPR_R32(i); value = CMDQ_REG_GET32(offset); CMDQ_LOG("[GPR %2d]+0x%lx = 0x%08x\n", i, offset, value); } CMDQ_LOG("========= GPR dump =========\n"); } /** * Event backup * */ struct cmdq_backup_event_struct { enum cmdq_event EventID; u32 BackupValue; }; static struct cmdq_backup_event_struct g_cmdq_backup_event[] = { #ifdef CMDQ_EVENT_NEED_BACKUP {CMDQ_SYNC_TOKEN_VENC_EOF, 0,}, {CMDQ_SYNC_TOKEN_VENC_INPUT_READY, 0,}, #endif /* CMDQ_EVENT_NEED_BACKUP */ }; void cmdq_virtual_event_backup(void) { int i; int array_size = (sizeof(g_cmdq_backup_event) / sizeof(struct cmdq_backup_event_struct)); for (i = 0; i < array_size; i++) { if (g_cmdq_backup_event[i].EventID < 0 || g_cmdq_backup_event[i].EventID >= CMDQ_SYNC_TOKEN_MAX) continue; g_cmdq_backup_event[i].BackupValue = cmdqCoreGetEvent( g_cmdq_backup_event[i].EventID); CMDQ_MSG("[backup event] event: %s, value: %d\n", cmdq_core_get_event_name_enum( g_cmdq_backup_event[i].EventID), g_cmdq_backup_event[i].BackupValue); } } void cmdq_virtual_event_restore(void) { int i; int array_size = (sizeof(g_cmdq_backup_event) / sizeof(struct cmdq_backup_event_struct)); for (i = 0; i < array_size; i++) { if (g_cmdq_backup_event[i].EventID < 0 || g_cmdq_backup_event[i].EventID >= CMDQ_SYNC_TOKEN_MAX) continue; CMDQ_MSG("[restore event] event: %s, value: %d\n", cmdq_core_get_event_name_enum( g_cmdq_backup_event[i].EventID), g_cmdq_backup_event[i].BackupValue); if (g_cmdq_backup_event[i].BackupValue == 1) cmdqCoreSetEvent(g_cmdq_backup_event[i].EventID); else if (g_cmdq_backup_event[i].BackupValue == 0) cmdqCoreClearEvent(g_cmdq_backup_event[i].EventID); } } /** * Test * */ void cmdq_virtual_test_setup(void) { /* unconditionally set CMDQ_SYNC_TOKEN_CONFIG_ALLOW and mutex * STREAM_DONE so that DISPSYS scenarios may pass check. */ } void cmdq_virtual_test_cleanup(void) { /* do nothing */ } void cmdq_virtual_init_module_PA_stat(void) { } void cmdq_virtual_function_setting(void) { struct cmdqCoreFuncStruct *pFunc; pFunc = &(gFunctionPointer); /* * GCE capability */ pFunc->getSubsysLSBArgA = cmdq_virtual_get_subsys_LSB_in_arg_a; /* HW thread related */ pFunc->isSecureThread = cmdq_virtual_is_a_secure_thread; /** * Scenario related * */ pFunc->isDispScenario = cmdq_virtual_is_disp_scenario; pFunc->isDynamic = cmdq_virtual_is_dynamic_scenario; pFunc->dispThread = cmdq_virtual_disp_thread; pFunc->getThreadID = cmdq_virtual_get_thread_index; pFunc->priority = cmdq_virtual_priority_from_scenario; pFunc->is_disp_loop = cmdq_virtual_is_disp_loop; /** * Module dependent * */ pFunc->getRegID = cmdq_virtual_get_reg_id_from_hwflag; pFunc->moduleFromEvent = cmdq_virtual_module_from_event_id; pFunc->parseModule = cmdq_virtual_parse_module_from_reg_addr; pFunc->moduleEntrySuspend = cmdq_virtual_can_module_entry_suspend; pFunc->printStatusClock = cmdq_virtual_print_status_clock; pFunc->printStatusSeqClock = cmdq_virtual_print_status_seq_clock; pFunc->enableGCEClockLocked = cmdq_virtual_enable_gce_clock_locked; pFunc->parseErrorModule = cmdq_virtual_parse_error_module_by_hwflag_impl; pFunc->parseHandleErrorModule = cmdq_virtual_parse_handle_error_module_by_hwflag_impl; /** * Debug * */ pFunc->dumpSMI = cmdq_virtual_dump_smi; pFunc->dumpGPR = cmdq_virtual_dump_gpr; /** * Event backup * */ pFunc->eventBackup = cmdq_virtual_event_backup; pFunc->eventRestore = cmdq_virtual_event_restore; /** * Test * */ pFunc->testSetup = cmdq_virtual_test_setup; pFunc->testCleanup = cmdq_virtual_test_cleanup; pFunc->initModulePAStat = cmdq_virtual_init_module_PA_stat; } struct cmdqCoreFuncStruct *cmdq_get_func(void) { return &gFunctionPointer; }